Adaptation fixes (a lot.)

This commit is contained in:
Federico Di Gregorio 2005-02-28 15:50:55 +00:00
parent 1ec3c83720
commit cd672525e1
15 changed files with 382 additions and 77 deletions

View File

@ -1,3 +1,22 @@
2005-03-01 Federico Di Gregorio <fog@debian.org>
* psycopg/microprotocols.c (microprotocols_adapt): fixed small
typo that made adaptation using __conform__ impossible.
2005-02-28 Federico Di Gregorio <fog@debian.org>
* lib/extras.py: removed AsIs adapter (now a built-in); also
removed prepare() method from the adapters that don't use it to
avoid an extra method call at mogrification time.
* psycopg/psycopgmodule.c (psyco_adapters_init): added
initialization of the AsIs adapter (adapts int, long, float and
*wonder* None!)
* psycopg/cursor_type.c (_mogrify_getquoted): reorganized the code
to adapt and then call .getquoted() to obtain the quoted data into
this new function.
2005-2-27 Federico Di Gregorio <fog@initd.org>
* examples/myfirstrecipe.py: fixed adapter registration.

2
NEWS
View File

@ -11,6 +11,8 @@ What's new in psycopg 1.99.12
connect to an old protocol 2 database and will detect it and use the
right code.
* getquoted() called for real by the mogrification code.
What's new in psycopg 1.99.11
-----------------------------

View File

