mirror of
				https://github.com/psycopg/psycopg2.git
				synced 2025-11-04 09:47:30 +03:00 
			
		
		
		
	Merge branch 'python2' into python3
Conflicts: NEWS-2.3 psycopg/connection_type.c tests/test_connection.py tests/types_basic.py
This commit is contained in:
		
						commit
						80bd6e2794
					
				
							
								
								
									
										2
									
								
								NEWS-2.3
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								NEWS-2.3
									
									
									
									
									
								
							| 
						 | 
					@ -5,6 +5,7 @@ What's new in psycopg 2.3.3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  - Added `register_composite()` function to cast PostgreSQL composite types
 | 
					  - Added `register_composite()` function to cast PostgreSQL composite types
 | 
				
			||||||
    into Python tuples/namedtuples.
 | 
					    into Python tuples/namedtuples.
 | 
				
			||||||
 | 
					  - Connections and cursors are weakly referenceable.
 | 
				
			||||||
  - The build script refuses to guess values if pg_config is not found.
 | 
					  - The build script refuses to guess values if pg_config is not found.
 | 
				
			||||||
  - Improved PostgreSQL-Python encodings mapping. Added a few
 | 
					  - Improved PostgreSQL-Python encodings mapping. Added a few
 | 
				
			||||||
    missing encodings: EUC_CN, EUC_JIS_2004, ISO885910, ISO885916,
 | 
					    missing encodings: EUC_CN, EUC_JIS_2004, ISO885910, ISO885916,
 | 
				
			||||||
