*
* Copyright (C) 2003-2019 Federico Di Gregorio <fog@debian.org>
* Copyright (C) 2020-2021 The Psycopg Team
*
* This file is part of psycopg.
*
* psycopg2 is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the copyright holders give
* permission to link this program with the OpenSSL library (or with
* modified versions of OpenSSL that use the same license as OpenSSL),
* and distribute linked combinations including the two.
*
* You must obey the GNU Lesser General Public License in all respects for
* all of the code used other than OpenSSL.
*
* psycopg2 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 Lesser General Public
* License for more details.
*/
#define PSYCOPG_MODULE
#include "psycopg/psycopg.h"
#include "psycopg/microprotocols.h"
#include "psycopg/microprotocols_proto.h"
#include "psycopg/cursor.h"
#include "psycopg/connection.h"
PyObject *psyco_adapters;
RAISES_NEG int
microprotocols_init(PyObject *module)
{
if (!(psyco_adapters = PyDict_New())) {
return -1;
}
Py_INCREF(psyco_adapters);
if (0 > PyModule_AddObject(module, "adapters", psyco_adapters)) {
Py_DECREF(psyco_adapters);
return -1;
}
return 0;
}
*
* Return 0 on success, else -1 and set an exception.
*/
RAISES_NEG int
microprotocols_add(PyTypeObject *type, PyObject *proto, PyObject *cast)
{
PyObject *key = NULL;
int rv = -1;
if (proto == NULL) proto = (PyObject*)&isqlquoteType;
if (!(key = PyTuple_Pack(2, (PyObject*)type, proto))) { goto exit; }
if (0 != PyDict_SetItem(psyco_adapters, key, cast)) { goto exit; }
rv = 0;
exit:
Py_XDECREF(key);
return rv;
}
*
* If it does, return a *borrowed reference* to the adapter, else to None.
*/
BORROWED static PyObject *
_get_superclass_adapter(PyObject *obj, PyObject *proto)
{
PyTypeObject *type;
PyObject *mro, *st;
PyObject *key, *adapter;
Py_ssize_t i, ii;
type = Py_TYPE(obj);
if (!(type->tp_mro)) {
return Py_None;
}
mro = type->tp_mro;
for (i = 1, ii = PyTuple_GET_SIZE(mro); i < ii; ++i) {
st = PyTuple_GET_ITEM(mro, i);
if (!(key = PyTuple_Pack(2, st, proto))) { return NULL; }
adapter = PyDict_GetItem(psyco_adapters, key);
Py_DECREF(key);
if (adapter) {
Dprintf(
"microprotocols_adapt: using '%s' adapter to adapt '%s'",
((PyTypeObject *)st)->tp_name, type->tp_name);
* so that the next time it will be found in the fast path */
* It would become a leak in case of dynamic
* classes generated in a loop (think namedtuples). */
* PyDict_SetItem(psyco_adapters, key, adapter);
* Py_DECREF(key);
*/
return adapter;
}
}
return Py_None;
}
PyObject *
microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
{
PyObject *adapter, *adapted, *key, *meth;
char buffer[256];
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",
Py_TYPE(obj)->tp_name);
if (!(key = PyTuple_Pack(2, Py_TYPE(obj), proto))) { return NULL; }
adapter = PyDict_GetItem(psyco_adapters, key);
Py_DECREF(key);
if (adapter) {
adapted = PyObject_CallFunctionObjArgs(adapter, obj, NULL);
return adapted;
}
if ((meth = PyObject_GetAttrString(proto, "__adapt__"))) {
adapted = PyObject_CallFunctionObjArgs(meth, obj, NULL);
Py_DECREF(meth);
if (adapted && adapted != Py_None) return adapted;
Py_XDECREF(adapted);
if (PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
PyErr_Clear();
} else {
return NULL;
}
}
}
else {
PyErr_Clear();
}
if ((meth = PyObject_GetAttrString(obj, "__conform__"))) {
adapted = PyObject_CallFunctionObjArgs(meth, proto, NULL);
Py_DECREF(meth);
if (adapted && adapted != Py_None) return adapted;
Py_XDECREF(adapted);
if (PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
PyErr_Clear();
} else {
return NULL;
}
}
}
else {
PyErr_Clear();
}
if (!(adapter = _get_superclass_adapter(obj, proto))) {
return NULL;
}
if (Py_None != adapter) {
adapted = PyObject_CallFunctionObjArgs(adapter, obj, NULL);
return adapted;
}
PyOS_snprintf(buffer, 255, "can't adapt type '%s'",
Py_TYPE(obj)->tp_name);
psyco_set_error(ProgrammingError, NULL, buffer);
return NULL;
}
*
* Return a bytes string, NULL on error.
*/
PyObject *
microprotocol_getquoted(PyObject *obj, connectionObject *conn)
{
PyObject *res = NULL;
PyObject *prepare = NULL;
PyObject *adapted;
if (!(adapted = microprotocols_adapt(obj, (PyObject*)&isqlquoteType, NULL))) {
goto exit;
}
Dprintf("microprotocol_getquoted: adapted to %s",
Py_TYPE(adapted)->tp_name);
if (conn) {
if ((prepare = PyObject_GetAttrString(adapted, "prepare"))) {
res = PyObject_CallFunctionObjArgs(
prepare, (PyObject *)conn, NULL);
if (res) {
Py_DECREF(res);
res = NULL;
} else {
goto exit;
}
}
else {
PyErr_Clear();
}
}
adapted to the right protocol) */
res = PyObject_CallMethod(adapted, "getquoted", NULL);
if (res && PyUnicode_CheckExact(res)) {
PyObject *b;
b = conn_encode(conn, res);
Py_DECREF(res);
res = b;
}
exit:
Py_XDECREF(adapted);
Py_XDECREF(prepare);
return res;
}
PyObject *
psyco_microprotocols_adapt(cursorObject *self, PyObject *args)
{
PyObject *obj, *alt = NULL;
PyObject *proto = (PyObject*)&isqlquoteType;
if (!PyArg_ParseTuple(args, "O|OO", &obj, &proto, &alt)) return NULL;
return microprotocols_adapt(obj, proto, alt);
}