/* microprotocols.c - minimalist and non-validating protocols implementation * * Copyright (C) 2003-2004 Federico Di Gregorio * * 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 #include #define PSYCOPG_MODULE #include "psycopg/config.h" #include "psycopg/python.h" #include "psycopg/psycopg.h" #include "psycopg/cursor.h" #include "psycopg/connection.h" #include "psycopg/microprotocols.h" #include "psycopg/microprotocols_proto.h" /** the adapters registry **/ PyObject *psyco_adapters; /* microprotocols_init - initialize the adapters dictionary */ int microprotocols_init(PyObject *dict) { /* create adapters dictionary and put it in module namespace */ if ((psyco_adapters = PyDict_New()) == NULL) { return -1; } PyDict_SetItemString(dict, "adapters", psyco_adapters); return 0; } /* microprotocols_add - add a reverse type-caster to the dictionary */ int 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); PyDict_SetItem(psyco_adapters, Py_BuildValue("(OO)", (PyObject*)type, proto), cast); return 0; } /* microprotocols_adapt - adapt an object to the built-in protocol */ PyObject * microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt) { PyObject *adapter, *key; /* we don't check for exact type conformance as specified in PEP 246 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); Py_DECREF(key); if (adapter) { PyObject *adapted = PyObject_CallFunctionObjArgs(adapter, obj, NULL); return adapted; } /* try to have the protocol adapt this object*/ if (PyObject_HasAttrString(proto, "__adapt__")) { PyObject *adapted = PyObject_CallMethod(proto, "__adapt__", "O", obj); if (adapted && adapted != Py_None) return adapted; Py_XDECREF(adapted); if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError)) return NULL; } /* and finally try to have the object adapt itself */ if (PyObject_HasAttrString(obj, "__conform__")) { PyObject *adapted = PyObject_CallMethod(obj, "__conform__","O", proto); if (adapted && adapted != Py_None) return adapted; Py_XDECREF(adapted); if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError)) return NULL; } /* else set the right exception and return NULL */ psyco_set_error(ProgrammingError, NULL, "can't adapt", NULL, NULL); return NULL; } /* microprotocol_getquoted - utility function that adapt and call getquoted */ PyObject * microprotocol_getquoted(PyObject *obj, connectionObject *conn) { PyObject *res = NULL; PyObject *tmp = microprotocols_adapt( obj, (PyObject*)&isqlquoteType, NULL); if (tmp != NULL) { Dprintf("microprotocol_getquoted: adapted to %s", tmp->ob_type->tp_name); /* if requested prepare the object passing it the connection */ if (PyObject_HasAttrString(tmp, "prepare") && conn) { 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; } /** module-level functions **/ 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); }