mirror of
https://github.com/psycopg/psycopg2.git
synced 2025-02-17 01:20:32 +03:00
Merge from jh
This commit is contained in:
commit
ceee4c816f
50
ChangeLog
50
ChangeLog
|
@ -3,6 +3,56 @@
|
||||||
* psycopg/connection_type.c: merged get_backend_pid() method
|
* psycopg/connection_type.c: merged get_backend_pid() method
|
||||||
by Casey Duncan.
|
by Casey Duncan.
|
||||||
|
|
||||||
|
2008-07-23 James Henstridge <james@jamesh.id.au>
|
||||||
|
|
||||||
|
* psycopg/lobject_type.c (lobject_setup): use
|
||||||
|
FORMAT_CODE_PY_SSIZE_T in Dprintf() call for 64-bit compatibility
|
||||||
|
when using Python 2.5 or later.
|
||||||
|
(lobject_dealloc): same here.
|
||||||
|
|
||||||
|
2008-07-18 James Henstridge <james@jamesh.id.au>
|
||||||
|
|
||||||
|
* psycopg/adapter_qstring.c (qstring_traverse): add cyclic GC
|
||||||
|
traversal for quoted string adapters.
|
||||||
|
|
||||||
|
* psycopg/adapter_pboolean.c (pboolean_traverse): add cyclic GC
|
||||||
|
traversal for boolean adapters.
|
||||||
|
|
||||||
|
* psycopg/adapter_mxdatetime.c (mxdatetime_traverse): add cyclic
|
||||||
|
GC traversal for mxdatetime adapters.
|
||||||
|
|
||||||
|
* psycopg/adapter_datetime.c (pydatetime_traverse): add cyclic GC
|
||||||
|
traversal for datetime adapters.
|
||||||
|
|
||||||
|
2008-07-01 James Henstridge <james@jamesh.id.au>
|
||||||
|
|
||||||
|
* psycopg/adapter_binary.c (binary_traverse): add cyclic GC
|
||||||
|
traversal for binary adapters.
|
||||||
|
|
||||||
|
* psycopg/adapter_asis.c (asis_traverse): add cyclic GC traversal
|
||||||
|
for AsIs adapters.
|
||||||
|
|
||||||
|
* psycopg/adapter_list.c (list_traverse): add cyclic GC traversal
|
||||||
|
for list adapters.
|
||||||
|
|
||||||
|
2008-05-28 James Henstridge <james@jamesh.id.au>
|
||||||
|
|
||||||
|
* psycopg/cursor_type.c (cursor_setup): incref before setting
|
||||||
|
attributes, to make things GC-safe.
|
||||||
|
|
||||||
|
* psycopg/cursor_int.c (curs_reset): make clearing of description
|
||||||
|
and casts attributes GC-safe.
|
||||||
|
|
||||||
|
* psycopg/typecast.c (typecast_traverse): implement cyclic GC
|
||||||
|
traversal for typecasters.
|
||||||
|
|
||||||
|
* psycopg/connection_type.c:
|
||||||
|
* psycopg/cursor_type.c: add support for cyclic GC traversal (no
|
||||||
|
support for clearing).
|
||||||
|
|
||||||
|
* psycopg/python.h: add definitions for Py_CLEAR() and Py_VISIT()
|
||||||
|
for compatibility with old versions of Python.
|
||||||
|
|
||||||
2008-06-28 Federico Di Gregorio <fog@initd.org>
|
2008-06-28 Federico Di Gregorio <fog@initd.org>
|
||||||
|
|
||||||
* setup.py: fixed problem with spaces in pg_config path.
|
* setup.py: fixed problem with spaces in pg_config path.
|
||||||
|
|
|
@ -96,8 +96,8 @@ asis_setup(asisObject *self, PyObject *obj)
|
||||||
self, ((PyObject *)self)->ob_refcnt
|
self, ((PyObject *)self)->ob_refcnt
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Py_INCREF(obj);
|
||||||
self->wrapped = obj;
|
self->wrapped = obj;
|
||||||
Py_INCREF(self->wrapped);
|
|
||||||
|
|
||||||
Dprintf("asis_setup: good asis object at %p, refcnt = "
|
Dprintf("asis_setup: good asis object at %p, refcnt = "
|
||||||
FORMAT_CODE_PY_SSIZE_T,
|
FORMAT_CODE_PY_SSIZE_T,
|
||||||
|
@ -106,12 +106,19 @@ asis_setup(asisObject *self, PyObject *obj)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
asis_traverse(asisObject *self, visitproc visit, void *arg)
|
||||||
|
{
|
||||||
|
Py_VISIT(self->wrapped);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
asis_dealloc(PyObject* obj)
|
asis_dealloc(PyObject* obj)
|
||||||
{
|
{
|
||||||
asisObject *self = (asisObject *)obj;
|
asisObject *self = (asisObject *)obj;
|
||||||
|
|
||||||
Py_XDECREF(self->wrapped);
|
Py_CLEAR(self->wrapped);
|
||||||
|
|
||||||
Dprintf("asis_dealloc: deleted asis object at %p, refcnt = "
|
Dprintf("asis_dealloc: deleted asis object at %p, refcnt = "
|
||||||
FORMAT_CODE_PY_SSIZE_T,
|
FORMAT_CODE_PY_SSIZE_T,
|
||||||
|
@ -141,7 +148,7 @@ asis_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
static void
|
static void
|
||||||
asis_del(PyObject* self)
|
asis_del(PyObject* self)
|
||||||
{
|
{
|
||||||
PyObject_Del(self);
|
PyObject_GC_Del(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -183,10 +190,10 @@ PyTypeObject asisType = {
|
||||||
0, /*tp_setattro*/
|
0, /*tp_setattro*/
|
||||||
0, /*tp_as_buffer*/
|
0, /*tp_as_buffer*/
|
||||||
|
|
||||||
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
|
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
|
||||||
asisType_doc, /*tp_doc*/
|
asisType_doc, /*tp_doc*/
|
||||||
|
|
||||||
0, /*tp_traverse*/
|
(traverseproc)asis_traverse, /*tp_traverse*/
|
||||||
0, /*tp_clear*/
|
0, /*tp_clear*/
|
||||||
|
|
||||||
0, /*tp_richcompare*/
|
0, /*tp_richcompare*/
|
||||||
|
|
|
@ -257,8 +257,8 @@ binary_setup(binaryObject *self, PyObject *str)
|
||||||
|
|
||||||
self->buffer = NULL;
|
self->buffer = NULL;
|
||||||
self->conn = NULL;
|
self->conn = NULL;
|
||||||
|
Py_INCREF(str);
|
||||||
self->wrapped = str;
|
self->wrapped = str;
|
||||||
Py_INCREF(self->wrapped);
|
|
||||||
|
|
||||||
Dprintf("binary_setup: good binary object at %p, refcnt = "
|
Dprintf("binary_setup: good binary object at %p, refcnt = "
|
||||||
FORMAT_CODE_PY_SSIZE_T,
|
FORMAT_CODE_PY_SSIZE_T,
|
||||||
|
@ -266,14 +266,25 @@ binary_setup(binaryObject *self, PyObject *str)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
binary_traverse(PyObject *obj, visitproc visit, void *arg)
|
||||||
|
{
|
||||||
|
binaryObject *self = (binaryObject *)obj;
|
||||||
|
|
||||||
|
Py_VISIT(self->wrapped);
|
||||||
|
Py_VISIT(self->buffer);
|
||||||
|
Py_VISIT(self->conn);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
binary_dealloc(PyObject* obj)
|
binary_dealloc(PyObject* obj)
|
||||||
{
|
{
|
||||||
binaryObject *self = (binaryObject *)obj;
|
binaryObject *self = (binaryObject *)obj;
|
||||||
|
|
||||||
Py_XDECREF(self->wrapped);
|
Py_CLEAR(self->wrapped);
|
||||||
Py_XDECREF(self->buffer);
|
Py_CLEAR(self->buffer);
|
||||||
Py_XDECREF(self->conn);
|
Py_CLEAR(self->conn);
|
||||||
|
|
||||||
Dprintf("binary_dealloc: deleted binary object at %p, refcnt = "
|
Dprintf("binary_dealloc: deleted binary object at %p, refcnt = "
|
||||||
FORMAT_CODE_PY_SSIZE_T,
|
FORMAT_CODE_PY_SSIZE_T,
|
||||||
|
@ -303,7 +314,7 @@ binary_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
static void
|
static void
|
||||||
binary_del(PyObject* self)
|
binary_del(PyObject* self)
|
||||||
{
|
{
|
||||||
PyObject_Del(self);
|
PyObject_GC_Del(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -341,11 +352,11 @@ PyTypeObject binaryType = {
|
||||||
0, /*tp_setattro*/
|
0, /*tp_setattro*/
|
||||||
0, /*tp_as_buffer*/
|
0, /*tp_as_buffer*/
|
||||||
|
|
||||||
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
|
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
|
||||||
|
|
||||||
binaryType_doc, /*tp_doc*/
|
binaryType_doc, /*tp_doc*/
|
||||||
|
|
||||||
0, /*tp_traverse*/
|
binary_traverse, /*tp_traverse*/
|
||||||
0, /*tp_clear*/
|
0, /*tp_clear*/
|
||||||
|
|
||||||
0, /*tp_richcompare*/
|
0, /*tp_richcompare*/
|
||||||
|
|
|
@ -131,8 +131,8 @@ pydatetime_setup(pydatetimeObject *self, PyObject *obj, int type)
|
||||||
self, ((PyObject *)self)->ob_refcnt);
|
self, ((PyObject *)self)->ob_refcnt);
|
||||||
|
|
||||||
self->type = type;
|
self->type = type;
|
||||||
|
Py_INCREF(obj);
|
||||||
self->wrapped = obj;
|
self->wrapped = obj;
|
||||||
Py_INCREF(self->wrapped);
|
|
||||||
|
|
||||||
Dprintf("pydatetime_setup: good pydatetime object at %p, refcnt = "
|
Dprintf("pydatetime_setup: good pydatetime object at %p, refcnt = "
|
||||||
FORMAT_CODE_PY_SSIZE_T,
|
FORMAT_CODE_PY_SSIZE_T,
|
||||||
|
@ -140,12 +140,21 @@ pydatetime_setup(pydatetimeObject *self, PyObject *obj, int type)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
pydatetime_traverse(PyObject *obj, visitproc visit, void *arg)
|
||||||
|
{
|
||||||
|
pydatetimeObject *self = (pydatetimeObject *)obj;
|
||||||
|
|
||||||
|
Py_VISIT(self->wrapped);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
pydatetime_dealloc(PyObject* obj)
|
pydatetime_dealloc(PyObject* obj)
|
||||||
{
|
{
|
||||||
pydatetimeObject *self = (pydatetimeObject *)obj;
|
pydatetimeObject *self = (pydatetimeObject *)obj;
|
||||||
|
|
||||||
Py_XDECREF(self->wrapped);
|
Py_CLEAR(self->wrapped);
|
||||||
|
|
||||||
Dprintf("mpydatetime_dealloc: deleted pydatetime object at %p, "
|
Dprintf("mpydatetime_dealloc: deleted pydatetime object at %p, "
|
||||||
"refcnt = " FORMAT_CODE_PY_SSIZE_T, obj, obj->ob_refcnt);
|
"refcnt = " FORMAT_CODE_PY_SSIZE_T, obj, obj->ob_refcnt);
|
||||||
|
@ -174,7 +183,7 @@ pydatetime_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
static void
|
static void
|
||||||
pydatetime_del(PyObject* self)
|
pydatetime_del(PyObject* self)
|
||||||
{
|
{
|
||||||
PyObject_Del(self);
|
PyObject_GC_Del(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -213,11 +222,11 @@ PyTypeObject pydatetimeType = {
|
||||||
0, /*tp_setattro*/
|
0, /*tp_setattro*/
|
||||||
0, /*tp_as_buffer*/
|
0, /*tp_as_buffer*/
|
||||||
|
|
||||||
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
|
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
|
||||||
|
|
||||||
pydatetimeType_doc, /*tp_doc*/
|
pydatetimeType_doc, /*tp_doc*/
|
||||||
|
|
||||||
0, /*tp_traverse*/
|
pydatetime_traverse, /*tp_traverse*/
|
||||||
0, /*tp_clear*/
|
0, /*tp_clear*/
|
||||||
|
|
||||||
0, /*tp_richcompare*/
|
0, /*tp_richcompare*/
|
||||||
|
|
|
@ -108,9 +108,9 @@ list_prepare(listObject *self, PyObject *args)
|
||||||
reference to it; we'll need it during the recursive adapt() call (the
|
reference to it; we'll need it during the recursive adapt() call (the
|
||||||
encoding is here for a future expansion that will make .getquoted()
|
encoding is here for a future expansion that will make .getquoted()
|
||||||
work even without a connection to the backend. */
|
work even without a connection to the backend. */
|
||||||
Py_XDECREF(self->connection);
|
Py_CLEAR(self->connection);
|
||||||
|
Py_INCREF(conn);
|
||||||
self->connection = (PyObject*)conn;
|
self->connection = (PyObject*)conn;
|
||||||
Py_INCREF(self->connection);
|
|
||||||
|
|
||||||
Py_INCREF(Py_None);
|
Py_INCREF(Py_None);
|
||||||
return Py_None;
|
return Py_None;
|
||||||
|
@ -169,8 +169,8 @@ list_setup(listObject *self, PyObject *obj, const char *enc)
|
||||||
if (enc) self->encoding = strdup(enc);
|
if (enc) self->encoding = strdup(enc);
|
||||||
|
|
||||||
self->connection = NULL;
|
self->connection = NULL;
|
||||||
|
Py_INCREF(obj);
|
||||||
self->wrapped = obj;
|
self->wrapped = obj;
|
||||||
Py_INCREF(self->wrapped);
|
|
||||||
|
|
||||||
Dprintf("list_setup: good list object at %p, refcnt = "
|
Dprintf("list_setup: good list object at %p, refcnt = "
|
||||||
FORMAT_CODE_PY_SSIZE_T,
|
FORMAT_CODE_PY_SSIZE_T,
|
||||||
|
@ -179,13 +179,23 @@ list_setup(listObject *self, PyObject *obj, const char *enc)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
list_traverse(PyObject *obj, visitproc visit, void *arg)
|
||||||
|
{
|
||||||
|
listObject *self = (listObject *)obj;
|
||||||
|
|
||||||
|
Py_VISIT(self->wrapped);
|
||||||
|
Py_VISIT(self->connection);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
list_dealloc(PyObject* obj)
|
list_dealloc(PyObject* obj)
|
||||||
{
|
{
|
||||||
listObject *self = (listObject *)obj;
|
listObject *self = (listObject *)obj;
|
||||||
|
|
||||||
Py_XDECREF(self->wrapped);
|
Py_CLEAR(self->wrapped);
|
||||||
Py_XDECREF(self->connection);
|
Py_CLEAR(self->connection);
|
||||||
if (self->encoding) free(self->encoding);
|
if (self->encoding) free(self->encoding);
|
||||||
|
|
||||||
Dprintf("list_dealloc: deleted list object at %p, "
|
Dprintf("list_dealloc: deleted list object at %p, "
|
||||||
|
@ -215,7 +225,7 @@ list_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
static void
|
static void
|
||||||
list_del(PyObject* self)
|
list_del(PyObject* self)
|
||||||
{
|
{
|
||||||
PyObject_Del(self);
|
PyObject_GC_Del(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -253,11 +263,11 @@ PyTypeObject listType = {
|
||||||
0, /*tp_setattro*/
|
0, /*tp_setattro*/
|
||||||
0, /*tp_as_buffer*/
|
0, /*tp_as_buffer*/
|
||||||
|
|
||||||
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
|
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
|
||||||
|
|
||||||
listType_doc, /*tp_doc*/
|
listType_doc, /*tp_doc*/
|
||||||
|
|
||||||
0, /*tp_traverse*/
|
list_traverse, /*tp_traverse*/
|
||||||
0, /*tp_clear*/
|
0, /*tp_clear*/
|
||||||
|
|
||||||
0, /*tp_richcompare*/
|
0, /*tp_richcompare*/
|
||||||
|
|
|
@ -152,8 +152,8 @@ mxdatetime_setup(mxdatetimeObject *self, PyObject *obj, int type)
|
||||||
);
|
);
|
||||||
|
|
||||||
self->type = type;
|
self->type = type;
|
||||||
|
Py_INCREF(obj);
|
||||||
self->wrapped = obj;
|
self->wrapped = obj;
|
||||||
Py_INCREF(self->wrapped);
|
|
||||||
|
|
||||||
Dprintf("mxdatetime_setup: good mxdatetime object at %p, refcnt = "
|
Dprintf("mxdatetime_setup: good mxdatetime object at %p, refcnt = "
|
||||||
FORMAT_CODE_PY_SSIZE_T,
|
FORMAT_CODE_PY_SSIZE_T,
|
||||||
|
@ -162,12 +162,21 @@ mxdatetime_setup(mxdatetimeObject *self, PyObject *obj, int type)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
mxdatetime_traverse(PyObject *obj, visitproc visit, void *arg)
|
||||||
|
{
|
||||||
|
mxdatetimeObject *self = (mxdatetimeObject *)obj;
|
||||||
|
|
||||||
|
Py_VISIT(self->wrapped);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
mxdatetime_dealloc(PyObject* obj)
|
mxdatetime_dealloc(PyObject* obj)
|
||||||
{
|
{
|
||||||
mxdatetimeObject *self = (mxdatetimeObject *)obj;
|
mxdatetimeObject *self = (mxdatetimeObject *)obj;
|
||||||
|
|
||||||
Py_XDECREF(self->wrapped);
|
Py_CLEAR(self->wrapped);
|
||||||
|
|
||||||
Dprintf("mxdatetime_dealloc: deleted mxdatetime object at %p, refcnt = "
|
Dprintf("mxdatetime_dealloc: deleted mxdatetime object at %p, refcnt = "
|
||||||
FORMAT_CODE_PY_SSIZE_T,
|
FORMAT_CODE_PY_SSIZE_T,
|
||||||
|
@ -198,7 +207,7 @@ mxdatetime_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
static void
|
static void
|
||||||
mxdatetime_del(PyObject* self)
|
mxdatetime_del(PyObject* self)
|
||||||
{
|
{
|
||||||
PyObject_Del(self);
|
PyObject_GC_Del(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -237,11 +246,11 @@ PyTypeObject mxdatetimeType = {
|
||||||
0, /*tp_setattro*/
|
0, /*tp_setattro*/
|
||||||
0, /*tp_as_buffer*/
|
0, /*tp_as_buffer*/
|
||||||
|
|
||||||
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
|
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
|
||||||
|
|
||||||
mxdatetimeType_doc, /*tp_doc*/
|
mxdatetimeType_doc, /*tp_doc*/
|
||||||
|
|
||||||
0, /*tp_traverse*/
|
mxdatetime_traverse, /*tp_traverse*/
|
||||||
0, /*tp_clear*/
|
0, /*tp_clear*/
|
||||||
|
|
||||||
0, /*tp_richcompare*/
|
0, /*tp_richcompare*/
|
||||||
|
|
|
@ -106,8 +106,8 @@ pboolean_setup(pbooleanObject *self, PyObject *obj)
|
||||||
self, ((PyObject *)self)->ob_refcnt
|
self, ((PyObject *)self)->ob_refcnt
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Py_INCREF(obj);
|
||||||
self->wrapped = obj;
|
self->wrapped = obj;
|
||||||
Py_INCREF(self->wrapped);
|
|
||||||
|
|
||||||
Dprintf("pboolean_setup: good pboolean object at %p, refcnt = "
|
Dprintf("pboolean_setup: good pboolean object at %p, refcnt = "
|
||||||
FORMAT_CODE_PY_SSIZE_T,
|
FORMAT_CODE_PY_SSIZE_T,
|
||||||
|
@ -116,12 +116,21 @@ pboolean_setup(pbooleanObject *self, PyObject *obj)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
pboolean_traverse(PyObject *obj, visitproc visit, void *arg)
|
||||||
|
{
|
||||||
|
pbooleanObject *self = (pbooleanObject *)obj;
|
||||||
|
|
||||||
|
Py_VISIT(self->wrapped);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
pboolean_dealloc(PyObject* obj)
|
pboolean_dealloc(PyObject* obj)
|
||||||
{
|
{
|
||||||
pbooleanObject *self = (pbooleanObject *)obj;
|
pbooleanObject *self = (pbooleanObject *)obj;
|
||||||
|
|
||||||
Py_XDECREF(self->wrapped);
|
Py_CLEAR(self->wrapped);
|
||||||
|
|
||||||
Dprintf("pboolean_dealloc: deleted pboolean object at %p, refcnt = "
|
Dprintf("pboolean_dealloc: deleted pboolean object at %p, refcnt = "
|
||||||
FORMAT_CODE_PY_SSIZE_T,
|
FORMAT_CODE_PY_SSIZE_T,
|
||||||
|
@ -151,7 +160,7 @@ pboolean_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
static void
|
static void
|
||||||
pboolean_del(PyObject* self)
|
pboolean_del(PyObject* self)
|
||||||
{
|
{
|
||||||
PyObject_Del(self);
|
PyObject_GC_Del(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -194,10 +203,10 @@ PyTypeObject pbooleanType = {
|
||||||
0, /*tp_setattro*/
|
0, /*tp_setattro*/
|
||||||
0, /*tp_as_buffer*/
|
0, /*tp_as_buffer*/
|
||||||
|
|
||||||
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
|
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
|
||||||
pbooleanType_doc, /*tp_doc*/
|
pbooleanType_doc, /*tp_doc*/
|
||||||
|
|
||||||
0, /*tp_traverse*/
|
pboolean_traverse, /*tp_traverse*/
|
||||||
0, /*tp_clear*/
|
0, /*tp_clear*/
|
||||||
|
|
||||||
0, /*tp_richcompare*/
|
0, /*tp_richcompare*/
|
||||||
|
|
|
@ -213,10 +213,10 @@ qstring_prepare(qstringObject *self, PyObject *args)
|
||||||
Dprintf("qstring_prepare: set encoding to %s", conn->encoding);
|
Dprintf("qstring_prepare: set encoding to %s", conn->encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_XDECREF(self->conn);
|
Py_CLEAR(self->conn);
|
||||||
if (conn) {
|
if (conn) {
|
||||||
|
Py_INCREF(conn);
|
||||||
self->conn = (PyObject*)conn;
|
self->conn = (PyObject*)conn;
|
||||||
Py_INCREF(self->conn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_INCREF(Py_None);
|
Py_INCREF(Py_None);
|
||||||
|
@ -277,8 +277,8 @@ qstring_setup(qstringObject *self, PyObject *str, const char *enc)
|
||||||
/* FIXME: remove this orrible strdup */
|
/* FIXME: remove this orrible strdup */
|
||||||
if (enc) self->encoding = strdup(enc);
|
if (enc) self->encoding = strdup(enc);
|
||||||
|
|
||||||
|
Py_INCREF(str);
|
||||||
self->wrapped = str;
|
self->wrapped = str;
|
||||||
Py_INCREF(self->wrapped);
|
|
||||||
|
|
||||||
Dprintf("qstring_setup: good qstring object at %p, refcnt = "
|
Dprintf("qstring_setup: good qstring object at %p, refcnt = "
|
||||||
FORMAT_CODE_PY_SSIZE_T,
|
FORMAT_CODE_PY_SSIZE_T,
|
||||||
|
@ -287,14 +287,25 @@ qstring_setup(qstringObject *self, PyObject *str, const char *enc)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
qstring_traverse(PyObject *obj, visitproc visit, void *arg)
|
||||||
|
{
|
||||||
|
qstringObject *self = (qstringObject *)obj;
|
||||||
|
|
||||||
|
Py_VISIT(self->wrapped);
|
||||||
|
Py_VISIT(self->buffer);
|
||||||
|
Py_VISIT(self->conn);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
qstring_dealloc(PyObject* obj)
|
qstring_dealloc(PyObject* obj)
|
||||||
{
|
{
|
||||||
qstringObject *self = (qstringObject *)obj;
|
qstringObject *self = (qstringObject *)obj;
|
||||||
|
|
||||||
Py_XDECREF(self->wrapped);
|
Py_CLEAR(self->wrapped);
|
||||||
Py_XDECREF(self->buffer);
|
Py_CLEAR(self->buffer);
|
||||||
Py_XDECREF(self->conn);
|
Py_CLEAR(self->conn);
|
||||||
|
|
||||||
if (self->encoding) free(self->encoding);
|
if (self->encoding) free(self->encoding);
|
||||||
|
|
||||||
|
@ -327,7 +338,7 @@ qstring_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
static void
|
static void
|
||||||
qstring_del(PyObject* self)
|
qstring_del(PyObject* self)
|
||||||
{
|
{
|
||||||
PyObject_Del(self);
|
PyObject_GC_Del(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -366,11 +377,11 @@ PyTypeObject qstringType = {
|
||||||
0, /*tp_setattro*/
|
0, /*tp_setattro*/
|
||||||
0, /*tp_as_buffer*/
|
0, /*tp_as_buffer*/
|
||||||
|
|
||||||
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
|
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
|
||||||
|
|
||||||
qstringType_doc, /*tp_doc*/
|
qstringType_doc, /*tp_doc*/
|
||||||
|
|
||||||
0, /*tp_traverse*/
|
qstring_traverse, /*tp_traverse*/
|
||||||
0, /*tp_clear*/
|
0, /*tp_clear*/
|
||||||
|
|
||||||
0, /*tp_richcompare*/
|
0, /*tp_richcompare*/
|
||||||
|
|
|
@ -465,11 +465,11 @@ connection_dealloc(PyObject* obj)
|
||||||
if (self->encoding) free(self->encoding);
|
if (self->encoding) free(self->encoding);
|
||||||
if (self->critical) free(self->critical);
|
if (self->critical) free(self->critical);
|
||||||
|
|
||||||
Py_XDECREF(self->notice_list);
|
Py_CLEAR(self->notice_list);
|
||||||
Py_XDECREF(self->notifies);
|
Py_CLEAR(self->notifies);
|
||||||
Py_XDECREF(self->async_cursor);
|
Py_CLEAR(self->async_cursor);
|
||||||
Py_XDECREF(self->string_types);
|
Py_CLEAR(self->string_types);
|
||||||
Py_XDECREF(self->binary_types);
|
Py_CLEAR(self->binary_types);
|
||||||
|
|
||||||
pthread_mutex_destroy(&(self->lock));
|
pthread_mutex_destroy(&(self->lock));
|
||||||
|
|
||||||
|
@ -501,7 +501,7 @@ connection_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
static void
|
static void
|
||||||
connection_del(PyObject* self)
|
connection_del(PyObject* self)
|
||||||
{
|
{
|
||||||
PyObject_Del(self);
|
PyObject_GC_Del(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -512,6 +512,18 @@ connection_repr(connectionObject *self)
|
||||||
self, self->dsn, self->closed);
|
self, self->dsn, self->closed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
connection_traverse(connectionObject *self, visitproc visit, void *arg)
|
||||||
|
{
|
||||||
|
Py_VISIT(self->async_cursor);
|
||||||
|
Py_VISIT(self->notice_list);
|
||||||
|
Py_VISIT(self->notice_filter);
|
||||||
|
Py_VISIT(self->notifies);
|
||||||
|
Py_VISIT(self->string_types);
|
||||||
|
Py_VISIT(self->binary_types);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* object type */
|
/* object type */
|
||||||
|
|
||||||
|
@ -545,10 +557,10 @@ PyTypeObject connectionType = {
|
||||||
0, /*tp_setattro*/
|
0, /*tp_setattro*/
|
||||||
0, /*tp_as_buffer*/
|
0, /*tp_as_buffer*/
|
||||||
|
|
||||||
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
|
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
|
||||||
connectionType_doc, /*tp_doc*/
|
connectionType_doc, /*tp_doc*/
|
||||||
|
|
||||||
0, /*tp_traverse*/
|
(traverseproc)connection_traverse, /*tp_traverse*/
|
||||||
0, /*tp_clear*/
|
0, /*tp_clear*/
|
||||||
|
|
||||||
0, /*tp_richcompare*/
|
0, /*tp_richcompare*/
|
||||||
|
|
|
@ -34,15 +34,19 @@
|
||||||
void
|
void
|
||||||
curs_reset(cursorObject *self)
|
curs_reset(cursorObject *self)
|
||||||
{
|
{
|
||||||
|
PyObject *tmp;
|
||||||
|
|
||||||
/* initialize some variables to default values */
|
/* initialize some variables to default values */
|
||||||
self->notuples = 1;
|
self->notuples = 1;
|
||||||
self->rowcount = -1;
|
self->rowcount = -1;
|
||||||
self->row = 0;
|
self->row = 0;
|
||||||
|
|
||||||
Py_XDECREF(self->description);
|
tmp = self->description;
|
||||||
Py_INCREF(Py_None);
|
Py_INCREF(Py_None);
|
||||||
self->description = Py_None;
|
self->description = Py_None;
|
||||||
|
Py_XDECREF(tmp);
|
||||||
|
|
||||||
Py_XDECREF(self->casts);
|
tmp = self->casts;
|
||||||
self->casts = NULL;
|
self->casts = NULL;
|
||||||
|
Py_XDECREF(tmp);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1590,8 +1590,8 @@ cursor_setup(cursorObject *self, connectionObject *conn, const char *name)
|
||||||
"argument 1 must be subclass of psycopg2._psycopg.connection");
|
"argument 1 must be subclass of psycopg2._psycopg.connection");
|
||||||
return 1;
|
return 1;
|
||||||
} */
|
} */
|
||||||
|
Py_INCREF(conn);
|
||||||
self->conn = conn;
|
self->conn = conn;
|
||||||
Py_INCREF((PyObject*)self->conn);
|
|
||||||
|
|
||||||
self->closed = 0;
|
self->closed = 0;
|
||||||
self->mark = conn->mark;
|
self->mark = conn->mark;
|
||||||
|
@ -1607,6 +1607,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;
|
||||||
|
|
||||||
|
Py_INCREF(Py_None);
|
||||||
self->description = Py_None;
|
self->description = Py_None;
|
||||||
Py_INCREF(Py_None);
|
Py_INCREF(Py_None);
|
||||||
self->pgstatus = Py_None;
|
self->pgstatus = Py_None;
|
||||||
|
@ -1614,11 +1615,10 @@ cursor_setup(cursorObject *self, connectionObject *conn, const char *name)
|
||||||
self->tuple_factory = Py_None;
|
self->tuple_factory = Py_None;
|
||||||
Py_INCREF(Py_None);
|
Py_INCREF(Py_None);
|
||||||
self->query = Py_None;
|
self->query = Py_None;
|
||||||
Py_INCREF(Py_None);
|
|
||||||
|
|
||||||
/* default tzinfo factory */
|
/* default tzinfo factory */
|
||||||
|
Py_INCREF(pyPsycopgTzFixedOffsetTimezone);
|
||||||
self->tzinfo_factory = pyPsycopgTzFixedOffsetTimezone;
|
self->tzinfo_factory = pyPsycopgTzFixedOffsetTimezone;
|
||||||
Py_INCREF(self->tzinfo_factory);
|
|
||||||
|
|
||||||
Dprintf("cursor_setup: good cursor object at %p, refcnt = "
|
Dprintf("cursor_setup: good cursor object at %p, refcnt = "
|
||||||
FORMAT_CODE_PY_SSIZE_T,
|
FORMAT_CODE_PY_SSIZE_T,
|
||||||
|
@ -1634,15 +1634,15 @@ cursor_dealloc(PyObject* obj)
|
||||||
|
|
||||||
if (self->name) PyMem_Free(self->name);
|
if (self->name) PyMem_Free(self->name);
|
||||||
|
|
||||||
Py_XDECREF((PyObject*)self->conn);
|
Py_CLEAR(self->conn);
|
||||||
Py_XDECREF(self->casts);
|
Py_CLEAR(self->casts);
|
||||||
Py_XDECREF(self->description);
|
Py_CLEAR(self->description);
|
||||||
Py_XDECREF(self->pgstatus);
|
Py_CLEAR(self->pgstatus);
|
||||||
Py_XDECREF(self->tuple_factory);
|
Py_CLEAR(self->tuple_factory);
|
||||||
Py_XDECREF(self->tzinfo_factory);
|
Py_CLEAR(self->tzinfo_factory);
|
||||||
Py_XDECREF(self->query);
|
Py_CLEAR(self->query);
|
||||||
Py_XDECREF(self->string_types);
|
Py_CLEAR(self->string_types);
|
||||||
Py_XDECREF(self->binary_types);
|
Py_CLEAR(self->binary_types);
|
||||||
|
|
||||||
IFCLEARPGRES(self->pgres);
|
IFCLEARPGRES(self->pgres);
|
||||||
|
|
||||||
|
@ -1675,7 +1675,7 @@ cursor_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
static void
|
static void
|
||||||
cursor_del(PyObject* self)
|
cursor_del(PyObject* self)
|
||||||
{
|
{
|
||||||
PyObject_Del(self);
|
PyObject_GC_Del(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -1685,6 +1685,23 @@ cursor_repr(cursorObject *self)
|
||||||
"<cursor object at %p; closed: %d>", self, self->closed);
|
"<cursor object at %p; closed: %d>", self, self->closed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cursor_traverse(cursorObject *self, visitproc visit, void *arg)
|
||||||
|
{
|
||||||
|
Py_VISIT((PyObject *)self->conn);
|
||||||
|
Py_VISIT(self->description);
|
||||||
|
Py_VISIT(self->pgstatus);
|
||||||
|
Py_VISIT(self->casts);
|
||||||
|
Py_VISIT(self->caster);
|
||||||
|
Py_VISIT(self->copyfile);
|
||||||
|
Py_VISIT(self->tuple_factory);
|
||||||
|
Py_VISIT(self->tzinfo_factory);
|
||||||
|
Py_VISIT(self->query);
|
||||||
|
Py_VISIT(self->string_types);
|
||||||
|
Py_VISIT(self->binary_types);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* object type */
|
/* object type */
|
||||||
|
|
||||||
|
@ -1714,10 +1731,11 @@ PyTypeObject cursorType = {
|
||||||
0, /*tp_setattro*/
|
0, /*tp_setattro*/
|
||||||
0, /*tp_as_buffer*/
|
0, /*tp_as_buffer*/
|
||||||
|
|
||||||
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_ITER, /*tp_flags*/
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_ITER |
|
||||||
|
Py_TPFLAGS_HAVE_GC, /*tp_flags*/
|
||||||
cursorType_doc, /*tp_doc*/
|
cursorType_doc, /*tp_doc*/
|
||||||
|
|
||||||
0, /*tp_traverse*/
|
(traverseproc)cursor_traverse, /*tp_traverse*/
|
||||||
0, /*tp_clear*/
|
0, /*tp_clear*/
|
||||||
|
|
||||||
0, /*tp_richcompare*/
|
0, /*tp_richcompare*/
|
||||||
|
|
|
@ -282,8 +282,8 @@ lobject_setup(lobjectObject *self, connectionObject *conn,
|
||||||
if (lobject_open(self, conn, oid, mode, new_oid, new_file) == -1)
|
if (lobject_open(self, conn, oid, mode, new_oid, new_file) == -1)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
Dprintf("lobject_setup: good lobject object at %p, refcnt = %d",
|
Dprintf("lobject_setup: good lobject object at %p, refcnt = "
|
||||||
self, ((PyObject *)self)->ob_refcnt);
|
FORMAT_CODE_PY_SSIZE_T, self, ((PyObject *)self)->ob_refcnt);
|
||||||
Dprintf("lobject_setup: oid = %d, fd = %d", self->oid, self->fd);
|
Dprintf("lobject_setup: oid = %d, fd = %d", self->oid, self->fd);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -297,8 +297,8 @@ lobject_dealloc(PyObject* obj)
|
||||||
PyErr_Print();
|
PyErr_Print();
|
||||||
Py_XDECREF((PyObject*)self->conn);
|
Py_XDECREF((PyObject*)self->conn);
|
||||||
|
|
||||||
Dprintf("lobject_dealloc: deleted lobject object at %p, refcnt = %d",
|
Dprintf("lobject_dealloc: deleted lobject object at %p, refcnt = "
|
||||||
obj, obj->ob_refcnt);
|
FORMAT_CODE_PY_SSIZE_T, obj, obj->ob_refcnt);
|
||||||
|
|
||||||
obj->ob_type->tp_free(obj);
|
obj->ob_type->tp_free(obj);
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,4 +41,27 @@
|
||||||
#define freefunc destructor
|
#define freefunc destructor
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Py_VISIT and Py_CLEAR introduced in Python 2.4 */
|
||||||
|
#ifndef Py_VISIT
|
||||||
|
#define Py_VISIT(op) \
|
||||||
|
do { \
|
||||||
|
if (op) { \
|
||||||
|
int vret = visit((op), arg); \
|
||||||
|
if (vret) \
|
||||||
|
return vret; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef Py_CLEAR
|
||||||
|
#define Py_CLEAR(op) \
|
||||||
|
do { \
|
||||||
|
if (op) { \
|
||||||
|
PyObject *tmp = (PyObject *)(op); \
|
||||||
|
(op) = NULL; \
|
||||||
|
Py_DECREF(tmp); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* !defined(PSYCOPG_PYTHON_H) */
|
#endif /* !defined(PSYCOPG_PYTHON_H) */
|
||||||
|
|
|
@ -382,11 +382,28 @@ typecast_dealloc(PyObject *obj)
|
||||||
{
|
{
|
||||||
typecastObject *self = (typecastObject*)obj;
|
typecastObject *self = (typecastObject*)obj;
|
||||||
|
|
||||||
Py_XDECREF(self->values);
|
Py_CLEAR(self->values);
|
||||||
Py_XDECREF(self->name);
|
Py_CLEAR(self->name);
|
||||||
Py_XDECREF(self->pcast);
|
Py_CLEAR(self->pcast);
|
||||||
|
|
||||||
PyObject_Del(self);
|
obj->ob_type->tp_free(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
typecast_traverse(PyObject *obj, visitproc visit, void *arg)
|
||||||
|
{
|
||||||
|
typecastObject *self = (typecastObject*)obj;
|
||||||
|
|
||||||
|
Py_VISIT(self->values);
|
||||||
|
Py_VISIT(self->name);
|
||||||
|
Py_VISIT(self->pcast);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
typecast_del(void *self)
|
||||||
|
{
|
||||||
|
PyObject_GC_Del(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -427,10 +444,11 @@ PyTypeObject typecastType = {
|
||||||
0, /*tp_setattro*/
|
0, /*tp_setattro*/
|
||||||
0, /*tp_as_buffer*/
|
0, /*tp_as_buffer*/
|
||||||
|
|
||||||
Py_TPFLAGS_HAVE_RICHCOMPARE, /*tp_flags*/
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_RICHCOMPARE |
|
||||||
|
Py_TPFLAGS_HAVE_GC, /*tp_flags*/
|
||||||
"psycopg type-casting object", /*tp_doc*/
|
"psycopg type-casting object", /*tp_doc*/
|
||||||
|
|
||||||
0, /*tp_traverse*/
|
typecast_traverse, /*tp_traverse*/
|
||||||
0, /*tp_clear*/
|
0, /*tp_clear*/
|
||||||
|
|
||||||
typecast_richcompare, /*tp_richcompare*/
|
typecast_richcompare, /*tp_richcompare*/
|
||||||
|
@ -454,7 +472,7 @@ PyTypeObject typecastType = {
|
||||||
0, /*tp_init*/
|
0, /*tp_init*/
|
||||||
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/
|
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/
|
||||||
0, /*tp_new*/
|
0, /*tp_new*/
|
||||||
0, /*tp_free Low-level free-memory routine */
|
typecast_del, /*tp_free Low-level free-memory routine */
|
||||||
0, /*tp_is_gc For PyObject_IS_GC */
|
0, /*tp_is_gc For PyObject_IS_GC */
|
||||||
0, /*tp_bases*/
|
0, /*tp_bases*/
|
||||||
0, /*tp_mro method resolution order */
|
0, /*tp_mro method resolution order */
|
||||||
|
@ -468,7 +486,7 @@ typecast_new(PyObject *name, PyObject *values, PyObject *cast, PyObject *base)
|
||||||
{
|
{
|
||||||
typecastObject *obj;
|
typecastObject *obj;
|
||||||
|
|
||||||
obj = PyObject_NEW(typecastObject, &typecastType);
|
obj = PyObject_GC_New(typecastObject, &typecastType);
|
||||||
if (obj == NULL) return NULL;
|
if (obj == NULL) return NULL;
|
||||||
|
|
||||||
Dprintf("typecast_new: new type at = %p, refcnt = " FORMAT_CODE_PY_SSIZE_T,
|
Dprintf("typecast_new: new type at = %p, refcnt = " FORMAT_CODE_PY_SSIZE_T,
|
||||||
|
@ -498,6 +516,8 @@ typecast_new(PyObject *name, PyObject *values, PyObject *cast, PyObject *base)
|
||||||
obj->pcast = cast;
|
obj->pcast = cast;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyObject_GC_Track(obj);
|
||||||
|
|
||||||
Dprintf("typecast_new: typecast object created at %p", obj);
|
Dprintf("typecast_new: typecast object created at %p", obj);
|
||||||
|
|
||||||
return (PyObject *)obj;
|
return (PyObject *)obj;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user