mirror of
				https://github.com/psycopg/psycopg2.git
				synced 2025-10-30 23:37:29 +03:00 
			
		
		
		
	Merge branch 'withhold-transactions'
This commit is contained in:
		
						commit
						6a5f778361
					
				
							
								
								
									
										3
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								NEWS
									
									
									
									
									
								
							|  | @ -18,6 +18,9 @@ What's new in psycopg 2.5.4 | |||
|   proper methods (:ticket:`#219`). | ||||
| - Force conversion of pool arguments to integer to avoid potentially unbounded | ||||
|   pools (:ticket:`#220`). | ||||
| - Cursors :sql:`WITH HOLD` don't begin a new transaction upon move/fetch/close | ||||
|   (:ticket:`#228`). | ||||
| - Cursors :sql:`WITH HOLD` can be used in autocommit (:ticket:`#229`). | ||||
| - Don't ignore silently the `cursor.callproc` argument without a length. | ||||
| - Added a few errors missing from `~psycopg2.errorcodes`, defined by | ||||
|   PostgreSQL but not documented. | ||||
|  |  | |||
|  | @ -63,7 +63,7 @@ psyco_curs_close(cursorObject *self) | |||
| 
 | ||||
|         EXC_IF_NO_MARK(self); | ||||
|         PyOS_snprintf(buffer, 127, "CLOSE \"%s\"", self->name); | ||||
|         if (pq_execute(self, buffer, 0, 0) == -1) return NULL; | ||||
|         if (pq_execute(self, buffer, 0, 0, 1) == -1) return NULL; | ||||
|     } | ||||
| 
 | ||||
|     self->closed = 1; | ||||
|  | @ -444,7 +444,7 @@ _psyco_curs_execute(cursorObject *self, | |||
| 
 | ||||
|     /* At this point, the SQL statement must be str, not unicode */ | ||||
| 
 | ||||
|     tmp = pq_execute(self, Bytes_AS_STRING(self->query), async, no_result); | ||||
|     tmp = pq_execute(self, Bytes_AS_STRING(self->query), async, no_result, 0); | ||||
|     Dprintf("psyco_curs_execute: res = %d, pgres = %p", tmp, self->pgres); | ||||
|     if (tmp < 0) { goto exit; } | ||||
| 
 | ||||
|  | @ -478,7 +478,7 @@ psyco_curs_execute(cursorObject *self, PyObject *args, PyObject *kwargs) | |||
|                 "can't call .execute() on named cursors more than once"); | ||||
|             return NULL; | ||||
|         } | ||||
|         if (self->conn->autocommit) { | ||||
|         if (self->conn->autocommit && !self->withhold) { | ||||
|             psyco_set_error(ProgrammingError, self, | ||||
|                 "can't use a named cursor outside of transactions"); | ||||
|             return NULL; | ||||
|  | @ -766,7 +766,7 @@ psyco_curs_fetchone(cursorObject *self) | |||
|         EXC_IF_ASYNC_IN_PROGRESS(self, fetchone); | ||||
|         EXC_IF_TPC_PREPARED(self->conn, fetchone); | ||||
|         PyOS_snprintf(buffer, 127, "FETCH FORWARD 1 FROM \"%s\"", self->name); | ||||
|         if (pq_execute(self, buffer, 0, 0) == -1) return NULL; | ||||
|         if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) return NULL; | ||||
|         if (_psyco_curs_prefetch(self) < 0) return NULL; | ||||
|     } | ||||
| 
 | ||||
|  | @ -816,7 +816,7 @@ psyco_curs_next_named(cursorObject *self) | |||
| 
 | ||||
|         PyOS_snprintf(buffer, 127, "FETCH FORWARD %ld FROM \"%s\"", | ||||
|             self->itersize, self->name); | ||||
|         if (pq_execute(self, buffer, 0, 0) == -1) return NULL; | ||||
|         if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) return NULL; | ||||
|         if (_psyco_curs_prefetch(self) < 0) return NULL; | ||||
|     } | ||||
| 
 | ||||
|  | @ -885,7 +885,7 @@ psyco_curs_fetchmany(cursorObject *self, PyObject *args, PyObject *kwords) | |||
|         EXC_IF_TPC_PREPARED(self->conn, fetchone); | ||||
|         PyOS_snprintf(buffer, 127, "FETCH FORWARD %d FROM \"%s\"", | ||||
|             (int)size, self->name); | ||||
|         if (pq_execute(self, buffer, 0, 0) == -1) { goto exit; } | ||||
|         if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) { goto exit; } | ||||
|         if (_psyco_curs_prefetch(self) < 0) { goto exit; } | ||||
|     } | ||||
| 
 | ||||
