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 and was adapted for pysqlite. Federico Di
6  * Gregorio gave the permission to use it within pysqlite under the following
7  * license:
8  *
9  * This software is provided 'as-is', without any express or implied
10  * warranty.  In no event will the authors be held liable for any damages
11  * arising from the use of this software.
12  *
13  * Permission is granted to anyone to use this software for any purpose,
14  * including commercial applications, and to alter it and redistribute it
15  * freely, subject to the following restrictions:
16  *
17  * 1. The origin of this software must not be misrepresented; you must not
18  *    claim that you wrote the original software. If you use this software
19  *    in a product, an acknowledgment in the product documentation would be
20  *    appreciated but is not required.
21  * 2. Altered source versions must be plainly marked as such, and must not be
22  *    misrepresented as being the original software.
23  * 3. This notice may not be removed or altered from any source distribution.
24  */
25 
26 #include <Python.h>
27 
28 #include "cursor.h"
29 #include "microprotocols.h"
30 #include "prepare_protocol.h"
31 
32 
33 /** the adapters registry **/
34 
35 static PyObject *psyco_adapters = NULL;
36 
37 /* pysqlite_microprotocols_init - initialize the adapters dictionary */
38 
39 int
pysqlite_microprotocols_init(PyObject * dict)40 pysqlite_microprotocols_init(PyObject *dict)
41 {
42     /* create adapters dictionary and put it in module namespace */
43     if ((psyco_adapters = PyDict_New()) == NULL) {
44         return -1;
45     }
46 
47     return PyDict_SetItemString(dict, "adapters", psyco_adapters);
48 }
49 
50 
51 /* pysqlite_microprotocols_add - add a reverse type-caster to the dictionary */
52 
53 int
pysqlite_microprotocols_add(PyTypeObject * type,PyObject * proto,PyObject * cast)54 pysqlite_microprotocols_add(PyTypeObject *type, PyObject *proto, PyObject *cast)
55 {
56     PyObject* key;
57     int rc;
58 
59     if (proto == NULL) proto = (PyObject*)&pysqlite_PrepareProtocolType;
60 
61     key = Py_BuildValue("(OO)", (PyObject*)type, proto);
62     if (!key) {
63         return -1;
64     }
65 
66     rc = PyDict_SetItem(psyco_adapters, key, cast);
67     Py_DECREF(key);
68 
69     return rc;
70 }
71 
72 /* pysqlite_microprotocols_adapt - adapt an object to the built-in protocol */
73 
74 PyObject *
pysqlite_microprotocols_adapt(PyObject * obj,PyObject * proto,PyObject * alt)75 pysqlite_microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
76 {
77     _Py_IDENTIFIER(__adapt__);
78     _Py_IDENTIFIER(__conform__);
79     PyObject *adapter, *key, *adapted;
80 
81     /* we don't check for exact type conformance as specified in PEP 246
82        because the pysqlite_PrepareProtocolType type is abstract and there is no
83        way to get a quotable object to be its instance */
84 
85     /* look for an adapter in the registry */
86     key = Py_BuildValue("(OO)", (PyObject*)Py_TYPE(obj), proto);
87     if (!key) {
88         return NULL;
89     }
90     adapter = PyDict_GetItemWithError(psyco_adapters, key);
91     Py_DECREF(key);
92     if (adapter) {
93         Py_INCREF(adapter);
94         adapted = PyObject_CallOneArg(adapter, obj);
95         Py_DECREF(adapter);
96         return adapted;
97     }
98     if (PyErr_Occurred()) {
99         return NULL;
100     }
101 
102     /* try to have the protocol adapt this object */
103     if (_PyObject_LookupAttrId(proto, &PyId___adapt__, &adapter) < 0) {
104         return NULL;
105     }
106     if (adapter) {
107         adapted = PyObject_CallOneArg(adapter, obj);
108         Py_DECREF(adapter);
109 
110         if (adapted == Py_None) {
111             Py_DECREF(adapted);
112         }
113         else if (adapted || !PyErr_ExceptionMatches(PyExc_TypeError)) {
114             return adapted;
115         }
116         else {
117             PyErr_Clear();
118         }
119     }
120 
121     /* and finally try to have the object adapt itself */
122     if (_PyObject_LookupAttrId(obj, &PyId___conform__, &adapter) < 0) {
123         return NULL;
124     }
125     if (adapter) {
126         adapted = PyObject_CallOneArg(adapter, proto);
127         Py_DECREF(adapter);
128 
129         if (adapted == Py_None) {
130             Py_DECREF(adapted);
131         }
132         else if (adapted || !PyErr_ExceptionMatches(PyExc_TypeError)) {
133             return adapted;
134         }
135         else {
136             PyErr_Clear();
137         }
138     }
139 
140     if (alt) {
141         Py_INCREF(alt);
142         return alt;
143     }
144     /* else set the right exception and return NULL */
145     PyErr_SetString(pysqlite_ProgrammingError, "can't adapt");
146     return NULL;
147 }
148 
149 /** module-level functions **/
150 
151 PyObject *
pysqlite_adapt(pysqlite_Cursor * self,PyObject * args)152 pysqlite_adapt(pysqlite_Cursor *self, PyObject *args)
153 {
154     PyObject *obj, *alt = NULL;
155     PyObject *proto = (PyObject*)&pysqlite_PrepareProtocolType;
156 
157     if (!PyArg_ParseTuple(args, "O|OO", &obj, &proto, &alt)) return NULL;
158     return pysqlite_microprotocols_adapt(obj, proto, alt);
159 }
160