/* * Copyright (C) 2001-2002 Federico Di Gregorio * * This file is part of the psycopg module. * * 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. */ /* * typeobj.c -- define the type objects * $Id: typeobj.c 554 2004-10-30 00:19:27Z fog $ */ #include "module.h" #include "typeobj.h" #include static PyObject *psyco_DBAPITypeObject_new(PyObject *, PyObject *, PyObject *); extern PyTypeObject psyco_DBAPITypeObject_Type; /**** builtin C casting functions ****/ static PyObject * psyco_INTEGER_cast(PyObject *s) { PyObject *i; if (s == Py_None) {Py_INCREF(s); return s;} i = PyNumber_Int(s); Dprintf("psyco_STRING_cast(): i->refcnt = %d\n", i->ob_refcnt); return i; } static PyObject * psyco_LONGINTEGER_cast(PyObject *s) { if (s == Py_None) {Py_INCREF(s); return s;} return PyNumber_Long(s); } static PyObject * psyco_FLOAT_cast(PyObject *s) { if (s == Py_None) {Py_INCREF(s); return s;} return PyNumber_Float(s); } static PyObject * psyco_STRING_cast(PyObject *s) { if (s == Py_None) {Py_INCREF(s); return s;} Py_INCREF(s); return s; } static PyObject * psyco_DATE_cast(PyObject *s) { int n, y=0, m=0, d=0; int hh=0, mm=0; double ss=0.0; char *str; if (s == Py_None) {Py_INCREF(s); return s;} str = PyString_AsString(s); /* check for infinity */ if (!strcmp(str, "infinity") || !strcmp(str, "-infinity")) { if (str[0] == '-') { return mxDateTimeP->DateTime_FromDateAndTime(-999998,1,1, 0,0,0); } else { return mxDateTimeP->DateTime_FromDateAndTime(999999,12,31, 0,0,0); } } Dprintf("psyco_DATE_cast(): s = %s\n", str); n = sscanf(str, "%d-%d-%d %d:%d:%lf", &y, &m, &d, &hh, &mm, &ss); Dprintf("psyco_DATE_cast(): date parsed, %d components\n", n); if (n != 3 && n != 6) { PyErr_SetString(DataError, "unable to parse date or timestamp"); return NULL; } return mxDateTimeP->DateTime_FromDateAndTime(y, m, d, hh, mm, ss); } static PyObject * psyco_TIME_cast(PyObject *s) { int n, hh=0, mm=0; double ss=0.0; char *str; if (s == Py_None) {Py_INCREF(s); return s;} str = PyString_AsString(s); /* postgres gives us only the time but a DateTime object wants a date too, so we use the unix epoch as the current date. */ Dprintf("psyco_TIME_cast(): s = %s\n", str); n = sscanf(str, "%d:%d:%lf", &hh, &mm, &ss); Dprintf("psyco_TIME_cast(): time parsed, %d components\n", n); Dprintf("psyco_TIME_cast(): hh = %d, mm = %d, ss = %f\n", hh, mm, ss); if (n != 3) { PyErr_SetString(DataError, "unable to parse time"); return NULL; } return mxDateTimeP->DateTimeDelta_FromTime(hh, mm ,ss); } static char * skip_until_space(char *s) { while (*s && *s != ' ') s++; return s; } static PyObject * psyco_INTERVAL_cast(PyObject *s) { long years = 0, months = 0, days = 0, denominator = 1; double hours = 0.0, minutes = 0.0, seconds = 0.0, hundredths = 0.0; double v = 0.0, sign = 1.0; int part = 0; char *str; if (s == Py_None) {Py_INCREF(s); return s;} str = PyString_AsString(s); Dprintf("psyco_INTERVAL_cast(): s = %s\n", str); while (*str) { switch (*str) { case '-': sign = -1.0; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': v = v*10 + (double)*str - (double)'0'; Dprintf("psyco_INTERVAL_cast(): v = %f\n", v); if (part == 6){ denominator *= 10; Dprintf("psyco_INTERVAL_cast(): denominator = %ld\n", denominator); } break; case 'y': if (part == 0) { years = (long)(v*sign); str = skip_until_space(str); Dprintf("psyco_INTERVAL_cast(): years = %ld, rest = %s\n", years, str); v = 0.0; sign = 1.0; part = 1; } break; case 'm': if (part <= 1) { months = (long)(v*sign); str = skip_until_space(str); Dprintf("psyco_INTERVAL_cast(): months = %ld, rest = %s\n", months, str); v = 0.0; sign = 1.0; part = 2; } break; case 'd': if (part <= 2) { days = (long)(v*sign); str = skip_until_space(str); Dprintf("psyco_INTERVAL_cast(): days = %ld, rest = %s\n", days, str); v = 0.0; sign = 1.0; part = 3; } break; case ':': if (part <= 3) { hours = v; Dprintf("psyco_INTERVAL_cast(): hours = %f\n", hours); v = 0.0; part = 4; } else if (part == 4) { minutes = v; Dprintf("psyco_INTERVAL_cast(): minutes = %f\n", minutes); v = 0.0; part = 5; } break; case '.': if (part == 5) { seconds = v; Dprintf("psyco_INTERVAL_cast(): seconds = %f\n", seconds); v = 0.0; part = 6; } break; default: break; } str++; } /* manage last value, be it minutes or seconds or hundredths of a second */ if (part == 4) { minutes = v; Dprintf("psyco_INTERVAL_cast(): minutes = %f\n", minutes); } else if (part == 5) { seconds = v; Dprintf("psyco_INTERVAL_cast(): seconds = %f\n", seconds); } else if (part == 6) { hundredths = v; Dprintf("psyco_INTERVAL_cast(): hundredths = %f\n", hundredths); hundredths = hundredths/denominator; Dprintf("psyco_INTERVAL_cast(): ACTUAL FRACTIONS = %.20f\n", hundredths); } /* calculates seconds */ if (sign < 0.0) { seconds = - (hundredths + seconds + minutes*60 + hours*3600); } else { seconds += hundredths + minutes*60 + hours*3600; } /* calculates days */ days += years*365 + months*30; Dprintf("psyco_INTERVAL_cast(): days = %ld, seconds = %f\n", days, seconds); return mxDateTimeP->DateTimeDelta_FromDaysAndSeconds(days, seconds); } static PyObject * psyco_BOOLEAN_cast(PyObject *s) { if (s == Py_None) {Py_INCREF(s); return s;} if (PyString_AsString(s)[0] == 't') return PyInt_FromLong(1L); else return PyInt_FromLong(0L); } /* convert all the \xxx octal sequences to the proper byte value */ static PyObject * psyco_BINARY_cast(PyObject *s) { char *str, *dstptr, *dststr; int len, i; PyObject *retstr; if (s == Py_None) {Py_INCREF(s); return s;} str = PyString_AS_STRING(s); len = strlen(str); dststr = (char*)calloc(len, sizeof(char)); dstptr = dststr; Py_BEGIN_ALLOW_THREADS; for (i = 0; i < len; i++) { if (str[i] == '\\') { if ( ++i < len) { if (str[i] == '\\') { *dstptr = '\\'; } else { *dstptr = 0; *dstptr |= (str[i++] & 7) << 6; *dstptr |= (str[i++] & 7) << 3; *dstptr |= (str[i] & 7); } } } else { *dstptr = str[i]; } dstptr++; } Py_END_ALLOW_THREADS; Dprintf("Binary: %s, len: %d\n", dststr, dstptr - dststr); retstr = PyString_FromStringAndSize(dststr, dstptr - dststr); free(dststr); return retstr; } #define psyco_NUMBER_cast psyco_FLOAT_cast #define psyco_DATETIME_cast psyco_DATE_cast #define psyco_ROWID_cast psyco_INTEGER_cast /*** builtin casting objects (generated by the buildtypes.py script) ****/ #include "typeobj_builtins.c" /**** the type dictionary and associated functions ****/ /* dictionary of types and default cast object */ PyObject *psyco_types; PyObject *psyco_default_cast; PyObject *psyco_binary_cast; static int psyco_default_cast_type_DEFAULT[] = {0}; static psyco_DBAPIInitList psyco_default_cast_type = { "DEFAULT", psyco_default_cast_type_DEFAULT, psyco_STRING_cast}; /* psyco_init_types() - initialize the dictionary and create default types */ int psyco_init_types(PyObject *md) { int i; /* create type dictionary and put it in module namespace */ psyco_types = PyDict_New(); if (!psyco_types) return -1; PyDict_SetItemString(md, "types", psyco_types); /* insert the cast types into the 'types' dictionary and register them * in the module dictionary */ for (i = 0; psyco_cast_types[i].name != NULL; i++) { psyco_DBAPITypeObject *t; Dprintf("psyco_init_types: initializing %s\n", psyco_cast_types[i].name); if (!(t = (psyco_DBAPITypeObject *) new_psyco_typeobject(&(psyco_cast_types[i])))) return -1; if (psyco_add_type((PyObject *)t) != 0) return -1; PyDict_SetItem(md, t->name, (PyObject *)t); /* export binary object */ if (psyco_cast_types[i].values == psyco_cast_types_BINARY) { psyco_binary_cast = (PyObject *)t; } } assert(psyco_binary_cast != NULL); /* create and save a default cast object (but does not register it) */ psyco_default_cast = new_psyco_typeobject(&psyco_default_cast_type); return 0; } /* psyco_add_type() - add a type object to the dictionary */ int psyco_add_type(PyObject *obj) { psyco_DBAPITypeObject *type = (psyco_DBAPITypeObject *)obj; PyObject *val; int len, i; Dprintf("psyco_add_type: typeobject = %p, values refcnt = %d\n", obj, type->values->ob_refcnt); len = PyTuple_Size(type->values); for (i = 0; i < len; i++) { val = PyTuple_GetItem(type->values, i); Dprintf("psyco_add_type: val = %ld\n",PyInt_AsLong(val)); PyDict_SetItem(psyco_types, val, obj); } return 0; } /**** DBAPITypeObject OBJECT DEFINITIONS ****/ /* object member list */ #define OFFSETOF(x) offsetof(psyco_DBAPITypeObject, x) static struct memberlist psyco_DBAPITypeObject_memberlist[] = { {"name", T_OBJECT, OFFSETOF(name), RO}, {"values", T_OBJECT, OFFSETOF(values), RO}, {NULL} }; /**** DBAPITypeObject METHODS ****/ /* the python number interface for the cursor object */ static int psyco_DBAPITypeObject_coerce(PyObject **pv, PyObject **pw) { if (PyObject_TypeCheck(*pv, &psyco_DBAPITypeObject_Type)) { if (PyInt_Check(*pw)) { PyObject *coer, *args; args = PyTuple_New(1); Py_INCREF(*pw); PyTuple_SET_ITEM(args, 0, *pw); coer = psyco_DBAPITypeObject_new(NULL, args, NULL); *pw = coer; Py_DECREF(args); Py_INCREF(*pv); return 0; } else if (PyObject_TypeCheck(*pw, &psyco_DBAPITypeObject_Type)){ Py_INCREF(*pv); Py_INCREF(*pw); return 0; } } PyErr_SetString(PyExc_TypeError, "psycopg type coercion failed"); return -1; } static PyNumberMethods psyco_DBAPITypeObject_as_number = { (binaryfunc)0, /*nb_add*/ (binaryfunc)0, /*nb_subtract*/ (binaryfunc)0, /*nb_multiply*/ (binaryfunc)0, /*nb_divide*/ (binaryfunc)0, /*nb_remainder*/ (binaryfunc)0, /*nb_divmod*/ (ternaryfunc)0, /*nb_power*/ (unaryfunc)0, /*nb_negative*/ (unaryfunc)0, /*nb_positive*/ (unaryfunc)0, /*nb_absolute*/ (inquiry)0, /*nb_nonzero*/ (unaryfunc)0, /*nb_invert*/ (binaryfunc)0, /*nb_lshift*/ (binaryfunc)0, /*nb_rshift*/ (binaryfunc)0, /*nb_and*/ (binaryfunc)0, /*nb_xor*/ (binaryfunc)0, /*nb_or*/ (coercion)psyco_DBAPITypeObject_coerce, /*nb_coerce*/ (unaryfunc)0, /*nb_int*/ (unaryfunc)0, /*nb_long*/ (unaryfunc)0, /*nb_float*/ (unaryfunc)0, /*nb_oct*/ (unaryfunc)0, /*nb_hex*/ }; /* the python object interface for the cursor object */ static int psyco_DBAPITypeObject_cmp(psyco_DBAPITypeObject *self, psyco_DBAPITypeObject *v) { int res; if (PyObject_Length(v->values) > 1 && PyObject_Length(self->values) == 1) { /* calls itself exchanging the args */ Dprintf("psyco_DBAPITypeObject_cmp: swapping args\n"); return psyco_DBAPITypeObject_cmp(v, self); } res = PySequence_Contains(self->values, PyTuple_GET_ITEM(v->values, 0)); Dprintf("psyco_DBAPITypeObject_cmp: res = %d\n", res); if (res < 0) return res; else return (res == 1 ? 0 : 1); } static struct PyMethodDef psyco_DBAPITypeObject_methods[] = { {"__cmp__", (PyCFunction)psyco_DBAPITypeObject_cmp, METH_VARARGS, NULL}, {NULL, NULL} }; static PyObject * psyco_DBAPITypeObject_getattr(psyco_DBAPITypeObject *self, char *name) { PyObject *rv; rv = PyMember_Get((char *)self, psyco_DBAPITypeObject_memberlist, name); if (rv) return rv; PyErr_Clear(); return Py_FindMethod(psyco_DBAPITypeObject_methods, (PyObject *)self, name); } static void psyco_DBAPITypeObject_destroy(psyco_DBAPITypeObject *self) { PyObject *name, *cast, *values; values = self->values; name = self->name; cast = self->pcast; Dprintf("psyco_DBAPITypeObject_destroy: value refcnt = %d\n", values->ob_refcnt); Dprintf("psyco_DBAPITypeObject_destroy: value[0] refcnt = %d\n", PyTuple_GET_ITEM(self->values, 0)->ob_refcnt); PyObject_Del(self); Py_XDECREF(name); Py_XDECREF(values); Py_XDECREF(cast); Dprintf("DBAPITypeObject at %p destroyed\n", self); } static PyObject * psyco_DBAPITypeObject_call(PyObject *self, PyObject *args, PyObject *keywds) { /* when called simply converts from string to an object dependent on the type this object represents */ PyObject *string, *res; psyco_DBAPITypeObject *me = (psyco_DBAPITypeObject *)self; Dprintf("psyco_DBAPITypeObject_call: self = %p, args = %p\n", self, args); if (!PyArg_ParseTuple(args, "O", &string)) { return NULL; } Dprintf("psyco_DBAPITypeObject_call: string argument at %p, refcnt = %d\n", string, string->ob_refcnt); if (me->ccast) { Dprintf("psyco_DBAPITypeObject_call: calling C cast function\n"); res = me->ccast(string); } else if (me->pcast) { PyObject *arg = PyTuple_New(1); Dprintf("psyco_DBAPITypeObject_call: calling python callable\n"); PyTuple_SET_ITEM(arg, 0, string); Py_INCREF(string); res = PyObject_CallObject(me->pcast, arg); Dprintf("psyco_DBAPITypeObject_call: res = %p\n", res); Py_DECREF(arg); } else { Py_INCREF(Py_None); res = Py_None; } Dprintf("psyco_DBAPITypeObject_call: string argument at %p, refcnt = %d\n", string, string->ob_refcnt); return res; } static char psyco_DBAPITypeObject__doc__[] = "Object to classify data types."; PyTypeObject psyco_DBAPITypeObject_Type = { #if !defined(_WIN32) && !defined(__CYGWIN__) PyObject_HEAD_INIT(&PyType_Type) #else PyObject_HEAD_INIT(NULL) #endif 0, /*ob_size*/ "type", /*tp_name*/ sizeof(psyco_DBAPITypeObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ (destructor)psyco_DBAPITypeObject_destroy, /*tp_dealloc*/ (printfunc)0, /*tp_print*/ (getattrfunc)psyco_DBAPITypeObject_getattr, /*tp_getattr*/ (setattrfunc)0, /*tp_setattr*/ (cmpfunc)psyco_DBAPITypeObject_cmp, /*tp_compare*/ (reprfunc)0, /*tp_repr*/ &psyco_DBAPITypeObject_as_number, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ (hashfunc)0, /*tp_hash*/ (ternaryfunc)psyco_DBAPITypeObject_call, /*tp_call*/ (reprfunc)0, /*tp_str*/ /* Space for future expansion */ 0L,0L,0L,0L, psyco_DBAPITypeObject__doc__ /* Documentation string */ }; static PyObject * psyco_DBAPITypeObject_new(PyObject *name, PyObject *values, PyObject *cast) { psyco_DBAPITypeObject *obj; #if defined(_WIN32) || defined(__CYGWIN__) /* For MSVC workaround */ psyco_DBAPITypeObject_Type.ob_type = &PyType_Type; #endif obj = PyObject_NEW(psyco_DBAPITypeObject, &psyco_DBAPITypeObject_Type); if (obj == NULL) return NULL; Py_INCREF(values); obj->values = values; if (name) { Py_INCREF(name); obj->name = name; } else { Py_INCREF(Py_None); obj->name = Py_None; } obj->pcast = NULL; if (cast && cast != Py_None) { Py_INCREF(cast); obj->pcast = cast; } obj->ccast = NULL; Dprintf("psyco_DBAPITypeObject_new: name = %p, values = %p, " "pcast = %p, ccast = %p\n", obj->name, obj->values, obj->pcast, obj->ccast); Dprintf("psyco_DBAPITypeObject_new: values refcnt = %d\n", values->ob_refcnt); Dprintf("psyco_DBAPITypeObject_new: object created at %p\n", obj); return (PyObject *)obj; } /* create a new type object from python */ PyObject * psyco_DBAPITypeObject_init(PyObject *self, PyObject *args, PyObject *keywds) { PyObject *v, *name, *cast = NULL; static char *kwlist[] = {"values", "name", "castobj", NULL}; if (!PyArg_ParseTupleAndKeywords(args, keywds, "O!|O!O", kwlist, &PyTuple_Type, &v, &PyString_Type, &name, &cast)) { return NULL; } Dprintf("psyco_DBAPITypeObject_init: arguments parsed\n"); return psyco_DBAPITypeObject_new(name, v, cast); } /* create a new type object from C */ PyObject * new_psyco_typeobject(psyco_DBAPIInitList *type) { PyObject *tuple; psyco_DBAPITypeObject *obj; int i, len = 0; while (type->values[len] != 0) len++; tuple = PyTuple_New(len); if (!tuple) return NULL; Dprintf("new_psyco_typeobject: type tuple created at %p\n", tuple); for (i = 0; i < len ; i++) { PyTuple_SET_ITEM(tuple, i, PyInt_FromLong(type->values[i])); } obj = (psyco_DBAPITypeObject *) psyco_DBAPITypeObject_new(PyString_FromString(type->name), tuple, NULL); if (obj) { obj->ccast = type->cast; obj->pcast = NULL; } return (PyObject *)obj; }