|  | @ -960,7 +960,7 @@ psyco_curs_fetchall(cursorObject *self) | |||
|         EXC_IF_ASYNC_IN_PROGRESS(self, fetchall); | ||||
|         EXC_IF_TPC_PREPARED(self->conn, fetchall); | ||||
|         PyOS_snprintf(buffer, 127, "FETCH FORWARD ALL FROM \"%s\"", self->name); | ||||
|         if (pq_execute(self, buffer, 0, 0) == -1) { goto exit; } | ||||
|         if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) { goto exit; } | ||||
|         if (_psyco_curs_prefetch(self) < 0) { goto exit; } | ||||
|     } | ||||
| 
 | ||||
|  | @ -1178,7 +1178,7 @@ psyco_curs_scroll(cursorObject *self, PyObject *args, PyObject *kwargs) | |||
|         else { | ||||
|             PyOS_snprintf(buffer, 127, "MOVE %d FROM \"%s\"", value, self->name); | ||||
|         } | ||||
|         if (pq_execute(self, buffer, 0, 0) == -1) return NULL; | ||||
|         if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) return NULL; | ||||
|         if (_psyco_curs_prefetch(self) < 0) return NULL; | ||||
|     } | ||||
| 
 | ||||
|  | @ -1391,7 +1391,7 @@ psyco_curs_copy_from(cursorObject *self, PyObject *args, PyObject *kwargs) | |||
|     Py_INCREF(file); | ||||
|     self->copyfile = file; | ||||
| 
 | ||||