| 
						 | 
					@ -16,6 +17,7 @@ What's new in psycopg 2.3.3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  - Fixed adaptation of None in composite types (ticket #26). Bug report by
 | 
					  - Fixed adaptation of None in composite types (ticket #26). Bug report by
 | 
				
			||||||
    Karsten Hilbert.
 | 
					    Karsten Hilbert.
 | 
				
			||||||
 | 
					  - Fixed several reference leaks in less common code paths.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
What's new in psycopg 2.3.2
 | 
					What's new in psycopg 2.3.2
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -769,7 +769,7 @@ class CompositeCaster(object):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.attnames = [ a[0] for a in attrs ]
 | 
					        self.attnames = [ a[0] for a in attrs ]
 | 
				
			||||||
        self.atttypes = [ a[1] for a in attrs ]
 | 
					        self.atttypes = [ a[1] for a in attrs ]
 | 
				
			||||||
        self.type = self._create_type(name, self.attnames)
 | 
					        self._create_type(name, self.attnames)
 | 
				
			||||||
        self.typecaster = _ext.new_type((oid,), name, self.parse)
 | 
					        self.typecaster = _ext.new_type((oid,), name, self.parse)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def parse(self, s, curs):
 | 
					    def parse(self, s, curs):
 | 
				
			||||||
| 
						 | 
					@ -784,7 +784,7 @@ class CompositeCaster(object):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        attrs = [ curs.cast(oid, token)
 | 
					        attrs = [ curs.cast(oid, token)
 | 
				
			||||||
            for oid, token in zip(self.atttypes, tokens) ]
 | 
					            for oid, token in zip(self.atttypes, tokens) ]
 | 
				
			||||||
        return self.type(*attrs)
 | 
					        return self._ctor(*attrs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _re_tokenize = regex.compile(r"""
 | 
					    _re_tokenize = regex.compile(r"""
 | 
				
			||||||
  \(? ([,\)])                       # an empty token, representing NULL
 | 
					  \(? ([,\)])                       # an empty token, representing NULL
 | 
				
			||||||
| 
						 | 
					@ -813,9 +813,11 @@ class CompositeCaster(object):
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            from collections import namedtuple
 | 
					            from collections import namedtuple
 | 
				
			||||||
        except ImportError:
 | 
					        except ImportError:
 | 
				
			||||||
            return tuple
 | 
					            self.type = tuple
 | 
				
			||||||
 | 
					            self._ctor = lambda *args: tuple(args)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            return namedtuple(name, attnames)
 | 
					            self.type = namedtuple(name, attnames)
 | 
				
			||||||
 | 
					            self._ctor = self.type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def _from_db(self, name, conn_or_curs):
 | 
					    def _from_db(self, name, conn_or_curs):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -362,20 +362,13 @@ psyco_Time(PyObject *self, PyObject *args)
 | 
				
			||||||
    return res;
 | 
					    return res;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PyObject *
 | 
					static PyObject *
 | 
				
			||||||
psyco_Timestamp(PyObject *self, PyObject *args)
 | 
					_psyco_Timestamp(int year, int month, int day,
 | 
				
			||||||
 | 
					                 int hour, int minute, double second, PyObject *tzinfo)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    double micro;
 | 
				
			||||||
 | 
					    PyObject *obj;
 | 
				
			||||||
    PyObject *res = NULL;
 | 
					    PyObject *res = NULL;
 | 
				
			||||||
    PyObject *tzinfo = NULL;
 | 
					 | 
				
			||||||
    int year, month, day;
 | 
					 | 
				
			||||||
    int hour=0, minute=0; /* default to midnight */
 | 
					 | 
				
			||||||
    double micro, second=0.0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    PyObject* obj = NULL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!PyArg_ParseTuple(args, "lii|iidO", &year, &month, &day,
 | 
					 | 
				
			||||||
                          &hour, &minute, &second, &tzinfo))
 | 
					 | 
				
			||||||
        return NULL;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    micro = (second - floor(second)) * 1000000.0;
 | 
					    micro = (second - floor(second)) * 1000000.0;
 | 
				
			||||||
    second = floor(second);
 | 
					    second = floor(second);
 | 
				
			||||||
| 
						 | 
					@ -400,6 +393,21 @@ psyco_Timestamp(PyObject *self, PyObject *args)
 | 
				
			||||||
    return res;
 | 
					    return res;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PyObject *
 | 
				
			||||||
 | 
					psyco_Timestamp(PyObject *self, PyObject *args)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    PyObject *tzinfo = NULL;
 | 
				
			||||||
 | 
					    int year, month, day;
 | 
				
			||||||
 | 
					    int hour=0, minute=0; /* default to midnight */
 | 
				
			||||||
 | 
					    double second=0.0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!PyArg_ParseTuple(args, "lii|iidO", &year, &month, &day,
 | 
				
			||||||
 | 
					                          &hour, &minute, &second, &tzinfo))
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return _psyco_Timestamp(year, month, day, hour, minute, second, tzinfo);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PyObject *
 | 
					PyObject *
 | 
				
			||||||
psyco_DateFromTicks(PyObject *self, PyObject *args)
 | 
					psyco_DateFromTicks(PyObject *self, PyObject *args)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -460,18 +468,10 @@ psyco_TimestampFromTicks(PyObject *self, PyObject *args)
 | 
				
			||||||
    t = (time_t)floor(ticks);
 | 
					    t = (time_t)floor(ticks);
 | 
				
			||||||
    ticks -= (double)t;
 | 
					    ticks -= (double)t;
 | 
				
			||||||
    if (localtime_r(&t, &tm)) {
 | 
					    if (localtime_r(&t, &tm)) {
 | 
				
			||||||
        PyObject *value = Py_BuildValue("iiiiidO",
 | 
					        res = _psyco_Timestamp(
 | 
				
			||||||
            tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
 | 
					            tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
 | 
				
			||||||
            tm.tm_hour, tm.tm_min,
 | 
					            tm.tm_hour, tm.tm_min, (double)tm.tm_sec + ticks,
 | 
				
			||||||
            (double)tm.tm_sec + ticks,
 | 
					 | 
				
			||||||
            pyPsycopgTzLOCAL);
 | 
					            pyPsycopgTzLOCAL);
 | 
				
			||||||
        if (value) {
 | 
					 | 
				
			||||||
            /* FIXME: not decref'ing the value here is a memory leak
 | 
					 | 
				
			||||||
	       but, on the other hand, if we decref we get a clean nice
 | 
					 | 
				
			||||||
	       segfault (on my 64 bit Python 2.4 box). So this leaks
 | 
					 | 
				
			||||||
	       will stay until after 2.0.7 when we'll try to plug it */
 | 
					 | 
				
			||||||
            res = psyco_Timestamp(self, value);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return res;
 | 
					    return res;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -99,7 +99,7 @@ typedef struct {
 | 
				
			||||||
    PGconn *pgconn;           /* the postgresql connection */
 | 
					    PGconn *pgconn;           /* the postgresql connection */
 | 
				
			||||||
    PGcancel *cancel;         /* the cancellation structure */
 | 
					    PGcancel *cancel;         /* the cancellation structure */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    PyObject *async_cursor;   /* a cursor executing an asynchronous query */
 | 
					    PyObject *async_cursor;   /* weakref to a cursor executing an asynchronous query */
 | 
				
			||||||
    int async_status;         /* asynchronous execution status */
 | 
					    int async_status;         /* asynchronous execution status */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* notice processing */
 | 
					    /* notice processing */
 | 
				
			||||||
| 
						 | 
					@ -115,6 +115,7 @@ typedef struct {
 | 
				
			||||||
    PyObject *binary_types;   /* a set of typecasters for binary types */
 | 
					    PyObject *binary_types;   /* a set of typecasters for binary types */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    int equote;               /* use E''-style quotes for escaped strings */
 | 
					    int equote;               /* use E''-style quotes for escaped strings */
 | 
				
			||||||
 | 
					    PyObject *weakreflist;    /* list of weak references */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} connectionObject;
 | 
					} connectionObject;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -823,7 +823,17 @@ conn_poll(connectionObject *self)
 | 
				
			||||||
        if (res == PSYCO_POLL_OK && self->async_cursor) {
 | 
					        if (res == PSYCO_POLL_OK && self->async_cursor) {
 | 
				
			||||||
            /* An async query has just finished: parse the tuple in the
 | 
					            /* An async query has just finished: parse the tuple in the
 | 
				
			||||||
             * target cursor. */
 | 
					             * target cursor. */
 | 
				
			||||||
            cursorObject *curs = (cursorObject *)self->async_cursor;
 | 
					            cursorObject *curs;
 | 
				
			||||||
 | 
					            PyObject *py_curs = PyWeakref_GetObject(self->async_cursor);
 | 
				
			||||||
 | 
					            if (Py_None == py_curs) {
 | 
				
			||||||
 | 
					                pq_clear_async(self);
 | 
				
			||||||
 | 
					                PyErr_SetString(InterfaceError,
 | 
				
			||||||
 | 
					                    "the asynchronous cursor has disappeared");
 | 
				
			||||||
 | 
					                res = PSYCO_POLL_ERROR;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            curs = (cursorObject *)py_curs;
 | 
				
			||||||
            IFCLEARPGRES(curs->pgres);
 | 
					            IFCLEARPGRES(curs->pgres);
 | 
				
			||||||
            curs->pgres = pq_get_last_result(self);
 | 
					            curs->pgres = pq_get_last_result(self);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -835,8 +845,7 @@ conn_poll(connectionObject *self)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            /* We have finished with our async_cursor */
 | 
					            /* We have finished with our async_cursor */
 | 
				
			||||||
            Py_XDECREF(self->async_cursor);
 | 
					            Py_CLEAR(self->async_cursor);
 | 
				
			||||||
            self->async_cursor = NULL;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -300,8 +300,6 @@ _psyco_conn_tpc_finish(connectionObject *self, PyObject *args,
 | 
				
			||||||
            goto exit;
 | 
					            goto exit;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        PyObject *tmp;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /* committing/aborting our own transaction. */
 | 
					        /* committing/aborting our own transaction. */
 | 
				
			||||||
        if (!self->tpc_xid) {
 | 
					        if (!self->tpc_xid) {
 | 
				
			||||||
            PyErr_SetString(ProgrammingError,
 | 
					            PyErr_SetString(ProgrammingError,
 | 
				
			||||||
| 
						 | 
					@ -327,11 +325,10 @@ _psyco_conn_tpc_finish(connectionObject *self, PyObject *args,
 | 
				
			||||||
            goto exit;
 | 
					            goto exit;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Py_CLEAR(self->tpc_xid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* connection goes ready */
 | 
					        /* connection goes ready */
 | 
				
			||||||
        self->status = CONN_STATUS_READY;
 | 
					        self->status = CONN_STATUS_READY;
 | 
				
			||||||
        tmp = (PyObject *)self->tpc_xid;
 | 
					 | 
				
			||||||
        self->tpc_xid = NULL;
 | 
					 | 
				
			||||||
        Py_DECREF(tmp);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Py_INCREF(Py_None);
 | 
					    Py_INCREF(Py_None);
 | 
				
			||||||
| 
						 | 
					@ -888,6 +885,10 @@ connection_dealloc(PyObject* obj)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    connectionObject *self = (connectionObject *)obj;
 | 
					    connectionObject *self = (connectionObject *)obj;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (self->weakreflist) {
 | 
				
			||||||
 | 
					        PyObject_ClearWeakRefs(obj);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    PyObject_GC_UnTrack(self);
 | 
					    PyObject_GC_UnTrack(self);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (self->closed == 0) conn_close(self);
 | 
					    if (self->closed == 0) conn_close(self);
 | 
				
			||||||
| 
						 | 
					@ -899,6 +900,7 @@ connection_dealloc(PyObject* obj)
 | 
				
			||||||
    PyMem_Free(self->codec);
 | 
					    PyMem_Free(self->codec);
 | 
				
			||||||
    if (self->critical) free(self->critical);
 | 
					    if (self->critical) free(self->critical);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Py_CLEAR(self->tpc_xid);
 | 
				
			||||||
    Py_CLEAR(self->async_cursor);
 | 
					    Py_CLEAR(self->async_cursor);
 | 
				
			||||||
    Py_CLEAR(self->notice_list);
 | 
					    Py_CLEAR(self->notice_list);
 | 
				
			||||||
    Py_CLEAR(self->notice_filter);
 | 
					    Py_CLEAR(self->notice_filter);
 | 
				
			||||||
| 
						 | 
					@ -952,6 +954,7 @@ connection_repr(connectionObject *self)
 | 
				
			||||||
static int
 | 
					static int
 | 
				
			||||||
connection_traverse(connectionObject *self, visitproc visit, void *arg)
 | 
					connection_traverse(connectionObject *self, visitproc visit, void *arg)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    Py_VISIT(self->tpc_xid);
 | 
				
			||||||
    Py_VISIT(self->async_cursor);
 | 
					    Py_VISIT(self->async_cursor);
 | 
				
			||||||
    Py_VISIT(self->notice_list);
 | 
					    Py_VISIT(self->notice_list);
 | 
				
			||||||
    Py_VISIT(self->notice_filter);
 | 
					    Py_VISIT(self->notice_filter);
 | 
				
			||||||
| 
						 | 
					@ -993,14 +996,16 @@ PyTypeObject connectionType = {
 | 
				
			||||||
    0,          /*tp_setattro*/
 | 
					    0,          /*tp_setattro*/
 | 
				
			||||||
    0,          /*tp_as_buffer*/
 | 
					    0,          /*tp_as_buffer*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
 | 
					    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
 | 
				
			||||||
 | 
					        Py_TPFLAGS_HAVE_WEAKREFS,
 | 
				
			||||||
 | 
					                /*tp_flags*/
 | 
				
			||||||
    connectionType_doc, /*tp_doc*/
 | 
					    connectionType_doc, /*tp_doc*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    (traverseproc)connection_traverse, /*tp_traverse*/
 | 
					    (traverseproc)connection_traverse, /*tp_traverse*/
 | 
				
			||||||
    0,          /*tp_clear*/
 | 
					    0,          /*tp_clear*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    0,          /*tp_richcompare*/
 | 
					    0,          /*tp_richcompare*/
 | 
				
			||||||
    0,          /*tp_weaklistoffset*/
 | 
					    offsetof(connectionObject, weakreflist), /* tp_weaklistoffset */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    0,          /*tp_iter*/
 | 
					    0,          /*tp_iter*/
 | 
				
			||||||
    0,          /*tp_iternext*/
 | 
					    0,          /*tp_iternext*/
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -76,6 +76,8 @@ typedef struct {
 | 
				
			||||||
    PyObject *string_types;   /* a set of typecasters for string types */
 | 
					    PyObject *string_types;   /* a set of typecasters for string types */
 | 
				
			||||||
    PyObject *binary_types;   /* a set of typecasters for binary types */
 | 
					    PyObject *binary_types;   /* a set of typecasters for binary types */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    PyObject *weakreflist;    /* list of weak references */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} cursorObject;
 | 
					} cursorObject;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* C-callable functions in cursor_int.c and cursor_ext.c */
 | 
					/* C-callable functions in cursor_int.c and cursor_ext.c */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -779,7 +779,8 @@ psyco_curs_fetchone(cursorObject *self, PyObject *args)
 | 
				
			||||||
    /* if the query was async aggresively free pgres, to allow
 | 
					    /* if the query was async aggresively free pgres, to allow
 | 
				
			||||||
       successive requests to reallocate it */
 | 
					       successive requests to reallocate it */
 | 
				
			||||||
    if (self->row >= self->rowcount
 | 
					    if (self->row >= self->rowcount
 | 
				
			||||||
        && self->conn->async_cursor == (PyObject*)self)
 | 
					        && self->conn->async_cursor
 | 
				
			||||||
 | 
					        && PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self)
 | 
				
			||||||
        IFCLEARPGRES(self->pgres);
 | 
					        IFCLEARPGRES(self->pgres);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return res;
 | 
					    return res;
 | 
				
			||||||
| 
						 | 
					@ -855,7 +856,8 @@ psyco_curs_fetchmany(cursorObject *self, PyObject *args, PyObject *kwords)
 | 
				
			||||||
    /* if the query was async aggresively free pgres, to allow
 | 
					    /* if the query was async aggresively free pgres, to allow
 | 
				
			||||||
       successive requests to reallocate it */
 | 
					       successive requests to reallocate it */
 | 
				
			||||||
    if (self->row >= self->rowcount
 | 
					    if (self->row >= self->rowcount
 | 
				
			||||||
        && self->conn->async_cursor == (PyObject*)self)
 | 
					        && self->conn->async_cursor
 | 
				
			||||||
 | 
					        && PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self)
 | 
				
			||||||
        IFCLEARPGRES(self->pgres);
 | 
					        IFCLEARPGRES(self->pgres);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return list;
 | 
					    return list;
 | 
				
			||||||
| 
						 | 
					@ -919,7 +921,8 @@ psyco_curs_fetchall(cursorObject *self, PyObject *args)
 | 
				
			||||||
    /* if the query was async aggresively free pgres, to allow
 | 
					    /* if the query was async aggresively free pgres, to allow
 | 
				
			||||||
       successive requests to reallocate it */
 | 
					       successive requests to reallocate it */
 | 
				
			||||||
    if (self->row >= self->rowcount
 | 
					    if (self->row >= self->rowcount
 | 
				
			||||||
        && self->conn->async_cursor == (PyObject*)self)
 | 
					        && self->conn->async_cursor
 | 
				
			||||||
 | 
					        && PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self)
 | 
				
			||||||
        IFCLEARPGRES(self->pgres);
 | 
					        IFCLEARPGRES(self->pgres);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return list;
 | 
					    return list;
 | 
				
			||||||
| 
						 | 
					@ -1626,6 +1629,7 @@ cursor_setup(cursorObject *self, connectionObject *conn, const char *name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    self->string_types = NULL;
 | 
					    self->string_types = NULL;
 | 
				
			||||||
    self->binary_types = NULL;
 | 
					    self->binary_types = NULL;
 | 
				
			||||||
 | 
					    self->weakreflist = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Py_INCREF(Py_None);
 | 
					    Py_INCREF(Py_None);
 | 
				
			||||||
    self->description = Py_None;
 | 
					    self->description = Py_None;
 | 
				
			||||||
| 
						 | 
					@ -1652,6 +1656,10 @@ cursor_dealloc(PyObject* obj)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    cursorObject *self = (cursorObject *)obj;
 | 
					    cursorObject *self = (cursorObject *)obj;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (self->weakreflist) {
 | 
				
			||||||
 | 
					        PyObject_ClearWeakRefs(obj);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    PyObject_GC_UnTrack(self);
 | 
					    PyObject_GC_UnTrack(self);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (self->name) PyMem_Free(self->name);
 | 
					    if (self->name) PyMem_Free(self->name);
 | 
				
			||||||
| 
						 | 
					@ -1752,14 +1760,15 @@ PyTypeObject cursorType = {
 | 
				
			||||||
    0,          /*tp_as_buffer*/
 | 
					    0,          /*tp_as_buffer*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_ITER |
 | 
					    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_ITER |
 | 
				
			||||||
      Py_TPFLAGS_HAVE_GC, /*tp_flags*/
 | 
					      Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_WEAKREFS ,
 | 
				
			||||||
 | 
					                /*tp_flags*/
 | 
				
			||||||
    cursorType_doc, /*tp_doc*/
 | 
					    cursorType_doc, /*tp_doc*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    (traverseproc)cursor_traverse, /*tp_traverse*/
 | 
					    (traverseproc)cursor_traverse, /*tp_traverse*/
 | 
				
			||||||
    0,          /*tp_clear*/
 | 
					    0,          /*tp_clear*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    0,          /*tp_richcompare*/
 | 
					    0,          /*tp_richcompare*/
 | 
				
			||||||
    0,          /*tp_weaklistoffset*/
 | 
					    offsetof(cursorObject, weakreflist), /*tp_weaklistoffset*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    cursor_iter, /*tp_iter*/
 | 
					    cursor_iter, /*tp_iter*/
 | 
				
			||||||
    cursor_next, /*tp_iternext*/
 | 
					    cursor_next, /*tp_iternext*/
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -275,8 +275,7 @@ pq_clear_async(connectionObject *conn)
 | 
				
			||||||
        Dprintf("pq_clear_async: clearing PGresult at %p", pgres);
 | 
					        Dprintf("pq_clear_async: clearing PGresult at %p", pgres);
 | 
				
			||||||
        CLEARPGRES(pgres);
 | 
					        CLEARPGRES(pgres);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    Py_XDECREF(conn->async_cursor);
 | 
					    Py_CLEAR(conn->async_cursor);
 | 
				
			||||||
    conn->async_cursor = NULL;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -820,8 +819,11 @@ pq_execute(cursorObject *curs, const char *query, int async)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else {
 | 
					    else {
 | 
				
			||||||
        curs->conn->async_status = async_status;
 | 
					        curs->conn->async_status = async_status;
 | 
				
			||||||
        Py_INCREF(curs);
 | 
					        curs->conn->async_cursor = PyWeakref_NewRef((PyObject *)curs, NULL);
 | 
				
			||||||
        curs->conn->async_cursor = (PyObject*)curs;
 | 
					        if (!curs->conn->async_cursor) {
 | 
				
			||||||
 | 
					            /* weakref creation failed */
 | 
				
			||||||
 | 
					            return -1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return 1-async;
 | 
					    return 1-async;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										111
									
								
								scripts/refcounter.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										111
									
								
								scripts/refcounter.py
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
					@ -0,0 +1,111 @@
 | 
				
			||||||
 | 
					#!/usr/bin/env python
 | 
				
			||||||
 | 
					"""Detect reference leaks after several unit test runs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The script runs the unit test and counts the objects alive after the run. If
 | 
				
			||||||
 | 
					the object count differs between the last two runs, a report is printed and the
 | 
				
			||||||
 | 
					script exits with error 1.
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Copyright (C) 2011 Daniele Varrazzo <daniele.varrazzo@gmail.com>
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This program is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					# it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					# the Free Software Foundation; either version 2 of the License, or
 | 
				
			||||||
 | 
					# (at your option) any later version.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					# GNU General Public License for more details.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					# along with this program; if not, write to the Free Software
 | 
				
			||||||
 | 
					# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import gc
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					import difflib
 | 
				
			||||||
 | 
					import unittest
 | 
				
			||||||
 | 
					from pprint import pprint
 | 
				
			||||||
 | 
					from collections import defaultdict
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def main():
 | 
				
			||||||
 | 
					    opt = parse_args()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    import psycopg2.tests
 | 
				
			||||||
 | 
					    test = psycopg2.tests
 | 
				
			||||||
 | 
					    if opt.suite:
 | 
				
			||||||
 | 
					        test = getattr(test, opt.suite)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sys.stdout.write("test suite %s\n" % test.__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for i in range(1, opt.nruns + 1):
 | 
				
			||||||
 | 
					        sys.stdout.write("test suite run %d of %d\n" % (i, opt.nruns))
 | 
				
			||||||
 | 
					        runner = unittest.TextTestRunner()
 | 
				
			||||||
 | 
					        runner.run(test.test_suite())
 | 
				
			||||||
 | 
					        dump(i, opt)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    f1 = open('debug-%02d.txt' % (opt.nruns - 1)).readlines()
 | 
				
			||||||
 | 
					    f2 = open('debug-%02d.txt' % (opt.nruns)).readlines()
 | 
				
			||||||
 | 
					    for line in difflib.unified_diff(f1, f2,
 | 
				
			||||||
 | 
					            "run %d" % (opt.nruns - 1), "run %d" % opt.nruns):
 | 
				
			||||||
 | 
					        sys.stdout.write(line)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    rv = f1 != f2 and 1 or 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if opt.objs:
 | 
				
			||||||
 | 
					        f1 = open('objs-%02d.txt' % (opt.nruns - 1)).readlines()
 | 
				
			||||||
 | 
					        f2 = open('objs-%02d.txt' % (opt.nruns)).readlines()
 | 
				
			||||||
 | 
					        for line in difflib.unified_diff(f1, f2,
 | 
				
			||||||
 | 
					                "run %d" % (opt.nruns - 1), "run %d" % opt.nruns):
 | 
				
			||||||
 | 
					            sys.stdout.write(line)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return rv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def parse_args():
 | 
				
			||||||
 | 
					    import optparse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    parser = optparse.OptionParser(description=__doc__)
 | 
				
			||||||
 | 
					    parser.add_option('--nruns', type='int', metavar="N", default=3,
 | 
				
			||||||
 | 
					        help="number of test suite runs [default: %default]")
 | 
				
			||||||
 | 
					    parser.add_option('--suite', metavar="NAME",
 | 
				
			||||||
 | 
					        help="the test suite to run (e.g. 'test_cursor'). [default: all]")
 | 
				
			||||||
 | 
					    parser.add_option('--objs', metavar="TYPE",
 | 
				
			||||||
 | 
					        help="in case of leaks, print a report of object TYPE "
 | 
				
			||||||
 | 
					            "(support still incomplete)")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    opt, args = parser.parse_args()
 | 
				
			||||||
 | 
					    return opt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def dump(i, opt):
 | 
				
			||||||
 | 
					    gc.collect()
 | 
				
			||||||
 | 
					    objs = gc.get_objects()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    c = defaultdict(int)
 | 
				
			||||||
 | 
					    for o in objs:
 | 
				
			||||||
 | 
					        c[type(o)] += 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pprint(
 | 
				
			||||||
 | 
					        sorted(((v,str(k)) for k,v in c.items()), reverse=True),
 | 
				
			||||||
 | 
					        stream=open("debug-%02d.txt" % i, "w"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if opt.objs:
 | 
				
			||||||
 | 
					        co = []
 | 
				
			||||||
 | 
					        t = getattr(__builtins__, opt.objs)
 | 
				
			||||||
 | 
					        for o in objs:
 | 
				
			||||||
 | 
					            if type(o) is t:
 | 
				
			||||||
 | 
					                co.append(o)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # TODO: very incomplete
 | 
				
			||||||
 | 
					        if t is dict:
 | 
				
			||||||
 | 
					            co.sort(key = lambda d: d.items())
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            co.sort()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        pprint(co, stream=open("objs-%02d.txt" % i, "w"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__':
 | 
				
			||||||
 | 
					    sys.exit(main())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -410,6 +410,18 @@ class AsyncTests(unittest.TestCase):
 | 
				
			||||||
        self.assertEqual("CREATE TABLE", cur.statusmessage)
 | 
					        self.assertEqual("CREATE TABLE", cur.statusmessage)
 | 
				
			||||||
        self.assert_(self.conn.notices)
 | 
					        self.assert_(self.conn.notices)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_async_cursor_gone(self):
 | 
				
			||||||
 | 
					        cur = self.conn.cursor()
 | 
				
			||||||
 | 
					        cur.execute("select 42;");
 | 
				
			||||||
 | 
					        del cur
 | 
				
			||||||
 | 
					        self.assertRaises(psycopg2.InterfaceError, self.wait, self.conn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # The connection is still usable
 | 
				
			||||||
 | 
					        cur = self.conn.cursor()
 | 
				
			||||||
 | 
					        cur.execute("select 42;");
 | 
				
			||||||
 | 
					        self.wait(self.conn)
 | 
				
			||||||
 | 
					        self.assertEqual(cur.fetchone(), (42,))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_suite():
 | 
					def test_suite():
 | 
				
			||||||
    return unittest.TestLoader().loadTestsFromName(__name__)
 | 
					    return unittest.TestLoader().loadTestsFromName(__name__)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -119,6 +119,14 @@ class ConnectionTests(unittest.TestCase):
 | 
				
			||||||
        cur.execute("select 'foo'::text;")
 | 
					        cur.execute("select 'foo'::text;")
 | 
				
			||||||
        self.assertEqual(cur.fetchone()[0], u'foo')
 | 
					        self.assertEqual(cur.fetchone()[0], u'foo')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_weakref(self):
 | 
				
			||||||
 | 
					        from weakref import ref
 | 
				
			||||||
 | 
					        conn = psycopg2.connect(self.conn.dsn)
 | 
				
			||||||
 | 
					        w = ref(conn)
 | 
				
			||||||
 | 
					        conn.close()
 | 
				
			||||||
 | 
					        del conn
 | 
				
			||||||
 | 
					        self.assert_(w() is None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class IsolationLevelsTestCase(unittest.TestCase):
 | 
					class IsolationLevelsTestCase(unittest.TestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -100,6 +100,13 @@ class CursorTests(unittest.TestCase):
 | 
				
			||||||
        curs2 = self.conn.cursor()
 | 
					        curs2 = self.conn.cursor()
 | 
				
			||||||
        self.assertEqual("foofoo", curs2.cast(705, 'foo'))
 | 
					        self.assertEqual("foofoo", curs2.cast(705, 'foo'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_weakref(self):
 | 
				
			||||||
 | 
					        from weakref import ref
 | 
				
			||||||
 | 
					        curs = self.conn.cursor()
 | 
				
			||||||
 | 
					        w = ref(curs)
 | 
				
			||||||
 | 
					        del curs
 | 
				
			||||||
 | 
					        self.assert_(w() is None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_suite():
 | 
					def test_suite():
 | 
				
			||||||
    return unittest.TestLoader().loadTestsFromName(__name__)
 | 
					    return unittest.TestLoader().loadTestsFromName(__name__)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -79,7 +79,7 @@ class QuotingTestCase(unittest.TestCase):
 | 
				
			||||||
            if not 0xD800 <= u <= 0xDFFF ]))    # surrogate area
 | 
					            if not 0xD800 <= u <= 0xDFFF ]))    # surrogate area
 | 
				
			||||||
        self.conn.set_client_encoding('UNICODE')
 | 
					        self.conn.set_client_encoding('UNICODE')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
 | 
					        psycopg2.extensions.register_type(psycopg2.extensions.UNICODE, self.conn)
 | 
				
			||||||
        curs.execute("SELECT %s::text;", (data,))
 | 
					        curs.execute("SELECT %s::text;", (data,))
 | 
				
			||||||
        res = curs.fetchone()[0]
 | 
					        res = curs.fetchone()[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -232,7 +232,11 @@ class AdaptSubclassTest(unittest.TestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        register_adapter(A, lambda a: AsIs("a"))
 | 
					        register_adapter(A, lambda a: AsIs("a"))
 | 
				
			||||||
        register_adapter(B, lambda b: AsIs("b"))
 | 
					        register_adapter(B, lambda b: AsIs("b"))
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
            self.assertEqual(b('b'), adapt(C()).getquoted())
 | 
					            self.assertEqual(b('b'), adapt(C()).getquoted())
 | 
				
			||||||
 | 
					        finally:
 | 
				
			||||||
 | 
					           del psycopg2.extensions.adapters[A, psycopg2.extensions.ISQLQuote]
 | 
				
			||||||
 | 
					           del psycopg2.extensions.adapters[B, psycopg2.extensions.ISQLQuote]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @testutils.skip_on_python3
 | 
					    @testutils.skip_on_python3
 | 
				
			||||||
    def test_no_mro_no_joy(self):
 | 
					    def test_no_mro_no_joy(self):
 | 
				
			||||||
| 
						 | 
					@ -242,7 +246,11 @@ class AdaptSubclassTest(unittest.TestCase):
 | 
				
			||||||
        class B(A): pass
 | 
					        class B(A): pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        register_adapter(A, lambda a: AsIs("a"))
 | 
					        register_adapter(A, lambda a: AsIs("a"))
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
            self.assertRaises(psycopg2.ProgrammingError, adapt, B())
 | 
					            self.assertRaises(psycopg2.ProgrammingError, adapt, B())
 | 
				
			||||||
 | 
					        finally:
 | 
				
			||||||
 | 
					           del psycopg2.extensions.adapters[A, psycopg2.extensions.ISQLQuote]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @testutils.skip_on_python2
 | 
					    @testutils.skip_on_python2
 | 
				
			||||||
    def test_adapt_subtype_3(self):
 | 
					    def test_adapt_subtype_3(self):
 | 
				
			||||||
| 
						 | 
					@ -252,7 +260,10 @@ class AdaptSubclassTest(unittest.TestCase):
 | 
				
			||||||
        class B(A): pass
 | 
					        class B(A): pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        register_adapter(A, lambda a: AsIs("a"))
 | 
					        register_adapter(A, lambda a: AsIs("a"))
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
            self.assertEqual(b("a"), adapt(B()).getquoted())
 | 
					            self.assertEqual(b("a"), adapt(B()).getquoted())
 | 
				
			||||||
 | 
					        finally:
 | 
				
			||||||
 | 
					           del psycopg2.extensions.adapters[A, psycopg2.extensions.ISQLQuote]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_suite():
 | 
					def test_suite():
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user