/* cursor_type.c - python interface to cursor objects * * 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 Likcense * along with this program; if not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #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/pqpath.h" #include "psycopg/typecast.h" #include "psycopg/microprotocols.h" #include "psycopg/microprotocols_proto.h" #include "pgversion.h" extern PyObject *pyPsycopgTzFixedOffsetTimezone; /** DBAPI methods **/ /* close method - close the cursor */ #define psyco_curs_close_doc \ "close() -- Close the cursor." static PyObject * psyco_curs_close(cursorObject *self, PyObject *args) { if (!PyArg_ParseTuple(args, "")) return NULL; EXC_IF_CURS_CLOSED(self); if (self->name != NULL) { char buffer[128]; EXC_IF_NO_MARK(self); PyOS_snprintf(buffer, 127, "CLOSE %s", self->name); if (pq_execute(self, buffer, 0) == -1) return NULL; } self->closed = 1; Dprintf("psyco_curs_close: cursor at %p closed", self); Py_INCREF(Py_None); return Py_None; } /* execute method - executes a query */ /* mogrify a query string and build argument array or dict */ static int _mogrify(PyObject *var, PyObject *fmt, connectionObject *conn, PyObject **new) { PyObject *key, *value, *n, *item; char *d, *c; int index = 0, force = 0; /* from now on we'll use n and replace its value in *new only at the end, just before returning. we also init *new to NULL to exit with an error if we can't complete the mogrification */ n = *new = NULL; c = PyString_AsString(fmt); while(*c) { /* handle plain percent symbol in format string */ if (c[0] == '%' && c[1] == '%') { c+=2; force = 1; } /* if we find '%(' then this is a dictionary, we: 1/ find the matching ')' and extract the key name 2/ locate the value in the dictionary (or return an error) 3/ mogrify the value into something usefull (quoting)... 4/ ...and add it to the new dictionary to be used as argument */ else if (c[0] == '%' && c[1] == '(') { /* let's have d point the end of the argument */ for (d = c + 2; *d && *d != ')'; d++); if (*d == ')') { key = PyString_FromStringAndSize(c+2, d-c-2); value = PyObject_GetItem(var, key); /* key has refcnt 1, value the original value + 1 */ /* if value is NULL we did not find the key (or this is not a dictionary): let python raise a KeyError */ if (value == NULL) { Py_DECREF(key); /* destroy key */ Py_XDECREF(n); /* destroy n */ return -1; } Dprintf("_mogrify: value refcnt: %d (+1)", value->ob_refcnt); if (n == NULL) { n = PyDict_New(); } if ((item = PyObject_GetItem(n, key)) == NULL) { PyObject *t = NULL; PyErr_Clear(); /* None is always converted to NULL; this is an optimization over the adapting code and can go away in the future if somebody finds a None adapter usefull. */ if (value == Py_None) { t = PyString_FromString("NULL"); PyDict_SetItem(n, key, t); /* t is a new object, refcnt = 1, key is at 2 */ /* if the value is None we need to substitute the formatting char with 's' (FIXME: this should not be necessary if we drop support for formats other than %s!) */ while (*d && !isalpha(*d)) d++; if (*d) *d = 's'; } else { t = microprotocol_getquoted(value, conn); if (t != NULL) { PyDict_SetItem(n, key, t); /* both key and t refcnt +1, key is at 2 now */ } else { /* no adapter found, raise a BIG exception */ Py_XDECREF(value); Py_DECREF(n); return -1; } } Py_XDECREF(t); /* t dies here */ /* after the DECREF value has the original refcnt plus 1 if it was added to the dictionary directly; good */ Py_XDECREF(value); } else { /* we have an item with one extra refcnt here, zap! */ Py_DECREF(item); } Py_DECREF(key); /* key has the original refcnt now */ Dprintf("_mogrify: after value refcnt: %d",value->ob_refcnt); } c = d; } else if (c[0] == '%' && c[1] != '(') { /* this is a format that expects a tuple; it is much easier, because we don't need to check the old/new dictionary for keys */ value = PySequence_GetItem(var, index); /* value has refcnt inc'ed by 1 here */ /* if value is NULL this is not a sequence or the index is wrong; anyway we let python set its own exception */ if (value == NULL) { Py_XDECREF(n); return -1; } if (n == NULL) { n = PyTuple_New(PyObject_Length(var)); } /* let's have d point just after the '%' */ d = c+1; if (value == Py_None) { PyTuple_SET_ITEM(n, index, PyString_FromString("NULL")); while (*d && !isalpha(*d)) d++; if (*d) *d = 's'; Py_DECREF(value); } else { PyObject *t = microprotocol_getquoted(value, conn); if (t != NULL) { PyTuple_SET_ITEM(n, index, t); Py_DECREF(value); } else { Py_DECREF(n); Py_DECREF(value); return -1; } } c = d; index += 1; } else { c++; } } if (force && n == NULL) n = PyTuple_New(0); *new = n; return 0; } #define psyco_curs_execute_doc \ "execute(query, vars=None, async=0) -- Execute query with bound vars." static int _psyco_curs_execute(cursorObject *self, PyObject *operation, PyObject *vars, long int async) { int res; PyObject *fquery, *cvt = NULL, *uoperation = NULL; pthread_mutex_lock(&(self->conn->lock)); if (self->conn->async_cursor != NULL && self->conn->async_cursor != (PyObject*)self) { pthread_mutex_unlock(&(self->conn->lock)); psyco_set_error(ProgrammingError, (PyObject*)self, "asynchronous query already in execution", NULL, NULL); return 0; } pthread_mutex_unlock(&(self->conn->lock)); if (!PyObject_IsTrue(operation)) { psyco_set_error(ProgrammingError, (PyObject*)self, "can't execute an empty query", NULL, NULL); return 0; } if (PyUnicode_Check(operation)) { PyObject *enc = PyDict_GetItemString(psycoEncodings, self->conn->encoding); /* enc is a borrowed reference, we won't decref it */ if (enc) { operation = PyUnicode_AsEncodedString( operation, PyString_AsString(enc), NULL); /* if there was an error during the encoding from unicode to the target encoding we just let the exception propagate */ if (operation == NULL) return 0; /* we clone operation in uoperation to be sure to free it later */ uoperation = operation; } else { PyErr_Format(InterfaceError, "can't encode unicode query to %s", self->conn->encoding); return 0; } } else if (!PyString_Check(operation)) { /* the operation is not unicode or string, raise an error */ PyErr_SetString(PyExc_TypeError, "argument 1 must be a string or unicode object"); return 0; } IFCLEARPGRES(self->pgres); if (self->query) { Py_DECREF(self->query); self->query = NULL; } Dprintf("psyco_curs_execute: starting execution of new query"); /* here we are, and we have a sequence or a dictionary filled with objects to be substituted (bound variables). we try to be smart and do the right thing (i.e., what the user expects) */ if (vars && vars != Py_None) { if(_mogrify(vars, operation, self->conn, &cvt) == -1) { Py_XDECREF(uoperation); return 0; } } if (vars && cvt) { /* if PyString_Format() return NULL an error occured: if the error is a TypeError we need to check the exception.args[0] string for the values: "not enough arguments for format string" "not all arguments converted" and return the appropriate ProgrammingError. we do that by grabbing the curren exception (we will later restore it if the type or the strings do not match.) */ if (!(fquery = PyString_Format(operation, cvt))) { PyObject *err, *arg, *trace; int pe = 0; PyErr_Fetch(&err, &arg, &trace); if (err && PyErr_GivenExceptionMatches(err, PyExc_TypeError)) { Dprintf("psyco_curs_execute: TypeError exception catched"); PyErr_NormalizeException(&err, &arg, &trace); if (PyObject_HasAttrString(arg, "args")) { PyObject *args = PyObject_GetAttrString(arg, "args"); PyObject *str = PySequence_GetItem(args, 0); char *s = PyString_AS_STRING(str); Dprintf("psyco_curs_execute: -> %s", s); if (!strcmp(s, "not enough arguments for format string") || !strcmp(s, "not all arguments converted")) { Dprintf("psyco_curs_execute: -> got a match"); psyco_set_error(ProgrammingError, (PyObject*)self, s, NULL, NULL); pe = 1; } Py_DECREF(args); Py_DECREF(str); } } /* if we did not manage our own exception, restore old one */ if (pe == 1) { Py_XDECREF(err); Py_XDECREF(arg); Py_XDECREF(trace); } else { PyErr_Restore(err, arg, trace); } Py_XDECREF(uoperation); return 0; } if (self->name != NULL) { self->query = PyString_FromFormat( "DECLARE %s CURSOR WITHOUT HOLD FOR %s", self->name, PyString_AS_STRING(fquery)); Py_DECREF(fquery); } else { self->query = fquery; } Dprintf("psyco_curs_execute: cvt->refcnt = %d", cvt->ob_refcnt); Py_DECREF(cvt); } else { if (self->name != NULL) { self->query = PyString_FromFormat( "DECLARE %s CURSOR WITHOUT HOLD FOR %s", self->name, PyString_AS_STRING(operation)); } else { Py_INCREF(operation); self->query = operation; } } res = pq_execute(self, PyString_AS_STRING(self->query), async); Dprintf("psyco_curs_execute: res = %d, pgres = %p", res, self->pgres); Py_XDECREF(uoperation); return res == -1 ? 0 : 1; } static PyObject * psyco_curs_execute(cursorObject *self, PyObject *args, PyObject *kwargs) { long int async = 0; PyObject *vars = NULL, *operation = NULL; static char *kwlist[] = {"query", "vars", "async", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Oi", kwlist, &operation, &vars, &async)) { return NULL; } if (self->name != NULL) { if (self->query != Py_None) { psyco_set_error(ProgrammingError, (PyObject*)self, "can't call .execute() on named cursors more than once", NULL, NULL); return NULL; } if (self->conn->isolation_level == 0) { psyco_set_error(ProgrammingError, (PyObject*)self, "can't use a named cursor outside of transactions", NULL, NULL); return NULL; } if (self->conn->mark != self->mark) { psyco_set_error(ProgrammingError, (PyObject*)self, "named cursor isn't valid anymore", NULL, NULL); return NULL; } } EXC_IF_CURS_CLOSED(self); if (_psyco_curs_execute(self, operation, vars, async)) { Py_INCREF(Py_None); return Py_None; } else { return NULL; } } #define psyco_curs_executemany_doc \ "executemany(query, vars_list) -- Execute many queries with bound vars." static PyObject * psyco_curs_executemany(cursorObject *self, PyObject *args, PyObject *kwargs) { PyObject *operation = NULL, *vars = NULL; PyObject *v, *iter = NULL; static char *kwlist[] = {"query", "vars_list", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO", kwlist, &operation, &vars)) { return NULL; } EXC_IF_CURS_CLOSED(self); if (self->name != NULL) { psyco_set_error(ProgrammingError, (PyObject*)self, "can't call .executemany() on named cursors", NULL, NULL); return NULL; } if (!PyIter_Check(vars)) { vars = iter = PyObject_GetIter(vars); if (iter == NULL) return NULL; } while ((v = PyIter_Next(vars)) != NULL) { if (_psyco_curs_execute(self, operation, v, 0) == 0) { Py_DECREF(v); return NULL; } else { Py_DECREF(v); } } Py_XDECREF(iter); Py_INCREF(Py_None); return Py_None; } #ifdef PSYCOPG_EXTENSIONS #define psyco_curs_mogrify_doc \ "mogrify(query, vars=None) -> str -- Return query after vars binding." static PyObject * psyco_curs_mogrify(cursorObject *self, PyObject *args, PyObject *kwargs) { PyObject *vars = NULL, *cvt = NULL, *operation = NULL; PyObject *fquery; static char *kwlist[] = {"query", "vars", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O", kwlist, &operation, &vars)) { return NULL; } if (PyUnicode_Check(operation)) { PyErr_SetString(NotSupportedError, "unicode queries not yet supported"); return NULL; } EXC_IF_CURS_CLOSED(self); IFCLEARPGRES(self->pgres); /* note that we don't overwrite the last query executed on the cursor, we just *return* the new query with bound variables TODO: refactor the common mogrification code (see psycopg_curs_execute for comments, the code is amost identical) */ if (vars) { if(_mogrify(vars, operation, self->conn, &cvt) == -1) return NULL; } if (vars && cvt) { if (!(fquery = PyString_Format(operation, cvt))) { PyObject *err, *arg, *trace; int pe = 0; PyErr_Fetch(&err, &arg, &trace); if (err && PyErr_GivenExceptionMatches(err, PyExc_TypeError)) { Dprintf("psyco_curs_execute: TypeError exception catched"); PyErr_NormalizeException(&err, &arg, &trace); if (PyObject_HasAttrString(arg, "args")) { PyObject *args = PyObject_GetAttrString(arg, "args"); PyObject *str = PySequence_GetItem(args, 0); char *s = PyString_AS_STRING(str); Dprintf("psyco_curs_execute: -> %s", s); if (!strcmp(s, "not enough arguments for format string") || !strcmp(s, "not all arguments converted")) { Dprintf("psyco_curs_execute: -> got a match"); psyco_set_error(ProgrammingError, (PyObject*)self, s, NULL, NULL); pe = 1; } Py_DECREF(args); Py_DECREF(str); } } /* if we did not manage our own exception, restore old one */ if (pe == 1) { Py_XDECREF(err); Py_XDECREF(arg); Py_XDECREF(trace); } else { PyErr_Restore(err, arg, trace); } return NULL; } Dprintf("psyco_curs_execute: cvt->refcnt = %d, fquery->refcnt = %d", cvt->ob_refcnt, fquery->ob_refcnt); Py_DECREF(cvt); } else { fquery = operation; Py_INCREF(operation); } return fquery; } #endif /* fetchone method - fetch one row of results */ #define psyco_curs_fetchone_doc \ "fetchone() -> tuple or None\n\n" \ "Return the next row of a query result set in the form of a tuple (by\n" \ "default) or using the sequence factory previously set in the\n" \ "`row_factory` attribute. Return `None` when no more data is available.\n" static int _psyco_curs_prefetch(cursorObject *self) { int i = 0; /* check if the fetching cursor is the one that did the asynchronous query and raise an exception if not */ pthread_mutex_lock(&(self->conn->lock)); if (self->conn->async_cursor != NULL && self->conn->async_cursor != (PyObject*)self) { pthread_mutex_unlock(&(self->conn->lock)); psyco_set_error(ProgrammingError, (PyObject*)self, "asynchronous fetch by wrong cursor", NULL, NULL); return -2; } pthread_mutex_unlock(&(self->conn->lock)); if (self->pgres == NULL || self->needsfetch) { self->needsfetch = 0; Dprintf("_psyco_curs_prefetch: trying to fetch data"); do { i = pq_fetch(self); Dprintf("_psycopg_curs_prefetch: result = %d", i); } while(i == 1); } Dprintf("_psyco_curs_prefetch: result = %d", i); return i; } static PyObject * _psyco_curs_buildrow_fill(cursorObject *self, PyObject *res, int row, int n, int istuple) { int i, len; unsigned char *str; PyObject *val; for (i=0; i < n; i++) { if (PQgetisnull(self->pgres, row, i)) { str = NULL; len = 0; } else { str = (unsigned char*)PQgetvalue(self->pgres, row, i); len = PQgetlength(self->pgres, row, i); } Dprintf("_psyco_curs_buildrow: row %ld, element %d, len %i", self->row, i, len); val = typecast_cast(PyTuple_GET_ITEM(self->casts, i), (char*)str, len, (PyObject*)self); if (val) { Dprintf("_psyco_curs_buildrow: val->refcnt = %d", val->ob_refcnt); if (istuple) { PyTuple_SET_ITEM(res, i, val); } else { PySequence_SetItem(res, i, val); Py_DECREF(val); } } else { /* an error occurred in the type system, we return NULL to raise an exception. the typecast code should already have set the exception type and text */ Py_DECREF(res); res = NULL; break; } } return res; } static PyObject * _psyco_curs_buildrow(cursorObject *self, int row) { int n; n = PQnfields(self->pgres); return _psyco_curs_buildrow_fill(self, PyTuple_New(n), row, n, 1); } static PyObject * _psyco_curs_buildrow_with_factory(cursorObject *self, int row) { int n; PyObject *res; n = PQnfields(self->pgres); if ((res = PyObject_CallFunction(self->tuple_factory, "O", self))== NULL) return NULL; return _psyco_curs_buildrow_fill(self, res, row, n, 0); } PyObject * psyco_curs_fetchone(cursorObject *self, PyObject *args) { PyObject *res; if (args && !PyArg_ParseTuple(args, "")) return NULL; EXC_IF_CURS_CLOSED(self) if (_psyco_curs_prefetch(self) < 0) return NULL; EXC_IF_NO_TUPLES(self); if (self->name != NULL) { char buffer[128]; EXC_IF_NO_MARK(self); PyOS_snprintf(buffer, 127, "FETCH FORWARD 1 FROM %s", self->name); if (pq_execute(self, buffer, 0) == -1) return NULL; if (_psyco_curs_prefetch(self) < 0) return NULL; } Dprintf("psyco_curs_fetchone: fetching row %ld", self->row); Dprintf("psyco_curs_fetchone: rowcount = %ld", self->rowcount); if (self->row >= self->rowcount) { /* we exausted available data: return None */ Py_INCREF(Py_None); return Py_None; } if (self->tuple_factory == Py_None) res = _psyco_curs_buildrow(self, self->row); else res = _psyco_curs_buildrow_with_factory(self, self->row); self->row++; /* move the counter to next line */ /* if the query was async aggresively free pgres, to allow successive requests to reallocate it */ if (self->row >= self->rowcount && self->conn->async_cursor == (PyObject*)self) IFCLEARPGRES(self->pgres); return res; } /* fetch many - fetch some results */ #define psyco_curs_fetchmany_doc \ "fetchmany(size=self.arraysize) -> list of tuple\n\n" \ "Return the next `size` rows of a query result set in the form of a list\n" \ "of tuples (by default) or using the sequence factory previously set in\n" \ "the `row_factory` attribute. Return `None` when no more data is available.\n" PyObject * psyco_curs_fetchmany(cursorObject *self, PyObject *args, PyObject *kwords) { int i; PyObject *list, *res; long int size = self->arraysize; static char *kwlist[] = {"size", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwords, "|l", kwlist, &size)) { return NULL; } EXC_IF_CURS_CLOSED(self); if (_psyco_curs_prefetch(self) < 0) return NULL; EXC_IF_NO_TUPLES(self); if (self->name != NULL) { char buffer[128]; EXC_IF_NO_MARK(self); PyOS_snprintf(buffer, 127, "FETCH FORWARD %d FROM %s", (int)size, self->name); if (pq_execute(self, buffer, 0) == -1) return NULL; if (_psyco_curs_prefetch(self) < 0) return NULL; } /* make sure size is not > than the available number of rows */ if (size > self->rowcount - self->row || size < 0) { size = self->rowcount - self->row; } Dprintf("psyco_curs_fetchmany: size = %ld", size); if (size <= 0) { return PyList_New(0); } list = PyList_New(size); for (i = 0; i < size; i++) { if (self->tuple_factory == Py_None) res = _psyco_curs_buildrow(self, self->row); else res = _psyco_curs_buildrow_with_factory(self, self->row); self->row++; if (res == NULL) { Py_DECREF(list); return NULL; } PyList_SET_ITEM(list, i, res); } /* if the query was async aggresively free pgres, to allow successive requests to reallocate it */ if (self->row >= self->rowcount && self->conn->async_cursor == (PyObject*)self) IFCLEARPGRES(self->pgres); return list; } /* fetch all - fetch all results */ #define psyco_curs_fetchall_doc \ "fetchall() -> list of tuple\n\n" \ "Return all the remaining rows of a query result set.\n\n" \ "Rows are returned in the form of a list of tuples (by default) or using\n" \ "the sequence factory previously set in the `row_factory` attribute.\n" \ "Return `None` when no more data is available.\n" PyObject * psyco_curs_fetchall(cursorObject *self, PyObject *args) { int i, size; PyObject *list, *res; if (!PyArg_ParseTuple(args, "")) { return NULL; } EXC_IF_CURS_CLOSED(self); if (_psyco_curs_prefetch(self) < 0) return NULL; EXC_IF_NO_TUPLES(self); if (self->name != NULL) { char buffer[128]; EXC_IF_NO_MARK(self); PyOS_snprintf(buffer, 127, "FETCH FORWARD ALL FROM %s", self->name); if (pq_execute(self, buffer, 0) == -1) return NULL; if (_psyco_curs_prefetch(self) < 0) return NULL; } size = self->rowcount - self->row; if (size <= 0) { return PyList_New(0); } list = PyList_New(size); for (i = 0; i < size; i++) { if (self->tuple_factory == Py_None) res = _psyco_curs_buildrow(self, self->row); else res = _psyco_curs_buildrow_with_factory(self, self->row); self->row++; if (res == NULL) { Py_DECREF(list); return NULL; } PyList_SET_ITEM(list, i, res); } /* if the query was async aggresively free pgres, to allow successive requests to reallocate it */ if (self->row >= self->rowcount && self->conn->async_cursor == (PyObject*)self) IFCLEARPGRES(self->pgres); return list; } /* callproc method - execute a stored procedure */ #define psyco_curs_callproc_doc \ "callproc(procname, parameters=None, async=0) -- Execute stored procedure." static PyObject * psyco_curs_callproc(cursorObject *self, PyObject *args, PyObject *kwargs) { char *procname = NULL, *sql = NULL; long int async = 0; int i, nparameters = 0, sl = 0; PyObject *parameters = NULL; PyObject *operation = NULL; PyObject *res = NULL; if (!PyArg_ParseTuple(args, "s|Oi", &procname, ¶meters, &async)) { return NULL; } EXC_IF_CURS_CLOSED(self); if (self->name != NULL) { psyco_set_error(ProgrammingError, (PyObject*)self, "can't call .callproc() on named cursors", NULL, NULL); return NULL; } if(parameters && parameters != Py_None) { nparameters = PyObject_Length(parameters); if (nparameters < 0) nparameters = 0; } /* allocate some memory, build the SQL and create a PyString from it */ sl = strlen(procname) + 10 + nparameters*3 - (nparameters ? 1 : 0); sql = (char*)PyMem_Malloc(sl); if (sql == NULL) return NULL; sprintf(sql, "SELECT %s(", procname); for(i=0; iname == NULL) { if (strcmp(mode, "relative") == 0) { newpos = self->row + value; } else if (strcmp( mode, "absolute") == 0) { newpos = value; } else { psyco_set_error(ProgrammingError, (PyObject*)self, "scroll mode must be 'relative' or 'absolute'", NULL, NULL); return NULL; } if (newpos < 0 || newpos >= self->rowcount ) { psyco_set_error(ProgrammingError, (PyObject*)self, "scroll destination out of bounds", NULL, NULL); return NULL; } self->row = newpos; } else { char buffer[128]; EXC_IF_NO_MARK(self); if (strcmp(mode, "absolute") == 0) { PyOS_snprintf(buffer, 127, "MOVE ABSOLUTE %d FROM %s", value, self->name); } else { PyOS_snprintf(buffer, 127, "MOVE %d FROM %s", value, self->name); } if (pq_execute(self, buffer, 0) == -1) return NULL; if (_psyco_curs_prefetch(self) < 0) return NULL; } Py_INCREF(Py_None); return Py_None; } #ifdef PSYCOPG_EXTENSIONS /* extension: copy_from - implements COPY FROM */ #define psyco_curs_copy_from_doc \ "copy_from(file, table, sep='\\t', null='\\N', columns=None) -- Copy table from file." static int _psyco_curs_has_read_check(PyObject* o, void* var) { if (PyObject_HasAttrString(o, "readline") && PyObject_HasAttrString(o, "read")) { Py_INCREF(o); *((PyObject**)var) = o; return 1; } else { PyErr_SetString(PyExc_TypeError, "argument 1 must have both .read() and .readline() methods"); return 0; } } static PyObject * psyco_curs_copy_from(cursorObject *self, PyObject *args, PyObject *kwargs) { char query[1024]; char *table_name; char *sep = "\t", *null = NULL; long int bufsize = DEFAULT_COPYSIZE; PyObject *file, *columns = NULL, *res = NULL; char columnlist[1024] = ""; static char *kwlist[] = {"file", "table", "sep", "null", "size", "columns", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&s|ssiO", kwlist, _psyco_curs_has_read_check, &file, &table_name, &sep, &null, &bufsize, &columns)) { return NULL; } if (columns != NULL && columns != Py_None) { PyObject* collistiter = PyObject_GetIter(columns); PyObject* col; int collistlen = 2; int colitemlen; char* colname; if (collistiter == NULL) { return NULL; } strcpy(columnlist, " ("); while ((col = PyIter_Next(collistiter)) != NULL) { if (!PyString_Check(col)) { Py_DECREF(col); Py_DECREF(collistiter); PyErr_SetString(PyExc_ValueError, "Elements in column list must be strings"); return NULL; } PyString_AsStringAndSize(col, &colname, &colitemlen); if (collistlen + colitemlen > 1022) { Py_DECREF(col); Py_DECREF(collistiter); PyErr_SetString(PyExc_ValueError, "Column list too long"); return NULL; } strncpy(&columnlist[collistlen], colname, colitemlen); collistlen += colitemlen; columnlist[collistlen++] = ','; Py_DECREF(col); } Py_DECREF(collistiter); if (collistlen == 2) { /* empty list; we printed no comma */ collistlen++; } columnlist[collistlen - 1] = ')'; columnlist[collistlen] = '\0'; } if (PyErr_Occurred()) { return NULL; } EXC_IF_CURS_CLOSED(self); if (null) { PyOS_snprintf(query, 1023, "COPY %s%s FROM stdin USING DELIMITERS '%s'" " WITH NULL AS '%s'", table_name, columnlist, sep, null); } else { PyOS_snprintf(query, 1023, "COPY %s%s FROM stdin USING DELIMITERS '%s'", table_name, columnlist, sep); } Dprintf("psyco_curs_copy_from: query = %s", query); self->copysize = bufsize; self->copyfile = file; if (pq_execute(self, query, 0) == 1) { res = Py_None; Py_INCREF(Py_None); } self->copyfile =NULL; return res; } #define psyco_curs_copy_to_doc \ "copy_to(file, table, sep='\\t', null='\\N') -- Copy table to file." static int _psyco_curs_has_write_check(PyObject* o, void* var) { if (PyObject_HasAttrString(o, "write")) { Py_INCREF(o); *((PyObject**)var) = o; return 1; } else { PyErr_SetString(PyExc_TypeError, "argument 1 must have a .write() method"); return 0; } } static PyObject * psyco_curs_copy_to(cursorObject *self, PyObject *args, PyObject *kwargs) { char query[256]; char *table_name; char *sep = "\t", *null = NULL; PyObject *file, *res = NULL; static char *kwlist[] = {"file", "table", "sep", "null", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&s|ss", kwlist, _psyco_curs_has_write_check, &file, &table_name, &sep, &null)) { return NULL; } EXC_IF_CURS_CLOSED(self); if (null) { PyOS_snprintf(query, 255, "COPY %s TO stdout USING DELIMITERS '%s'" " WITH NULL AS '%s'", table_name, sep, null); } else { PyOS_snprintf(query, 255, "COPY %s TO stdout USING DELIMITERS '%s'", table_name, sep); } self->copysize = 0; self->copyfile = file; if (pq_execute(self, query, 0) == 1) { res = Py_None; Py_INCREF(Py_None); } self->copyfile = NULL; return res; } /* extension: fileno - return the file descripor of the connection */ #define psyco_curs_fileno_doc \ "fileno() -> int -- Return file descriptor associated to database connection." static PyObject * psyco_curs_fileno(cursorObject *self, PyObject *args) { long int socket; if (!PyArg_ParseTuple(args, "")) return NULL; EXC_IF_CURS_CLOSED(self); /* note how we call PQflush() to make sure the user will use select() in the safe way! */ pthread_mutex_lock(&(self->conn->lock)); Py_BEGIN_ALLOW_THREADS; PQflush(self->conn->pgconn); socket = (long int)PQsocket(self->conn->pgconn); Py_END_ALLOW_THREADS; pthread_mutex_unlock(&(self->conn->lock)); return PyInt_FromLong(socket); } /* extension: isready - return true if data from async execute is ready */ #define psyco_curs_isready_doc \ "isready() -> bool -- Return True if data is ready after an async query." static PyObject * psyco_curs_isready(cursorObject *self, PyObject *args) { if (!PyArg_ParseTuple(args, "")) return NULL; EXC_IF_CURS_CLOSED(self); /* pq_is_busy does its own locking, we don't need anything special but if the cursor is ready we need to fetch the result and free the connection for the next query. */ if (pq_is_busy(self->conn)) { Py_INCREF(Py_False); return Py_False; } else { IFCLEARPGRES(self->pgres); pthread_mutex_lock(&(self->conn->lock)); self->pgres = PQgetResult(self->conn->pgconn); self->conn->async_cursor = NULL; pthread_mutex_unlock(&(self->conn->lock)); self->needsfetch = 1; Py_INCREF(Py_True); return Py_True; } } #endif /** the cursor object **/ /* iterator protocol */ static PyObject * cursor_iter(PyObject *self) { EXC_IF_CURS_CLOSED((cursorObject*)self); Py_INCREF(self); return self; } static PyObject * cursor_next(PyObject *self) { PyObject *res; /* we don't parse arguments: psyco_curs_fetchone will do that for us */ res = psyco_curs_fetchone((cursorObject*)self, NULL); /* convert a None to NULL to signal the end of iteration */ if (res && res == Py_None) { Py_DECREF(res); res = NULL; } return res; } /* object method list */ static struct PyMethodDef cursorObject_methods[] = { /* DBAPI-2.0 core */ {"close", (PyCFunction)psyco_curs_close, METH_VARARGS, psyco_curs_close_doc}, {"execute", (PyCFunction)psyco_curs_execute, METH_VARARGS|METH_KEYWORDS, psyco_curs_execute_doc}, {"executemany", (PyCFunction)psyco_curs_executemany, METH_VARARGS|METH_KEYWORDS, psyco_curs_executemany_doc}, {"fetchone", (PyCFunction)psyco_curs_fetchone, METH_VARARGS, psyco_curs_fetchone_doc}, {"fetchmany", (PyCFunction)psyco_curs_fetchmany, METH_VARARGS|METH_KEYWORDS, psyco_curs_fetchmany_doc}, {"fetchall", (PyCFunction)psyco_curs_fetchall, METH_VARARGS, psyco_curs_fetchall_doc}, {"callproc", (PyCFunction)psyco_curs_callproc, METH_VARARGS, psyco_curs_callproc_doc}, {"nextset", (PyCFunction)psyco_curs_nextset, METH_VARARGS, psyco_curs_nextset_doc}, {"setinputsizes", (PyCFunction)psyco_curs_setinputsizes, METH_VARARGS, psyco_curs_setinputsizes_doc}, {"setoutputsize", (PyCFunction)psyco_curs_setoutputsize, METH_VARARGS, psyco_curs_setoutputsize_doc}, /* DBAPI-2.0 extensions */ {"scroll", (PyCFunction)psyco_curs_scroll, METH_VARARGS|METH_KEYWORDS, psyco_curs_scroll_doc}, /* psycopg extensions */ #ifdef PSYCOPG_EXTENSIONS {"mogrify", (PyCFunction)psyco_curs_mogrify, METH_VARARGS|METH_KEYWORDS, psyco_curs_mogrify_doc}, {"fileno", (PyCFunction)psyco_curs_fileno, METH_VARARGS, psyco_curs_fileno_doc}, {"isready", (PyCFunction)psyco_curs_isready, METH_VARARGS, psyco_curs_isready_doc}, {"copy_from", (PyCFunction)psyco_curs_copy_from, METH_VARARGS|METH_KEYWORDS, psyco_curs_copy_from_doc}, {"copy_to", (PyCFunction)psyco_curs_copy_to, METH_VARARGS|METH_KEYWORDS, psyco_curs_copy_to_doc}, #endif {NULL} }; /* object member list */ #define OFFSETOF(x) offsetof(cursorObject, x) static struct PyMemberDef cursorObject_members[] = { /* DBAPI-2.0 basics */ {"rowcount", T_LONG, OFFSETOF(rowcount), RO, "Number of rows read from the backend in the last command."}, {"arraysize", T_LONG, OFFSETOF(arraysize), 0, "Number of records `fetchmany()` must fetch if not explicitely " \ "specified."}, {"description", T_OBJECT, OFFSETOF(description), RO, "Cursor description as defined in DBAPI-2.0."}, {"lastrowid", T_LONG, OFFSETOF(lastoid), RO, "The ``oid`` of the last row inserted by the cursor."}, /* DBAPI-2.0 extensions */ {"rownumber", T_LONG, OFFSETOF(row), RO, "The current row position."}, {"connection", T_OBJECT, OFFSETOF(conn), RO, "The connection where the cursor comes from."}, #ifdef PSYCOPG_EXTENSIONS {"name", T_STRING, OFFSETOF(name), RO}, {"statusmessage", T_OBJECT, OFFSETOF(pgstatus), RO, "The return message of the last command."}, {"query", T_OBJECT, OFFSETOF(query), RO, "The last query text sent to the backend."}, {"row_factory", T_OBJECT, OFFSETOF(tuple_factory), 0}, {"tzinfo_factory", T_OBJECT, OFFSETOF(tzinfo_factory), 0}, {"typecaster", T_OBJECT, OFFSETOF(caster), RO}, {"string_types", T_OBJECT, OFFSETOF(string_types), 0}, {"binary_types", T_OBJECT, OFFSETOF(binary_types), 0}, #endif {NULL} }; /* initialization and finalization methods */ static int cursor_setup(cursorObject *self, connectionObject *conn, char *name) { Dprintf("cursor_setup: init cursor object at %p", self); Dprintf("cursor_setup: parameters: name = %s, conn = %p", name, conn); if (name) { self->name = PyMem_Malloc(strlen(name)+1); if (self->name == NULL) return 1; strncpy(self->name, name, strlen(name)+1); } /* FIXME: why does this raise an excpetion on the _next_ line of code? if (PyObject_IsInstance((PyObject*)conn, (PyObject *)&connectionType) == 0) { PyErr_SetString(PyExc_TypeError, "argument 1 must be subclass of psycopg2._psycopg.connection"); return 1; } */ self->conn = conn; Py_INCREF((PyObject*)self->conn); self->closed = 0; self->mark = conn->mark; self->pgres = NULL; self->notuples = 1; self->arraysize = 1; self->rowcount = -1; self->lastoid = InvalidOid; self->casts = NULL; self->notice = NULL; self->string_types = NULL; self->binary_types = NULL; self->description = Py_None; Py_INCREF(Py_None); self->pgstatus = Py_None; Py_INCREF(Py_None); self->tuple_factory = Py_None; Py_INCREF(Py_None); self->query = Py_None; Py_INCREF(Py_None); /* default tzinfo factory */ self->tzinfo_factory = pyPsycopgTzFixedOffsetTimezone; Py_INCREF(self->tzinfo_factory); Dprintf("cursor_setup: good cursor object at %p, refcnt = %d", self, ((PyObject *)self)->ob_refcnt); return 0; } static void cursor_dealloc(PyObject* obj) { cursorObject *self = (cursorObject *)obj; if (self->name) PyMem_Free(self->name); Py_XDECREF((PyObject*)self->conn); Py_XDECREF(self->casts); Py_XDECREF(self->description); Py_XDECREF(self->pgstatus); Py_XDECREF(self->tuple_factory); Py_XDECREF(self->tzinfo_factory); Py_XDECREF(self->query); IFCLEARPGRES(self->pgres); Dprintf("cursor_dealloc: deleted cursor object at %p, refcnt = %d", obj, obj->ob_refcnt); obj->ob_type->tp_free(obj); } static int cursor_init(PyObject *obj, PyObject *args, PyObject *kwds) { char *name = NULL; PyObject *conn; if (!PyArg_ParseTuple(args, "O|s", &conn, &name)) return -1; return cursor_setup((cursorObject *)obj, (connectionObject *)conn, name); } static PyObject * cursor_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { return type->tp_alloc(type, 0); } static void cursor_del(PyObject* self) { PyObject_Del(self); } static PyObject * cursor_repr(cursorObject *self) { return PyString_FromFormat( "", self, self->closed); } /* object type */ #define cursorType_doc \ "A database cursor." PyTypeObject cursorType = { PyObject_HEAD_INIT(NULL) 0, "psycopg2._psycopg.cursor", sizeof(cursorObject), 0, cursor_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ (reprfunc)cursor_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ (reprfunc)cursor_repr, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_ITER, /*tp_flags*/ cursorType_doc, /*tp_doc*/ 0, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ 0, /*tp_weaklistoffset*/ cursor_iter, /*tp_iter*/ cursor_next, /*tp_iternext*/ /* Attribute descriptor and subclassing stuff */ cursorObject_methods, /*tp_methods*/ cursorObject_members, /*tp_members*/ 0, /*tp_getset*/ 0, /*tp_base*/ 0, /*tp_dict*/ 0, /*tp_descr_get*/ 0, /*tp_descr_set*/ 0, /*tp_dictoffset*/ cursor_init, /*tp_init*/ 0, /*tp_alloc Will be set to PyType_GenericAlloc in module init*/ cursor_new, /*tp_new*/ (freefunc)cursor_del, /*tp_free Low-level free-memory routine */ 0, /*tp_is_gc For PyObject_IS_GC */ 0, /*tp_bases*/ 0, /*tp_mro method resolution order */ 0, /*tp_cache*/ 0, /*tp_subclasses*/ 0 /*tp_weaklist*/ };