|     if (pq_execute(self, query, 0, 0) >= 0) { | ||||
|     if (pq_execute(self, query, 0, 0, 0) >= 0) { | ||||
|         res = Py_None; | ||||
|         Py_INCREF(Py_None); | ||||
|     } | ||||
|  | @ -1485,7 +1485,7 @@ psyco_curs_copy_to(cursorObject *self, PyObject *args, PyObject *kwargs) | |||
|     Py_INCREF(file); | ||||
|     self->copyfile = file; | ||||
| 
 | ||||
|     if (pq_execute(self, query, 0, 0) >= 0) { | ||||
|     if (pq_execute(self, query, 0, 0, 0) >= 0) { | ||||
|         res = Py_None; | ||||
|         Py_INCREF(Py_None); | ||||
|     } | ||||
|  | @ -1559,7 +1559,7 @@ psyco_curs_copy_expert(cursorObject *self, PyObject *args, PyObject *kwargs) | |||
|     self->copyfile = file; | ||||
| 
 | ||||
|     /* At this point, the SQL statement must be str, not unicode */ | ||||
|     if (pq_execute(self, Bytes_AS_STRING(sql), 0, 0) >= 0) { | ||||
|     if (pq_execute(self, Bytes_AS_STRING(sql), 0, 0, 0) >= 0) { | ||||
|         res = Py_None; | ||||
|         Py_INCREF(res); | ||||
|     } | ||||
|  |  | |||
|  | @ -893,7 +893,7 @@ pq_flush(connectionObject *conn) | |||
| */ | ||||
| 
 | ||||
| RAISES_NEG int | ||||
| pq_execute(cursorObject *curs, const char *query, int async, int no_result) | ||||
| pq_execute(cursorObject *curs, const char *query, int async, int no_result, int no_begin) | ||||
| { | ||||
|     PGresult *pgres = NULL; | ||||
|     char *error = NULL; | ||||
|  | @ -916,7 +916,7 @@ pq_execute(cursorObject *curs, const char *query, int async, int no_result) | |||
|     Py_BEGIN_ALLOW_THREADS; | ||||
|     pthread_mutex_lock(&(curs->conn->lock)); | ||||
| 
 | ||||
|     if (pq_begin_locked(curs->conn, &pgres, &error, &_save) < 0) { | ||||
|     if (!no_begin && pq_begin_locked(curs->conn, &pgres, &error, &_save) < 0) { | ||||
|         pthread_mutex_unlock(&(curs->conn->lock)); | ||||
|         Py_BLOCK_THREADS; | ||||
|         pq_complete_error(curs->conn, &pgres, &error); | ||||
|  |  | |||
|  | @ -36,7 +36,7 @@ | |||
| HIDDEN PGresult *pq_get_last_result(connectionObject *conn); | ||||
| RAISES_NEG HIDDEN int pq_fetch(cursorObject *curs, int no_result); | ||||
| RAISES_NEG HIDDEN int pq_execute(cursorObject *curs, const char *query, | ||||
|                                  int async, int no_result); | ||||
|                                  int async, int no_result, int no_begin); | ||||
| HIDDEN int pq_send_query(connectionObject *conn, const char *query); | ||||
| HIDDEN int pq_begin_locked(connectionObject *conn, PGresult **pgres, | ||||
|                            char **error, PyThreadState **tstate); | ||||
|  |  | |||
|  | @ -176,10 +176,7 @@ class CursorTests(ConnectingTestCase): | |||
|         curs.execute("select data from invname order by data") | ||||
|         self.assertEqual(curs.fetchall(), [(10,), (20,), (30,)]) | ||||
| 
 | ||||
|     def test_withhold(self): | ||||
|         self.assertRaises(psycopg2.ProgrammingError, self.conn.cursor, | ||||
|                           withhold=True) | ||||
| 
 | ||||
|     def _create_withhold_table(self): | ||||
|         curs = self.conn.cursor() | ||||
|         try: | ||||
|             curs.execute("drop table withhold") | ||||
|  | @ -190,6 +187,11 @@ class CursorTests(ConnectingTestCase): | |||
|             curs.execute("insert into withhold values (%s)", (i,)) | ||||
|         curs.close() | ||||
| 
 | ||||
|     def test_withhold(self): | ||||
|         self.assertRaises(psycopg2.ProgrammingError, self.conn.cursor, | ||||
|                           withhold=True) | ||||
| 
 | ||||
|         self._create_withhold_table() | ||||
|         curs = self.conn.cursor("W") | ||||
|         self.assertEqual(curs.withhold, False); | ||||
|         curs.withhold = True | ||||
|  | @ -209,6 +211,52 @@ class CursorTests(ConnectingTestCase): | |||
|         curs.execute("drop table withhold") | ||||
|         self.conn.commit() | ||||
| 
 | ||||
|     def test_withhold_no_begin(self): | ||||
|         self._create_withhold_table() | ||||
|         curs = self.conn.cursor("w", withhold=True) | ||||
|         curs.execute("select data from withhold order by data") | ||||
|         self.assertEqual(curs.fetchone(), (10,)) | ||||
|         self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_BEGIN) | ||||
|         self.assertEqual(self.conn.get_transaction_status(), | ||||
|                          psycopg2.extensions.TRANSACTION_STATUS_INTRANS) | ||||
| 
 | ||||
|         self.conn.commit() | ||||
|         self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY) | ||||
|         self.assertEqual(self.conn.get_transaction_status(), | ||||
|                          psycopg2.extensions.TRANSACTION_STATUS_IDLE) | ||||
| 
 | ||||
|         self.assertEqual(curs.fetchone(), (20,)) | ||||
|         self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY) | ||||
|         self.assertEqual(self.conn.get_transaction_status(), | ||||
|                          psycopg2.extensions.TRANSACTION_STATUS_IDLE) | ||||
| 
 | ||||
|         curs.close() | ||||
|         self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY) | ||||
|         self.assertEqual(self.conn.get_transaction_status(), | ||||
|                          psycopg2.extensions.TRANSACTION_STATUS_IDLE) | ||||
| 
 | ||||
|     def test_withhold_autocommit(self): | ||||
|         self._create_withhold_table() | ||||
|         self.conn.commit() | ||||
|         self.conn.autocommit = True | ||||
|         curs = self.conn.cursor("w", withhold=True) | ||||
|         curs.execute("select data from withhold order by data") | ||||
| 
 | ||||
|         self.assertEqual(curs.fetchone(), (10,)) | ||||
|         self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY) | ||||
|         self.assertEqual(self.conn.get_transaction_status(), | ||||
|                          psycopg2.extensions.TRANSACTION_STATUS_IDLE) | ||||
| 
 | ||||
|         self.conn.commit() | ||||
|         self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY) | ||||
|         self.assertEqual(self.conn.get_transaction_status(), | ||||
|                          psycopg2.extensions.TRANSACTION_STATUS_IDLE) | ||||
| 
 | ||||
|         curs.close() | ||||
|         self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY) | ||||
|         self.assertEqual(self.conn.get_transaction_status(), | ||||
|                          psycopg2.extensions.TRANSACTION_STATUS_IDLE) | ||||
| 
 | ||||
|     def test_scrollable(self): | ||||
|         self.assertRaises(psycopg2.ProgrammingError, self.conn.cursor, | ||||
|                           scrollable=True) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user