mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-13 04:26:33 +03:00
Added support for per-cursor and per-connection typecasters.
This commit is contained in:
parent
c2e16b8901
commit
6598a279e2
|
@ -1,3 +1,7 @@
|
||||||
|
2007-02-22 Federico Di Gregorio <fog@initd.org>
|
||||||
|
|
||||||
|
* Added support for per-connection and per-cursor typecasters.
|
||||||
|
|
||||||
2007-02-11 Federico Di Gregorio <fog@initd.org>
|
2007-02-11 Federico Di Gregorio <fog@initd.org>
|
||||||
|
|
||||||
* psycopg/pqpath.c: ported psycopg1 patch from #135.
|
* psycopg/pqpath.c: ported psycopg1 patch from #135.
|
||||||
|
|
67
examples/typecast.py
Normal file
67
examples/typecast.py
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
# typecast.py - example of per-cursor and per-connection typecasters.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2001-2007 Federico Di Gregorio <fog@debian.org>
|
||||||
|
#
|
||||||
|
# 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, 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 MERCHANTIBILITY
|
||||||
|
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
# for more details.
|
||||||
|
|
||||||
|
## put in DSN your DSN string
|
||||||
|
|
||||||
|
DSN = 'dbname=test'
|
||||||
|
|
||||||
|
## don't modify anything below this line (except for experimenting)
|
||||||
|
|
||||||
|
class SimpleQuoter(object):
|
||||||
|
def sqlquote(x=None):
|
||||||
|
return "'bar'"
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import psycopg2
|
||||||
|
import psycopg2.extensions
|
||||||
|
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
DSN = sys.argv[1]
|
||||||
|
|
||||||
|
print "Opening connection using dns:", DSN
|
||||||
|
conn = psycopg2.connect(DSN)
|
||||||
|
print "Encoding for this connection is", conn.encoding
|
||||||
|
|
||||||
|
curs = conn.cursor()
|
||||||
|
curs.execute("SELECT 'text'::text AS foo")
|
||||||
|
textoid = curs.description[0][1]
|
||||||
|
print "Oid for the text datatype is", textoid
|
||||||
|
|
||||||
|
def castA(s, curs):
|
||||||
|
if s is not None: return "(A) " + s
|
||||||
|
TYPEA = psycopg2.extensions.new_type((textoid,), "TYPEA", castA)
|
||||||
|
|
||||||
|
def castB(s, curs):
|
||||||
|
if s is not None: return "(B) " + s
|
||||||
|
TYPEB = psycopg2.extensions.new_type((textoid,), "TYPEB", castB)
|
||||||
|
|
||||||
|
curs = conn.cursor()
|
||||||
|
curs.execute("SELECT 'some text.'::text AS foo")
|
||||||
|
print "Some text from plain connection:", curs.fetchone()[0]
|
||||||
|
|
||||||
|
psycopg2.extensions.register_type(TYPEA, conn)
|
||||||
|
curs = conn.cursor()
|
||||||
|
curs.execute("SELECT 'some text.'::text AS foo")
|
||||||
|
print "Some text from connection with typecaster:", curs.fetchone()[0]
|
||||||
|
|
||||||
|
curs = conn.cursor()
|
||||||
|
psycopg2.extensions.register_type(TYPEB, curs)
|
||||||
|
curs.execute("SELECT 'some text.'::text AS foo")
|
||||||
|
print "Some text from cursor with typecaster:", curs.fetchone()[0]
|
||||||
|
|
||||||
|
curs = conn.cursor()
|
||||||
|
curs.execute("SELECT 'some text.'::text AS foo")
|
||||||
|
print "Some text from connection with typecaster again:", curs.fetchone()[0]
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,10 @@ typedef struct {
|
||||||
PyObject *exc_DataError;
|
PyObject *exc_DataError;
|
||||||
PyObject *exc_NotSupportedError;
|
PyObject *exc_NotSupportedError;
|
||||||
|
|
||||||
|
/* per-connection typecasters */
|
||||||
|
PyObject *string_types; /* a set of typecasters for string types */
|
||||||
|
PyObject *binary_types; /* a set of typecasters for binary types */
|
||||||
|
|
||||||
} connectionObject;
|
} connectionObject;
|
||||||
|
|
||||||
/* C-callable functions in connection_int.c and connection_ext.c */
|
/* C-callable functions in connection_int.c and connection_ext.c */
|
||||||
|
|
|
@ -276,7 +276,11 @@ static struct PyMemberDef connectionObject_members[] = {
|
||||||
"The current connection string."},
|
"The current connection string."},
|
||||||
{"status", T_LONG,
|
{"status", T_LONG,
|
||||||
offsetof(connectionObject, status), RO,
|
offsetof(connectionObject, status), RO,
|
||||||
"The current transaction status."},
|
"The current transaction status."},
|
||||||
|
{"string_types", T_OBJECT, offsetof(connectionObject, string_types), RO,
|
||||||
|
"A set of typecasters to convert textual values."},
|
||||||
|
{"binary_types", T_OBJECT, offsetof(connectionObject, binary_types), RO,
|
||||||
|
"A set of typecasters to convert binary values."},
|
||||||
#endif
|
#endif
|
||||||
{NULL}
|
{NULL}
|
||||||
};
|
};
|
||||||
|
@ -301,7 +305,8 @@ connection_setup(connectionObject *self, char *dsn)
|
||||||
self->async_cursor = NULL;
|
self->async_cursor = NULL;
|
||||||
self->pgconn = NULL;
|
self->pgconn = NULL;
|
||||||
self->mark = 0;
|
self->mark = 0;
|
||||||
|
self->string_types = PyDict_New();
|
||||||
|
self->binary_types = PyDict_New();
|
||||||
|
|
||||||
pthread_mutex_init(&(self->lock), NULL);
|
pthread_mutex_init(&(self->lock), NULL);
|
||||||
|
|
||||||
|
@ -339,6 +344,8 @@ connection_dealloc(PyObject* obj)
|
||||||
Py_XDECREF(self->notice_list);
|
Py_XDECREF(self->notice_list);
|
||||||
Py_XDECREF(self->notifies);
|
Py_XDECREF(self->notifies);
|
||||||
Py_XDECREF(self->async_cursor);
|
Py_XDECREF(self->async_cursor);
|
||||||
|
Py_XDECREF(self->string_types);
|
||||||
|
Py_XDECREF(self->binary_types);
|
||||||
|
|
||||||
pthread_mutex_destroy(&(self->lock));
|
pthread_mutex_destroy(&(self->lock));
|
||||||
|
|
||||||
|
|
|
@ -1448,6 +1448,8 @@ cursor_dealloc(PyObject* obj)
|
||||||
Py_XDECREF(self->tuple_factory);
|
Py_XDECREF(self->tuple_factory);
|
||||||
Py_XDECREF(self->tzinfo_factory);
|
Py_XDECREF(self->tzinfo_factory);
|
||||||
Py_XDECREF(self->query);
|
Py_XDECREF(self->query);
|
||||||
|
Py_XDECREF(self->string_types);
|
||||||
|
Py_XDECREF(self->binary_types);
|
||||||
|
|
||||||
IFCLEARPGRES(self->pgres);
|
IFCLEARPGRES(self->pgres);
|
||||||
|
|
||||||
|
|
|
@ -501,34 +501,44 @@ _pq_fetch_tuples(cursorObject *curs)
|
||||||
|
|
||||||
PyTuple_SET_ITEM(curs->description, i, dtitem);
|
PyTuple_SET_ITEM(curs->description, i, dtitem);
|
||||||
|
|
||||||
/* fill the right cast function by accessing the global dictionary of
|
/* fill the right cast function by accessing three different dictionaries:
|
||||||
casting objects. if we got no defined cast use the default
|
- the per-cursor dictionary, if available (can be NULL or None)
|
||||||
one */
|
- the per-connection dictionary (always exists but can be null)
|
||||||
if (!(cast = PyDict_GetItem(curs->casts, type))) {
|
- the global dictionary (at module level)
|
||||||
Dprintf("_pq_fetch_tuples: cast %d not in per-cursor dict", ftype);
|
if we get no defined cast use the default one */
|
||||||
if (!(cast = PyDict_GetItem(psyco_types, type))) {
|
|
||||||
Dprintf("_pq_fetch_tuples: cast %d not found, using default",
|
Dprintf("_pq_fetch_tuples: looking for cast %d:", ftype);
|
||||||
PQftype(curs->pgres,i));
|
if (curs->string_types != NULL && curs->string_types != Py_None) {
|
||||||
cast = psyco_default_cast;
|
cast = PyDict_GetItem(curs->string_types, type);
|
||||||
}
|
Dprintf("_pq_fetch_tuples: per-cursor dict: %p", cast);
|
||||||
}
|
}
|
||||||
|
if (cast == NULL) {
|
||||||
|
cast = PyDict_GetItem(curs->conn->string_types, type);
|
||||||
|
Dprintf("_pq_fetch_tuples: per-connection dict: %p", cast);
|
||||||
|
}
|
||||||
|
if (cast == NULL) {
|
||||||
|
cast = PyDict_GetItem(psyco_types, type);
|
||||||
|
Dprintf("_pq_fetch_tuples: global dict: %p", cast);
|
||||||
|
}
|
||||||
|
if (cast == NULL) cast = psyco_default_cast;
|
||||||
|
|
||||||
/* else if we got binary tuples and if we got a field that
|
/* else if we got binary tuples and if we got a field that
|
||||||
is binary use the default cast
|
is binary use the default cast
|
||||||
FIXME: what the hell am I trying to do here? This just can't work..
|
FIXME: what the hell am I trying to do here? This just can't work..
|
||||||
*/
|
*/
|
||||||
else if (pgbintuples && cast == psyco_default_binary_cast) {
|
if (pgbintuples && cast == psyco_default_binary_cast) {
|
||||||
Dprintf("_pq_fetch_tuples: Binary cursor and "
|
Dprintf("_pq_fetch_tuples: Binary cursor and "
|
||||||
"binary field: %i using default cast",
|
"binary field: %i using default cast",
|
||||||
PQftype(curs->pgres,i));
|
PQftype(curs->pgres,i));
|
||||||
cast = psyco_default_cast;
|
cast = psyco_default_cast;
|
||||||
}
|
}
|
||||||
|
|
||||||
Dprintf("_pq_fetch_tuples: using cast at %p (%s) for type %d",
|
Dprintf("_pq_fetch_tuples: using cast at %p (%s) for type %d",
|
||||||
cast, PyString_AS_STRING(((typecastObject*)cast)->name),
|
cast, PyString_AS_STRING(((typecastObject*)cast)->name),
|
||||||
PQftype(curs->pgres,i));
|
PQftype(curs->pgres,i));
|
||||||
Py_INCREF(cast);
|
Py_INCREF(cast);
|
||||||
PyTuple_SET_ITEM(curs->casts, i, cast);
|
PyTuple_SET_ITEM(curs->casts, i, cast);
|
||||||
|
|
||||||
|
|
||||||
/* 1/ fill the other fields */
|
/* 1/ fill the other fields */
|
||||||
PyTuple_SET_ITEM(dtitem, 0,
|
PyTuple_SET_ITEM(dtitem, 0,
|
||||||
PyString_FromString(PQfname(curs->pgres, i)));
|
PyString_FromString(PQfname(curs->pgres, i)));
|
||||||
|
|
|
@ -228,16 +228,39 @@ psyco_connect(PyObject *self, PyObject *args, PyObject *keywds)
|
||||||
" the string representation returned by PostgreSQL (`None` if ``NULL``)\n" \
|
" the string representation returned by PostgreSQL (`None` if ``NULL``)\n" \
|
||||||
" and ``cur`` is the cursor from which data are read."
|
" and ``cur`` is the cursor from which data are read."
|
||||||
|
|
||||||
|
static void
|
||||||
|
_psyco_register_type_set(PyObject **dict, PyObject *type)
|
||||||
|
{
|
||||||
|
if (*dict == NULL)
|
||||||
|
*dict = PyDict_New();
|
||||||
|
typecast_add(type, *dict, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
psyco_register_type(PyObject *self, PyObject *args)
|
psyco_register_type(PyObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
PyObject *type;
|
PyObject *type, *obj;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "O!", &typecastType, &type)) {
|
if (!PyArg_ParseTuple(args, "O!|O", &typecastType, &type, &obj)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
typecast_add(type, 0);
|
if (obj != NULL) {
|
||||||
|
if (obj->ob_type == &cursorType) {
|
||||||
|
_psyco_register_type_set(&(((cursorObject*)obj)->string_types), type);
|
||||||
|
}
|
||||||
|
else if (obj->ob_type == &connectionType) {
|
||||||
|
typecast_add(type, ((connectionObject*)obj)->string_types, 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PyErr_SetString(PyExc_TypeError,
|
||||||
|
"argument 2 must be a connection, cursor or None");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
typecast_add(type, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
Py_INCREF(Py_None);
|
Py_INCREF(Py_None);
|
||||||
return Py_None;
|
return Py_None;
|
||||||
|
|
|
@ -226,7 +226,7 @@ typecast_init(PyObject *dict)
|
||||||
|
|
||||||
t = (typecastObject *)typecast_from_c(&(typecast_builtins[i]), dict);
|
t = (typecastObject *)typecast_from_c(&(typecast_builtins[i]), dict);
|
||||||
if (t == NULL) return -1;
|
if (t == NULL) return -1;
|
||||||
if (typecast_add((PyObject *)t, 0) != 0) return -1;
|
if (typecast_add((PyObject *)t, NULL, 0) != 0) return -1;
|
||||||
|
|
||||||
PyDict_SetItem(dict, t->name, (PyObject *)t);
|
PyDict_SetItem(dict, t->name, (PyObject *)t);
|
||||||
|
|
||||||
|
@ -264,7 +264,7 @@ typecast_init(PyObject *dict)
|
||||||
|
|
||||||
/* typecast_add - add a type object to the dictionary */
|
/* typecast_add - add a type object to the dictionary */
|
||||||
int
|
int
|
||||||
typecast_add(PyObject *obj, int binary)
|
typecast_add(PyObject *obj, PyObject *dict, int binary)
|
||||||
{
|
{
|
||||||
PyObject *val;
|
PyObject *val;
|
||||||
Py_ssize_t len, i;
|
Py_ssize_t len, i;
|
||||||
|
@ -274,16 +274,14 @@ typecast_add(PyObject *obj, int binary)
|
||||||
Dprintf("typecast_add: object at %p, values refcnt = %d",
|
Dprintf("typecast_add: object at %p, values refcnt = %d",
|
||||||
obj, type->values->ob_refcnt);
|
obj, type->values->ob_refcnt);
|
||||||
|
|
||||||
|
if (dict == NULL)
|
||||||
|
dict = (binary ? psyco_binary_types : psyco_types);
|
||||||
|
|
||||||
len = PyTuple_Size(type->values);
|
len = PyTuple_Size(type->values);
|
||||||
for (i = 0; i < len; i++) {
|
for (i = 0; i < len; i++) {
|
||||||
val = PyTuple_GetItem(type->values, i);
|
val = PyTuple_GetItem(type->values, i);
|
||||||
Dprintf("typecast_add: adding val: %ld", PyInt_AsLong(val));
|
Dprintf("typecast_add: adding val: %ld", PyInt_AsLong(val));
|
||||||
if (binary) {
|
PyDict_SetItem(dict, val, obj);
|
||||||
PyDict_SetItem(psyco_binary_types, val, obj);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
PyDict_SetItem(psyco_types, val, obj);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Dprintf("typecast_add: base caster: %p", type->bcast);
|
Dprintf("typecast_add: base caster: %p", type->bcast);
|
||||||
|
|
|
@ -69,7 +69,7 @@ extern PyObject *psyco_default_binary_cast;
|
||||||
|
|
||||||
/* used by module.c to init the type system and register types */
|
/* used by module.c to init the type system and register types */
|
||||||
extern int typecast_init(PyObject *dict);
|
extern int typecast_init(PyObject *dict);
|
||||||
extern int typecast_add(PyObject *obj, int binary);
|
extern int typecast_add(PyObject *obj, PyObject *dict, int binary);
|
||||||
|
|
||||||
/* the C callable typecastObject creator function */
|
/* the C callable typecastObject creator function */
|
||||||
extern PyObject *typecast_from_c(typecastObject_initlist *type, PyObject *d);
|
extern PyObject *typecast_from_c(typecastObject_initlist *type, PyObject *d);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[build_ext]
|
[build_ext]
|
||||||
define=PSYCOPG_DEBUG,PSYCOPG_EXTENSIONS,PSYCOPG_DISPLAY_SIZE,PSYCOPG_NEW_BOOLEAN,HAVE_PQFREEMEM,HAVE_PQPROTOCOL3
|
define=PSYCOPG_EXTENSIONS,PSYCOPG_DISPLAY_SIZE,PSYCOPG_NEW_BOOLEAN,HAVE_PQFREEMEM,HAVE_PQPROTOCOL3
|
||||||
# PSYCOPG_EXTENSIONS enables extensions to PEP-249 (you really want this)
|
# PSYCOPG_EXTENSIONS enables extensions to PEP-249 (you really want this)
|
||||||
# PSYCOPG_DISPLAY_SIZE enable display size calculation (a little slower)
|
# PSYCOPG_DISPLAY_SIZE enable display size calculation (a little slower)
|
||||||
# HAVE_PQFREEMEM should be defined on PostgreSQL >= 7.4
|
# HAVE_PQFREEMEM should be defined on PostgreSQL >= 7.4
|
||||||
|
|
Loading…
Reference in New Issue
Block a user