Merge remote-tracking branch 'origin/pycurl' into planetlab-4_0-branch
[plcapi.git] / trunk / psycopg2 / psycopg / microprotocols.c
1 /* microprotocols.c - minimalist and non-validating protocols implementation
2  *
3  * Copyright (C) 2003-2004 Federico Di Gregorio <fog@debian.org>
4  *
5  * This file is part of psycopg.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2,
10  * or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */
21
22 #include <Python.h>
23 #include <structmember.h>
24
25 #define PSYCOPG_MODULE
26 #include "psycopg/config.h"
27 #include "psycopg/python.h"
28 #include "psycopg/psycopg.h"
29 #include "psycopg/cursor.h"
30 #include "psycopg/connection.h"
31 #include "psycopg/microprotocols.h"
32 #include "psycopg/microprotocols_proto.h"
33
34
35 /** the adapters registry **/
36
37 PyObject *psyco_adapters;
38
39 /* microprotocols_init - initialize the adapters dictionary */
40
41 int
42 microprotocols_init(PyObject *dict)
43 {
44     /* create adapters dictionary and put it in module namespace */
45     if ((psyco_adapters = PyDict_New()) == NULL) {
46         return -1;
47     }                         
48
49     PyDict_SetItemString(dict, "adapters", psyco_adapters);
50     
51     return 0;
52 }
53
54
55 /* microprotocols_add - add a reverse type-caster to the dictionary */
56
57 int
58 microprotocols_add(PyTypeObject *type, PyObject *proto, PyObject *cast)
59 {
60     if (proto == NULL) proto = (PyObject*)&isqlquoteType;
61
62     Dprintf("microprotocols_add: cast %p for (%s, ?)", cast, type->tp_name);
63
64     PyDict_SetItem(psyco_adapters,
65                    Py_BuildValue("(OO)", (PyObject*)type, proto),
66                    cast);
67     return 0;
68 }
69
70 /* microprotocols_adapt - adapt an object to the built-in protocol */
71
72 PyObject *
73 microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
74 {
75     PyObject *adapter, *key;
76
77     /* we don't check for exact type conformance as specified in PEP 246
78        because the ISQLQuote type is abstract and there is no way to get a
79        quotable object to be its instance */
80
81     Dprintf("microprotocols_adapt: trying to adapt %s", obj->ob_type->tp_name);
82     
83     /* look for an adapter in the registry */
84     key = Py_BuildValue("(OO)", (PyObject*)obj->ob_type, proto);
85     adapter = PyDict_GetItem(psyco_adapters, key);
86     Py_DECREF(key);
87     if (adapter) {
88         PyObject *adapted = PyObject_CallFunctionObjArgs(adapter, obj, NULL);
89         return adapted;
90     }
91
92     /* try to have the protocol adapt this object*/
93     if (PyObject_HasAttrString(proto, "__adapt__")) {
94         PyObject *adapted = PyObject_CallMethod(proto, "__adapt__", "O", obj);
95         if (adapted && adapted != Py_None) return adapted;
96         Py_XDECREF(adapted);
97         if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError))
98             return NULL;
99     }
100
101     /* and finally try to have the object adapt itself */   
102     if (PyObject_HasAttrString(obj, "__conform__")) {
103         PyObject *adapted = PyObject_CallMethod(obj, "__conform__","O", proto);
104         if (adapted && adapted != Py_None) return adapted;
105         Py_XDECREF(adapted);
106         if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError))
107             return NULL;
108     }
109     
110     /* else set the right exception and return NULL */
111     psyco_set_error(ProgrammingError, NULL, "can't adapt", NULL, NULL);
112     return NULL;
113 }
114
115 /* microprotocol_getquoted - utility function that adapt and call getquoted */
116
117 PyObject *
118 microprotocol_getquoted(PyObject *obj, connectionObject *conn)
119 {
120     PyObject *res = NULL;
121     PyObject *tmp = microprotocols_adapt(
122         obj, (PyObject*)&isqlquoteType, NULL);
123     
124     if (tmp != NULL) {
125         Dprintf("microprotocol_getquoted: adapted to %s",
126                 tmp->ob_type->tp_name);
127
128         /* if requested prepare the object passing it the connection */
129         if (PyObject_HasAttrString(tmp, "prepare") && conn) {
130             res = PyObject_CallMethod(tmp, "prepare", "O", (PyObject*)conn);
131             if (res == NULL) {
132                 Py_DECREF(tmp);
133                 return NULL;
134             }
135             else {
136                 Py_DECREF(res);
137             }
138         }
139
140         /* call the getquoted method on tmp (that should exist because we
141            adapted to the right protocol) */
142         res = PyObject_CallMethod(tmp, "getquoted", NULL);
143         Py_DECREF(tmp);
144     }
145
146     /* we return res with one extra reference, the caller shall free it */
147     return res;
148 }
149
150
151 /** module-level functions **/
152
153 PyObject *
154 psyco_microprotocols_adapt(cursorObject *self, PyObject *args)
155 {
156     PyObject *obj, *alt = NULL;
157     PyObject *proto = (PyObject*)&isqlquoteType;
158
159     if (!PyArg_ParseTuple(args, "O|OO", &obj, &proto, &alt)) return NULL;
160     return microprotocols_adapt(obj, proto, alt);
161 }