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