@ -23,6 +23,12 @@ import sys, psycopg
import psycopg.extensions
import whrandom
# importing psycopg.extras will give us a nice tuple adapter: this is wrong
# because the adapter is meant to be used in SQL IN clauses while we use
# tuples to represent points but it works and the example is about Rect, not
# "Point"
import psycopg.extras
if len(sys.argv) > 1:
DSN = sys.argv[1]
@ -43,7 +49,7 @@ conn.commit()
# usually a function, but we use a class, just to demonstrate the
# flexibility of the psycopg casting system
class Rect:
class Rect(object):
"""Very simple rectangle.
Note that we use this type as a data holder, as an adapter of itself for
@ -59,7 +65,6 @@ class Rect:
def __conform__(self, proto):
"""This is a terrible hack, just ignore proto and return self."""
print "CONFORMIG!"
return self
def from_points(self, x0, y0, x1, y1):
@ -82,11 +87,6 @@ class Rect:
self.x, self.y, self.x + self.width, self.y + self.height)
return s
__str__ = getquoted
def prepare(self, conn):
pass
def show(self):
"""Format a description of the box."""
s = "X: %d\tY: %d\tWidth: %d\tHeight: %d" % (

View File

@ -71,40 +71,19 @@ class DictRow(list):
class AsIs(object):
"""An adapter that just return the object 'as is'.
psycopg 1.99.9 has some optimizations that make impossible to call adapt()
without adding some basic adapters externally. This limitation will be
lifted in a future release. In the meantime you can use the AsIs adapter.
"""
def __init__(self, obj):
self.__obj = obj
def getquoted(self):
return str(self.__obj)
def prepare(self, conn):
pass
__str__ = getquoted
class SQL_IN(object):
"""Adapt any iterable to an SQL quotable object."""
def __init__(self, seq):
self._seq = seq
def prepare(self, conn):
pass
def getquoted(self):
# this is the important line: note how every object in the
# list is adapted and then how getquoted() is called on it
qobjs = [str(_A(o).getquoted()) for o in self._seq]
return '(' + ', '.join(qobjs) + ')'
__str__ = getquoted
_RA(tuple, SQL_IN)
_RA(int, AsIs)
_RA(float, AsIs)

223
psycopg/adapter_asis.c Normal file
View File

@ -0,0 +1,223 @@
/* adapter_asis.c - adapt types as they are
*
* Copyright (C) 2003-2004 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* 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
* 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <Python.h>
#include <structmember.h>
#include <stringobject.h>
#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/adapter_asis.h"
/** the AsIs object **/
static PyObject *
asis_str(asisObject *self)
{
if (self->wrapped == Py_None) {
return PyString_FromString("NULL");
}
else {
return PyObject_Repr(self->wrapped);
}
}
PyObject *
asis_getquoted(asisObject *self, PyObject *args)
{
if (!PyArg_ParseTuple(args, "")) return NULL;
return asis_str(self);
}
PyObject *
asis_prepare(asisObject *self, PyObject *args)
{
PyObject *fake;
if (!PyArg_ParseTuple(args, "O", &fake)) return NULL;
Py_INCREF(Py_None);
return Py_None;
}
/** the AsIs object */
/* object member list */
static struct PyMemberDef asisObject_members[] = {
{"adapted", T_OBJECT, offsetof(asisObject, wrapped), RO},
{NULL}
};
/* object method table */
static PyMethodDef asisObject_methods[] = {
{"getquoted", (PyCFunction)asis_getquoted, METH_VARARGS,
"getquoted() -> wrapped object value as SQL-quoted string"},
/* {"prepare", (PyCFunction)asis_prepare, METH_VARARGS,
"prepare(conn) -> currently does nothing"}, */
{NULL} /* Sentinel */
};
/* initialization and finalization methods */
static int
asis_setup(asisObject *self, PyObject *obj)
{
Dprintf("asis_setup: init asis object at %p, refcnt = %d",
self, ((PyObject *)self)->ob_refcnt);
self->wrapped = obj;
Py_INCREF(self->wrapped);
Dprintf("asis_setup: good asis object at %p, refcnt = %d",
self, ((PyObject *)self)->ob_refcnt);
return 0;
}
static void
asis_dealloc(PyObject* obj)
{
asisObject *self = (asisObject *)obj;
Py_XDECREF(self->wrapped);
Dprintf("asis_dealloc: deleted asis object at %p, refcnt = %d",
obj, obj->ob_refcnt);
obj->ob_type->tp_free(obj);
}
static int
asis_init(PyObject *obj, PyObject *args, PyObject *kwds)
{
PyObject *o;
if (!PyArg_ParseTuple(args, "O", &o))
return -1;
return asis_setup((asisObject *)obj, o);
}
static PyObject *
asis_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
return type->tp_alloc(type, 0);
}
static void
asis_del(PyObject* self)
{
PyObject_Del(self);
}
static PyObject *
asis_repr(asisObject *self)
{
return PyString_FromFormat("<psycopg.AsIs object at %p>", self);
}
/* object type */
#define asisType_doc \
"psycopg.AsIs(str) -> new AsIs adapter object"
PyTypeObject asisType = {
PyObject_HEAD_INIT(NULL)
0,
"psycopg._psycopg.AsIs",
sizeof(asisObject),
0,
asis_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
(reprfunc)asis_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
(reprfunc)asis_str, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
asisType_doc, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
asisObject_methods, /*tp_methods*/
asisObject_members, /*tp_members*/
0, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
asis_init, /*tp_init*/
PyType_GenericAlloc, /*tp_alloc*/
asis_new, /*tp_new*/
(freefunc)asis_del, /*tp_free Low-level free-memory routine */
0, /*tp_is_gc For PyObject_IS_GC */
0, /*tp_bases*/
0, /*tp_mro method resolution order */
0, /*tp_cache*/
0, /*tp_subclasses*/
0 /*tp_weaklist*/
};
/** module-level functions **/
PyObject *
psyco_AsIs(PyObject *module, PyObject *args)
{
PyObject *obj;
if (!PyArg_ParseTuple(args, "O", &obj))
return NULL;
return PyObject_CallFunction((PyObject *)&asisType, "O", obj);
}

51
psycopg/adapter_asis.h Normal file
View File

@ -0,0 +1,51 @@
/* adapter_asis.h - definition for the psycopg AsIs type wrapper
*
* Copyright (C) 2003-2005 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* 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
* 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef PSYCOPG_ASIS_H
#define PSYCOPG_ASIS_H 1
#include <Python.h>
#ifdef __cplusplus
extern "C" {
#endif
extern PyTypeObject asisType;
typedef struct {
PyObject HEAD;
/* this is the real object we wrap */
PyObject *wrapped;
} asisObject;
/* functions exported to psycopgmodule.c */
extern PyObject *psyco_AsIs(PyObject *module, PyObject *args);
#define psyco_AsIs_doc \
"psycopg.AsIs(obj) -> new AsIs wrapper object"
#ifdef __cplusplus
}
#endif
#endif /* !defined(PSYCOPG_ASIS_H) */

View File

@ -190,8 +190,8 @@ static struct PyMemberDef binaryObject_members[] = {
static PyMethodDef binaryObject_methods[] = {
{"getquoted", (PyCFunction)binary_getquoted, METH_VARARGS,
"getquoted() -> wrapped object value as SQL-quoted binary string"},
{"prepare", (PyCFunction)binary_prepare, METH_VARARGS,
"prepare(conn) -> currently does nothing"},
/* {"prepare", (PyCFunction)binary_prepare, METH_VARARGS,
"prepare(conn) -> currently does nothing"},*/
{NULL} /* Sentinel */
};

View File

@ -108,8 +108,8 @@ static struct PyMemberDef pydatetimeObject_members[] = {
static PyMethodDef pydatetimeObject_methods[] = {
{"getquoted", (PyCFunction)pydatetime_getquoted, METH_VARARGS,
"getquoted() -> wrapped object value as SQL date/time"},
{"prepare", (PyCFunction)pydatetime_prepare, METH_VARARGS,
"prepare(conn) -> currently does nothing"},
/* {"prepare", (PyCFunction)pydatetime_prepare, METH_VARARGS,
"prepare(conn) -> currently does nothing"}, */
{NULL} /* Sentinel */
};

View File

@ -84,8 +84,8 @@ static struct PyMemberDef mxdatetimeObject_members[] = {
static PyMethodDef mxdatetimeObject_methods[] = {
{"getquoted", (PyCFunction)mxdatetime_getquoted, METH_VARARGS,
"getquoted() -> wrapped object value as SQL date/time"},
{"prepare", (PyCFunction)mxdatetime_prepare, METH_VARARGS,
"prepare(conn) -> currently does nothing"},
/* {"prepare", (PyCFunction)mxdatetime_prepare, METH_VARARGS,
"prepare(conn) -> currently does nothing"}, */
{NULL} /* Sentinel */
};

View File

@ -76,8 +76,8 @@ static struct PyMemberDef pbooleanObject_members[] = {
static PyMethodDef pbooleanObject_methods[] = {
{"getquoted", (PyCFunction)pboolean_getquoted, METH_VARARGS,
"getquoted() -> wrapped object value as SQL-quoted string"},
{"prepare", (PyCFunction)pboolean_prepare, METH_VARARGS,
"prepare(conn) -> currently does nothing"},
/* {"prepare", (PyCFunction)pboolean_prepare, METH_VARARGS,
"prepare(conn) -> currently does nothing"}, */
{NULL} /* Sentinel */
};

View File

@ -61,6 +61,38 @@ psyco_curs_close(cursorObject *self, PyObject *args)
/* mogrify a query string and build argument array or dict */
static PyObject*
_mogrify_getquoted(PyObject *obj, connectionObject *conn)
{
PyObject *res = NULL;
PyObject *tmp = microprotocols_adapt(
obj, (PyObject*)&isqlquoteType, NULL);
if (tmp != NULL) {
Dprintf("_mogrify: adapted to %s", tmp->ob_type->tp_name);
/* if requested prepare the object passing it the connection */
if (PyObject_HasAttrString(tmp, "prepare")) {
res = PyObject_CallMethod(tmp, "prepare", "O", (PyObject*)conn);
if (res == NULL) {
Py_DECREF(tmp);
return NULL;
}
else {
Py_DECREF(res);
}
}
/* call the getquoted method on tmp (that should exist because we
adapted to the right protocol) */
res = PyObject_CallMethod(tmp, "getquoted", NULL);
Py_DECREF(tmp);
}
/* we return res with one extra reference, the caller shall free it */
return res;
}
static int
_mogrify(PyObject *var, PyObject *fmt, connectionObject *conn, PyObject **new)
{
@ -131,28 +163,17 @@ _mogrify(PyObject *var, PyObject *fmt, connectionObject *conn, PyObject **new)
if (*d) *d = 's';
}
else {
t = microprotocols_adapt(value,
(PyObject*)&isqlquoteType,
NULL);
t = _mogrify_getquoted(value, conn);
if (t != NULL) {
/* t is a new object, refcnt = 1 */
Dprintf("_mogrify: adapted to %s",
t->ob_type->tp_name);
/* prepare the object passing it the connection */
PyObject_CallMethod(t, "prepare", "O",
(PyObject*)conn);
PyDict_SetItem(n, key, t);
/* both key and t refcnt +1, key is at 2 now */
}
else {
/* we did not found an adapter but we don't raise
an exception; just pass the original value */
PyErr_Clear();
PyDict_SetItem(n, key, value);
Dprintf("_mogrify: set value refcnt: %d",
value->ob_refcnt);
/* no adapter found, raise a BIG exception */
Py_XDECREF(value);
Py_DECREF(n);
return -1;
}
}
@ -200,20 +221,16 @@ _mogrify(PyObject *var, PyObject *fmt, connectionObject *conn, PyObject **new)
Py_DECREF(value);
}
else {
PyObject *t = microprotocols_adapt(value,
(PyObject*)&isqlquoteType,
NULL);
if (t != NULL) {
/* prepare the object passing it the connection */
PyObject_CallMethod(t, "prepare", "O", (PyObject*)conn);
PyObject *t = _mogrify_getquoted(value, conn);
if (t != NULL) {
PyTuple_SET_ITEM(n, index, t);
Py_DECREF(value);
}
else {
PyErr_Clear();
PyTuple_SET_ITEM(n, index, value);
/* here we steal value ref, no need to DECREF */
Py_DECREF(n);
Py_DECREF(value);
return -1;
}
}
c = d;
@ -228,7 +245,7 @@ _mogrify(PyObject *var, PyObject *fmt, connectionObject *conn, PyObject **new)
n = PyTuple_New(0);
*new = n;
return 0;;
return 0;
}
#define psyco_curs_execute_doc \

View File

@ -58,8 +58,7 @@ microprotocols_add(PyTypeObject *type, PyObject *proto, PyObject *cast)
{
if (proto == NULL) proto = (PyObject*)&isqlquoteType;
Dprintf("microprotocols_add: cast %p for (%s, ?)",
cast, type->tp_name);
Dprintf("microprotocols_add: cast %p for (%s, ?)", cast, type->tp_name);
PyDict_SetItem(psyco_adapters,
Py_BuildValue("(OO)", (PyObject*)type, proto),
@ -78,6 +77,8 @@ microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
because the ISQLQuote type is abstract and there is no way to get a
quotable object to be its instance */
Dprintf("microprotocols_adapt: trying to adapt %s", obj->ob_type->tp_name);
/* look for an adapter in the registry */
key = Py_BuildValue("(OO)", (PyObject*)obj->ob_type, proto);
adapter = PyDict_GetItem(psyco_adapters, key);
@ -95,14 +96,14 @@ microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
return NULL;
}
/* and finally try to have the object adapt itself */
/* and finally try to have the object adapt itself */
if (PyObject_HasAttrString(obj, "__conform__")) {
PyObject *adapted = PyObject_CallMethod(proto, "__conform__","O", obj);
PyObject *adapted = PyObject_CallMethod(obj, "__conform__","O", proto);
if (adapted && adapted != Py_None) return adapted;
if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError))
return NULL;
}
/* else set the right exception and return NULL */
PyErr_SetString(ProgrammingError, "can't adapt");
return NULL;

View File

@ -106,8 +106,8 @@ static struct PyMethodDef isqlquoteObject_methods[] = {
METH_VARARGS, psyco_isqlquote_getbinary_doc},
{"getbuffer", (PyCFunction)psyco_isqlquote_getbuffer,
METH_VARARGS, psyco_isqlquote_getbuffer_doc},
{"prepare", (PyCFunction)psyco_isqlquote_prepare,
METH_VARARGS, psyco_isqlquote_prepare_doc},
/* {"prepare", (PyCFunction)psyco_isqlquote_prepare,
METH_VARARGS, psyco_isqlquote_prepare_doc}, */
{NULL}
};

View File

@ -34,6 +34,7 @@
#include "psycopg/adapter_qstring.h"
#include "psycopg/adapter_binary.h"
#include "psycopg/adapter_pboolean.h"
#include "psycopg/adapter_asis.h"
#ifdef HAVE_MXDATETIME
@ -187,6 +188,10 @@ psyco_adapters_init(PyObject *mod)
{
PyObject *call;
microprotocols_add(&PyFloat_Type, NULL, (PyObject*)&asisType);
microprotocols_add(&PyInt_Type, NULL, (PyObject*)&asisType);
microprotocols_add(&PyLong_Type, NULL, (PyObject*)&asisType);
microprotocols_add(&PyString_Type, NULL, (PyObject*)&qstringType);
microprotocols_add(&PyUnicode_Type, NULL, (PyObject*)&qstringType);
microprotocols_add(&PyBuffer_Type, NULL, (PyObject*)&binaryType);
@ -225,6 +230,9 @@ static encodingPair encodings[] = {
{"SQL_ASCII", "ascii"},
{"LATIN1", "latin_1"},
{"UNICODE", "utf_8"},
/* some compatibility stuff */
{"latin-1", "latin_1"},
{NULL, NULL}
};
static void psyco_encodings_fill(PyObject *dict)
@ -328,7 +336,9 @@ static PyMethodDef psycopgMethods[] = {
METH_VARARGS, psyco_register_type_doc},
{"new_type", (PyCFunction)typecast_from_python,
METH_VARARGS|METH_KEYWORDS},
{"AsIs", (PyCFunction)psyco_AsIs,
METH_VARARGS, psyco_AsIs_doc},
{"QuotedString", (PyCFunction)psyco_QuotedString,
METH_VARARGS, psyco_QuotedString_doc},
{"Boolean", (PyCFunction)psyco_Boolean,
@ -388,8 +398,9 @@ init_psycopg(void)
cursorType.ob_type = &PyType_Type;
typecastType.ob_type = &PyType_Type;
qstringType.ob_type = &PyType_Type;
binaryType.ob_type = &PyType_Type;
isqlquoteType.ob_type = &PyType_Type;
binaryType.ob_type = &PyType_Type;
isqlquoteType.ob_type = &PyType_Type;
asisType.ob_type = &PyType_Type;
if (PyType_Ready(&connectionType) == -1) return;
if (PyType_Ready(&cursorType) == -1) return;
@ -397,6 +408,7 @@ init_psycopg(void)
if (PyType_Ready(&qstringType) == -1) return;
if (PyType_Ready(&binaryType) == -1) return;
if (PyType_Ready(&isqlquoteType) == -1) return;
if (PyType_Ready(&asisType) == -1) return;
#ifdef HAVE_PYBOOL
pbooleanType.ob_type = &PyType_Type;

View File

@ -47,7 +47,7 @@ from distutils.core import setup, Extension
from distutils.sysconfig import get_python_inc
import distutils.ccompiler
PSYCOPG_VERSION = '1.99.11'
PSYCOPG_VERSION = '1.99.11/devel'
have_pydatetime = False
have_mxdatetime = False
@ -114,7 +114,8 @@ sources = [
'psycopgmodule.c', 'pqpath.c', 'typecast.c',
'microprotocols.c', 'microprotocols_proto.c',
'connection_type.c', 'connection_int.c', 'cursor_type.c', 'cursor_int.c',
'adapter_qstring.c', 'adapter_pboolean.c', 'adapter_binary.c']
'adapter_qstring.c', 'adapter_pboolean.c', 'adapter_binary.c',
'adapter_asis.c']
# check for mx package
mxincludedir = os.path.join(get_python_inc(plat_specific=1), "mx")