diff options
| -rw-r--r-- | ChangeLog | 13 | ||||
| -rw-r--r-- | examples/lobject.py | 79 | ||||
| -rw-r--r-- | lib/extensions.py | 3 | ||||
| -rw-r--r-- | psycopg/connection_int.c | 3 | ||||
| -rw-r--r-- | psycopg/connection_type.c | 88 | ||||
| -rw-r--r-- | psycopg/lobject.h | 89 | ||||
| -rw-r--r-- | psycopg/lobject_int.c | 244 | ||||
| -rw-r--r-- | psycopg/lobject_type.c | 389 | ||||
| -rw-r--r-- | psycopg/pqpath.c | 4 | ||||
| -rw-r--r-- | psycopg/pqpath.h | 2 | ||||
| -rw-r--r-- | psycopg/psycopgmodule.c | 17 | ||||
| -rw-r--r-- | setup.py | 1 |
12 files changed, 923 insertions, 9 deletions
@@ -1,3 +1,16 @@ +2006-09-01 Federico Di Gregorio <fog@initd.org> + + * psycopg/connection_type.c: merged in double mutex destroy patch + from Joerg Sonnenberger. + + * Implemented large objects support. + + * psycopg/connection_int.c: removed increment of self->mark, + now it is done directly in pqpath.c to make sure even the + large object support gets it. + + * Starting 2.1 development. + 2006-09-30 Federico Di Gregorio <fog@initd.org> * ZpsycopgDA/DA.py: applied the infinity patch from 1.1 (fixes #122). diff --git a/examples/lobject.py b/examples/lobject.py new file mode 100644 index 0000000..182c951 --- /dev/null +++ b/examples/lobject.py @@ -0,0 +1,79 @@ +# lobject.py - lobject example +# +# Copyright (C) 2001-2006 Federico Di Gregorio <fog@debian.org> +# +# 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 MERCHANTIBILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. + +## put in DSN your DSN string + +DSN = 'dbname=test' + +## don't modify anything below this line (except for experimenting) + +import sys +import psycopg2 + +if len(sys.argv) > 1: + DSN = sys.argv[1] + +print "Opening connection using dns:", DSN +conn = psycopg2.connect(DSN) +print "Encoding for this connection is", conn.encoding + +# this will create a large object with a new random oid, we'll +# use it to make some basic tests about read/write and seek. +lobj = conn.lobject() +loid = lobj.oid +print "Created a new large object with oid", loid + +print "Manually importing some binary data into the object:" +data = open("somehackers.jpg").read() +len = lobj.write(data) +print " imported", len, "bytes of data" + +conn.commit() + +print "Trying to (re)open large object with oid", loid +lobj = conn.lobject(loid) +print "Manually exporting the data from the lobject:" +data1 = lobj.read() +len = lobj.tell() +lobj.seek(0, 0) +data2 = lobj.read() +if data1 != data2: + print "ERROR: read after seek returned different data" +open("somehackers_lobject1.jpg", 'wb').write(data1) +print " written", len, "bytes of data to somehackers_lobject1.jpg" + +lobj.unlink() +print "Large object with oid", loid, "removed" + +conn.commit() + +# now we try to use the import and export functions to do the same +lobj = conn.lobject(0, 'n', 0, "somehackers.jpg") +loid = lobj.oid +print "Imported a new large object with oid", loid + +conn.commit() + +print "Trying to (re)open large object with oid", loid +lobj = conn.lobject(loid, 'n') +print "Using export() to export the data from the large object:" +lobj.export("somehackers_lobject2.jpg") +print " exported large object to somehackers_lobject2.jpg" + +lobj.unlink() +print "Large object with oid", loid, "removed" + +conn.commit() + +print "\nNow try to load the new images, to check it worked!" diff --git a/lib/extensions.py b/lib/extensions.py index 9233d1d..5fedbd2 100644 --- a/lib/extensions.py +++ b/lib/extensions.py @@ -4,6 +4,7 @@ This module holds all the extensions to the DBAPI-2.0 provided by psycopg. - `connection` -- the new-type inheritable connection class - `cursor` -- the new-type inheritable cursor class +- `lobject` -- the new-type inheritable large object class - `adapt()` -- exposes the PEP-246_ compatible adapting mechanism used by psycopg to adapt Python types to PostgreSQL ones @@ -38,7 +39,7 @@ try: except: pass -from _psycopg import adapt, adapters, encodings, connection, cursor +from _psycopg import adapt, adapters, encodings, connection, cursor, lobject from _psycopg import string_types, binary_types, new_type, register_type from _psycopg import ISQLQuote diff --git a/psycopg/connection_int.c b/psycopg/connection_int.c index 3e61ddf..16ce5ab 100644 --- a/psycopg/connection_int.c +++ b/psycopg/connection_int.c @@ -202,7 +202,6 @@ conn_commit(connectionObject *self) pthread_mutex_lock(&self->lock); res = pq_commit(self); - self->mark++; pthread_mutex_unlock(&self->lock); Py_END_ALLOW_THREADS; @@ -221,7 +220,6 @@ conn_rollback(connectionObject *self) pthread_mutex_lock(&self->lock); res = pq_abort(self); - self->mark++; pthread_mutex_unlock(&self->lock); Py_END_ALLOW_THREADS; @@ -245,7 +243,6 @@ conn_switch_isolation_level(connectionObject *self, int level) res = pq_abort(self); } self->isolation_level = level; - self->mark++; Dprintf("conn_switch_isolation_level: switched to level %d", level); diff --git a/psycopg/connection_type.c b/psycopg/connection_type.c index 990cc6a..191582a 100644 --- a/psycopg/connection_type.c +++ b/psycopg/connection_type.c @@ -31,6 +31,7 @@ #include "psycopg/psycopg.h" #include "psycopg/connection.h" #include "psycopg/cursor.h" +#include "psycopg/lobject.h" /** DBAPI methods **/ @@ -169,9 +170,8 @@ psyco_conn_set_isolation_level(connectionObject *self, PyObject *args) return Py_None; } - -/* set_isolation_level method - switch connection isolation level */ +/* set_client_encoding method - set client encoding */ #define psyco_conn_set_client_encoding_doc \ "set_client_encoding(encoding) -- Set client encoding to ``encoding``." @@ -193,6 +193,86 @@ psyco_conn_set_client_encoding(connectionObject *self, PyObject *args) return NULL; } } + + +/* lobject method - allocate a new lobject */ + +#define psyco_conn_lobject_doc \ +"cursor(oid=0, mode=0, new_oid=0, new_file=None,\n" \ +" lobject_factory=extensions.lobject) -- new lobject\n\n" \ +"Return a new lobject.\n\nThe ``lobject_factory`` argument can be used\n" \ +"to create non-standard lobjects by passing a class different from the\n" \ +"default. Note that the new class *should* be a sub-class of\n" \ +"`extensions.lobject`.\n\n" \ +":rtype: `extensions.lobject`" + +static PyObject * +psyco_conn_lobject(connectionObject *self, PyObject *args, PyObject *keywds) +{ + Oid oid=InvalidOid, new_oid=InvalidOid; + char *smode = NULL, *new_file = NULL; + int mode=0; + PyObject *obj, *factory = NULL; + + static char *kwlist[] = {"oid", "mode", "new_oid", "new_file", + "cursor_factory", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, keywds, "|izizO", kwlist, + &oid, &smode, &new_oid, &new_file, + &factory)) { + return NULL; + } + + EXC_IF_CONN_CLOSED(self); + + Dprintf("psyco_conn_lobject: new lobject for connection at %p", self); + Dprintf("psyco_conn_lobject: parameters: oid = %d, mode = %s", + oid, smode); + Dprintf("psyco_conn_lobject: parameters: new_oid = %d, new_file = %s", + new_oid, new_file); + + /* build a mode number out of the mode string: right now we only accept + 'r', 'w' and 'rw' (but note that 'w' implies 'rw' because PostgreSQL + backend does that. */ + if (smode) { + if (strncmp("rw", smode, 2) == 0) + mode = INV_READ+INV_WRITE; + else if (smode[0] == 'r') + mode = INV_READ; + else if (smode[0] == 'w') + mode = INV_WRITE; + else if (smode[0] == 'n') + mode = -1; + else { + PyErr_SetString(PyExc_TypeError, + "mode should be one of 'r', 'w' or 'rw'"); + return NULL; + } + } + + if (factory == NULL) factory = (PyObject *)&lobjectType; + if (new_file) + obj = PyObject_CallFunction(factory, "Oiiis", + self, oid, mode, new_oid, new_file); + else + obj = PyObject_CallFunction(factory, "Oiii", + self, oid, mode, new_oid); + + if (obj == NULL) return NULL; + if (PyObject_IsInstance(obj, (PyObject *)&lobjectType) == 0) { + PyErr_SetString(PyExc_TypeError, + "lobject factory must be subclass of psycopg2._psycopg.lobject"); + Py_DECREF(obj); + return NULL; + } + + Dprintf("psyco_conn_lobject: new lobject at %p: refcnt = %d", + obj, obj->ob_refcnt); + return obj; +} + + + #endif @@ -214,7 +294,9 @@ static struct PyMethodDef connectionObject_methods[] = { {"set_isolation_level", (PyCFunction)psyco_conn_set_isolation_level, METH_VARARGS, psyco_conn_set_isolation_level_doc}, {"set_client_encoding", (PyCFunction)psyco_conn_set_client_encoding, - METH_VARARGS, psyco_conn_set_client_encoding_doc}, + METH_VARARGS, psyco_conn_set_client_encoding_doc}, + {"lobject", (PyCFunction)psyco_conn_lobject, + METH_VARARGS|METH_KEYWORDS, psyco_conn_lobject_doc}, #endif {NULL} }; diff --git a/psycopg/lobject.h b/psycopg/lobject.h new file mode 100644 index 0000000..4b84021 --- /dev/null +++ b/psycopg/lobject.h @@ -0,0 +1,89 @@ +/* lobject.h - definition for the psycopg lobject type + * + * Copyright (C) 2006 Federico Di Gregorio <fog@debian.org> + * + * 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. + */ + +#ifndef PSYCOPG_LOBJECT_H +#define PSYCOPG_LOBJECT_H 1 + +#include <Python.h> +#include <libpq-fe.h> +#include <libpq/libpq-fs.h> + +#include "psycopg/connection.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern PyTypeObject lobjectType; + +typedef struct { + PyObject HEAD; + + connectionObject *conn; /* connection owning the lobject */ + + int closed; /* 1 if the lobject is closed */ + int mode; /* numeric mode, tells if lobject was opened */ + char *smode; /* string mode if lobject was opened */ + + long int mark; /* copied from conn->mark */ + + Oid oid; /* the oid for this lobject */ + int fd; /* the file descriptor for file-like ops */ +} lobjectObject; + +/* functions exported from lobject_int.c */ + +extern int lobject_open(lobjectObject *self, connectionObject *conn, + Oid oid, int mode, Oid new_oid, char *new_file); +extern int lobject_unlink(lobjectObject *self); +extern int lobject_export(lobjectObject *self, char *filename); + +extern size_t lobject_read(lobjectObject *self, char *buf, size_t len); +extern size_t lobject_write(lobjectObject *self, char *buf, size_t len); +extern int lobject_seek(lobjectObject *self, int pos, int whence); +extern int lobject_tell(lobjectObject *self); +extern void lobject_close(lobjectObject *self); + +/* exception-raising macros */ + +#define EXC_IF_LOBJ_CLOSED(self) \ +if ((self)->closed || ((self)->conn && (self)->conn->closed)) { \ + PyErr_SetString(InterfaceError, "lobject already closed"); \ + return NULL; } + +#define EXC_IF_LOBJ_LEVEL0(self) \ +if (self->conn->isolation_level == 0) { \ + psyco_set_error(ProgrammingError, (PyObject*)self, \ + "can't use a lobject outside of transactions", NULL, NULL); \ + return NULL; \ +} +#define EXC_IF_LOBJ_UNMARKED(self) \ +if (self->conn->mark != self->mark) { \ + psyco_set_error(ProgrammingError, (PyObject*)self, \ + "lobject isn't valid anymore", NULL, NULL); \ + return NULL; \ +} + +#ifdef __cplusplus +} +#endif + +#endif /* !defined(PSYCOPG_LOBJECT_H) */ diff --git a/psycopg/lobject_int.c b/psycopg/lobject_int.c new file mode 100644 index 0000000..d4cc38f --- /dev/null +++ b/psycopg/lobject_int.c @@ -0,0 +1,244 @@ +/* lobject_int.c - code used by the lobject object + * + * Copyright (C) 2006 Federico Di Gregorio <fog@debian.org> + * + * 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 <Python.h> +#include <string.h> + +#define PSYCOPG_MODULE +#include "psycopg/config.h" +#include "psycopg/psycopg.h" +#include "psycopg/connection.h" +#include "psycopg/lobject.h" +#include "psycopg/pqpath.h" + +#ifdef PSYCOPG_EXTENSIONS + +/* lobject_open - create a new/open an existing lo */ + +int +lobject_open(lobjectObject *self, connectionObject *conn, + Oid oid, int mode, Oid new_oid, char *new_file) +{ + Py_BEGIN_ALLOW_THREADS; + pthread_mutex_lock(&(self->conn->lock)); + + pq_begin(self->conn); + + /* if the oid is InvalidOid we create a new lob before opening it + or we import a file from the FS, depending on the value of + new_name */ + if (oid == InvalidOid) { + if (new_file) + self->oid = lo_import(self->conn->pgconn, new_file); + else + self->oid = lo_create(self->conn->pgconn, new_oid); + + Dprintf("lobject_open: large object created with oid = %d", + self->oid); + + if (self->oid == InvalidOid) goto end; + + mode = INV_WRITE; + } + else { + self->oid = oid; + if (mode == 0) mode = INV_READ; + } + + /* if the oid is a real one we try to open with the given mode, + unless the mode is -1, meaning "don't open!" */ + if (mode != -1) { + self->fd = lo_open(self->conn->pgconn, self->oid, mode); + Dprintf("lobject_open: large object opened with fd = %d", + self->fd); + } + else { + /* this is necessary to make sure no function that needs and + fd is called on unopened lobjects */ + self->closed = 1; + } + + end: + pthread_mutex_unlock(&(self->conn->lock)); + Py_END_ALLOW_THREADS; + + /* here we check for errors before returning 0 */ + if ((self->fd == -1 && mode != -1) || self->oid == InvalidOid) { + pq_raise(conn, NULL, NULL, NULL); + return -1; + } + else { + /* set the mode for future reference and return */ + self->mode = mode; + switch (mode) { + case -1: + self->smode = "n"; break; + case INV_READ: + self->smode = "r"; break; + case INV_WRITE: + self->smode = "w"; break; + case INV_READ+INV_WRITE: + self->smode = "rw"; break; + } + return 0; + } +} + +/* lobject_unlink - remove an lo from database */ + +int +lobject_unlink(lobjectObject *self) +{ + int res; + + /* first we make sure the lobject is closed and then we unlink */ + lobject_close(self); + + Py_BEGIN_ALLOW_THREADS; + pthread_mutex_lock(&(self->conn->lock)); + + pq_begin(self->conn); + + res = lo_unlink(self->conn->pgconn, self->oid); + + pthread_mutex_unlock(&(self->conn->lock)); + Py_END_ALLOW_THREADS; + + if (res == -1) + pq_raise(self->conn, NULL, NULL, NULL); + return res; +} + +/* lobject_close - close an existing lo */ + +void +lobject_close(lobjectObject *self) +{ + if (self->conn->isolation_level > 0 + && self->conn->mark == self->mark) { + if (self->fd != -1) + lo_close(self->conn->pgconn, self->fd); + } +} + +/* lobject_write - write bytes to a lo */ + +size_t +lobject_write(lobjectObject *self, char *buf, size_t len) +{ + size_t written; + + Py_BEGIN_ALLOW_THREADS; + pthread_mutex_lock(&(self->conn->lock)); + + written = lo_write(self->conn->pgconn, self->fd, buf, len); + + pthread_mutex_unlock(&(self->conn->lock)); + Py_END_ALLOW_THREADS; + + if (written < 0) + pq_raise(self->conn, NULL, NULL, NULL); + return written; +} + +/* lobject_read - read bytes from a lo */ + +size_t +lobject_read(lobjectObject *self, char *buf, size_t len) +{ + size_t readed; + + Py_BEGIN_ALLOW_THREADS; + pthread_mutex_lock(&(self->conn->lock)); + + readed = lo_read(self->conn->pgconn, self->fd, buf, len); + + pthread_mutex_unlock(&(self->conn->lock)); + Py_END_ALLOW_THREADS; + + if (readed < 0) + pq_raise(self->conn, NULL, NULL, NULL); + return readed; +} + +/* lobject_seek - move the current position in the lo */ + +int +lobject_seek(lobjectObject *self, int pos, int whence) +{ + int where; + + Py_BEGIN_ALLOW_THREADS; + pthread_mutex_lock(&(self->conn->lock)); + + where = lo_lseek(self->conn->pgconn, self->fd, pos, whence); + + pthread_mutex_unlock(&(self->conn->lock)); + Py_END_ALLOW_THREADS; + + if (where < 0) + pq_raise(self->conn, NULL, NULL, NULL); + return where; +} + +/* lobject_tell - tell the current position in the lo */ + +int +lobject_tell(lobjectObject *self) +{ + int where; + + Py_BEGIN_ALLOW_THREADS; + pthread_mutex_lock(&(self->conn->lock)); + + where = lo_tell(self->conn->pgconn, self->fd); + + pthread_mutex_unlock(&(self->conn->lock)); + Py_END_ALLOW_THREADS; + + if (where < 0) + pq_raise(self->conn, NULL, NULL, NULL); + return where; +} + +/* lobject_export - export to a local file */ + +int +lobject_export(lobjectObject *self, char *filename) +{ + int res; + + Py_BEGIN_ALLOW_THREADS; + pthread_mutex_lock(&(self->conn->lock)); + + res = lo_export(self->conn->pgconn, self->oid, filename); + + pthread_mutex_unlock(&(self->conn->lock)); + Py_END_ALLOW_THREADS; + + if (res < 0) + pq_raise(self->conn, NULL, NULL, NULL); + return res; +} + + +#endif + diff --git a/psycopg/lobject_type.c b/psycopg/lobject_type.c new file mode 100644 index 0000000..8d299e9 --- /dev/null +++ b/psycopg/lobject_type.c @@ -0,0 +1,389 @@ +/* lobject_type.c - python interface to lobject objects + * + * Copyright (C) 2003-2006 Federico Di Gregorio <fog@debian.org> + * + * 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 <Python.h> +#include <structmember.h> +#include <string.h> + +#define PSYCOPG_MODULE +#include "psycopg/config.h" +#include "psycopg/python.h" +#include "psycopg/psycopg.h" +#include "psycopg/lobject.h" +#include "psycopg/connection.h" +#include "psycopg/microprotocols.h" +#include "psycopg/microprotocols_proto.h" +#include "psycopg/pqpath.h" +#include "pgversion.h" + + +#ifdef PSYCOPG_EXTENSIONS + +/** public methods **/ + +/* close method - close the lobject */ + +#define psyco_lobj_close_doc \ +"close() -- Close the lobject." + +static PyObject * +psyco_lobj_close(lobjectObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) return NULL; + + /* file-like objects can be closed multiple times and remember that + closing the current transaction is equivalent to close all the + opened large objects */ + if (!self->closed + && self->conn->isolation_level > 0 + && self->conn->mark == self->mark) + { + self->closed = 1; + lobject_close(self); + + Dprintf("psyco_lobj_close: lobject at %p closed", self); + } + + Py_INCREF(Py_None); + return Py_None; +} + +/* write method - write data to the lobject */ + +#define psyco_lobj_write_doc \ +"write(str) -- Write a string to the large object." + +static PyObject * +psyco_lobj_write(lobjectObject *self, PyObject *args) +{ + int len, res=0; + char *buffer; + + if (!PyArg_ParseTuple(args, "s#", &buffer, &len)) return NULL; + + EXC_IF_LOBJ_CLOSED(self); + EXC_IF_LOBJ_LEVEL0(self); + EXC_IF_LOBJ_UNMARKED(self); + + if ((res = lobject_write(self, buffer, len)) < 0) return NULL; + + return PyInt_FromLong((long)res); +} + +/* read method - read data from the lobject */ + +#define psyco_lobj_read_doc \ +"read(size=-1) -- Read at most size bytes or to the end of the large object." + +static PyObject * +psyco_lobj_read(lobjectObject *self, PyObject *args) +{ + int where, end, size = -1; + char *buffer; + + if (!PyArg_ParseTuple(args, "|i", &size)) return NULL; + + EXC_IF_LOBJ_CLOSED(self); + EXC_IF_LOBJ_LEVEL0(self); + EXC_IF_LOBJ_UNMARKED(self); + + if (size < 0) { + if ((where = lobject_tell(self)) < 0) return NULL; + if ((end = lobject_seek(self, 0, SEEK_END)) < 0) return NULL; + if (lobject_seek(self, where, SEEK_SET) < 0) return NULL; + size = end - where; + } + + if ((buffer = PyMem_Malloc(size)) == NULL) return NULL; + if ((size = lobject_read(self, buffer, size)) < 0) { + PyMem_Free(buffer); + return NULL; + } + + return PyString_FromStringAndSize(buffer, size); +} + +/* seek method - seek in the lobject */ + +#define psyco_lobj_seek_doc \ +"seek(offset, whence=0) -- Set the lobject's current position." + +static PyObject * +psyco_lobj_seek(lobjectObject *self, PyObject *args) +{ + int offset, whence=0; + int pos=0; + + if (!PyArg_ParseTuple(args, "i|i", &offset, &whence)) + return NULL; + + EXC_IF_LOBJ_CLOSED(self); + EXC_IF_LOBJ_LEVEL0(self); + EXC_IF_LOBJ_UNMARKED(self); + + if ((pos = lobject_seek(self, pos, whence)) < 0) + return NULL; + + return PyInt_FromLong((long)pos); +} + +/* tell method - tell current position in the lobject */ + +#define psyco_lobj_tell_doc \ +"tell() -- Return the lobject's current position." + +static PyObject * +psyco_lobj_tell(lobjectObject *self, PyObject *args) +{ + int pos; + + if (!PyArg_ParseTuple(args, "")) return NULL; + + EXC_IF_LOBJ_CLOSED(self); + EXC_IF_LOBJ_LEVEL0(self); + EXC_IF_LOBJ_UNMARKED(self); + + if ((pos = lobject_tell(self)) < 0) + return NULL; + + return PyInt_FromLong((long)pos); +} + +/* unlink method - unlink (destroy) the lobject */ + +#define psyco_lobj_unlink_doc \ +"unlink() -- Close and then remove the lobject." + +static PyObject * +psyco_lobj_unlink(lobjectObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) return NULL; + + if (lobject_unlink(self) < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +/* export method - export lobject's content to given file */ + +#define psyco_lobj_export_doc \ +"export(filename) -- Export large object to given file." + +static PyObject * +psyco_lobj_export(lobjectObject *self, PyObject *args) +{ + char *filename; + + if (!PyArg_ParseTuple(args, "s", &filename)) + return NULL; + + if (lobject_export(self, filename) < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + + +/** the lobject object **/ + +/* object method list */ + +static struct PyMethodDef lobjectObject_methods[] = { + {"read", (PyCFunction)psyco_lobj_read, + METH_VARARGS, psyco_lobj_read_doc}, + {"write", (PyCFunction)psyco_lobj_write, + METH_VARARGS, psyco_lobj_write_doc}, + {"seek", (PyCFunction)psyco_lobj_seek, + METH_VARARGS, psyco_lobj_seek_doc}, + {"tell", (PyCFunction)psyco_lobj_tell, + METH_VARARGS, psyco_lobj_tell_doc}, + {"close", (PyCFunction)psyco_lobj_close, + METH_VARARGS, psyco_lobj_close_doc}, + {"unlink",(PyCFunction)psyco_lobj_unlink, + METH_VARARGS, psyco_lobj_unlink_doc}, + {"export",(PyCFunction)psyco_lobj_export, + METH_VARARGS, psyco_lobj_export_doc}, + {NULL} +}; + +/* object member list */ + +static struct PyMemberDef lobjectObject_members[] = { + {"oid", T_LONG, offsetof(lobjectObject, oid), RO, + "The backend OID associated to this lobject."}, + {"closed", T_LONG, offsetof(lobjectObject, closed), RO, + "The if the large object is closed (no file-like methods)."}, + {"mode", T_STRING, offsetof(lobjectObject, smode), RO, + "Open mode ('r', 'w', 'rw' or 'n')."}, + {NULL} +}; + +/* initialization and finalization methods */ + +static int +lobject_setup(lobjectObject *self, connectionObject *conn, + Oid oid, int mode, Oid new_oid, char *new_file) +{ + Dprintf("lobject_setup: init lobject object at %p", self); + + if (conn->isolation_level == 0) { + psyco_set_error(ProgrammingError, (PyObject*)self, + "can't use a lobject outside of transactions", NULL, NULL); + return -1; + } + + self->conn = conn; + self->mark = conn->mark; + + Py_INCREF((PyObject*)self->conn); + + self->closed = 0; + self->oid = InvalidOid; + self->fd = -1; + + if (lobject_open(self, conn, oid, mode, new_oid, new_file) == -1) + return -1; + + Dprintf("lobject_setup: good lobject object at %p, refcnt = %d", + self, ((PyObject *)self)->ob_refcnt); + Dprintf("lobject_setup: oid = %d, fd = %d", self->oid, self->fd); + return 0; +} + +static void +lobject_dealloc(PyObject* obj) +{ + lobjectObject *self = (lobjectObject *)obj; + + lobject_close(self); + Py_XDECREF((PyObject*)self->conn); + + Dprintf("lobject_dealloc: deleted lobject object at %p, refcnt = %d", + obj, obj->ob_refcnt); + + obj->ob_type->tp_free(obj); +} + +static int +lobject_init(PyObject *obj, PyObject *args, PyObject *kwds) +{ + Oid oid=InvalidOid, new_oid=InvalidOid; + int mode=0; + char *new_file = NULL; + PyObject *conn; + + if (!PyArg_ParseTuple(args, "O|iiis", + &conn, &oid, &mode, &new_oid, &new_file)) + return -1; + + return lobject_setup((lobjectObject *)obj, + (connectionObject *)conn, oid, mode, new_oid, new_file); +} + +static PyObject * +lobject_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + return type->tp_alloc(type, 0); +} + +static void +lobject_del(PyObject* self) +{ + PyObject_Del(self); +} + +static PyObject * +lobject_repr(lobjectObject *self) +{ + return PyString_FromFormat( + "<lobject object at %p; closed: %d>", self, self->closed); +} + + +/* object type */ + +#define lobjectType_doc \ +"A database large object." + +PyTypeObject lobjectType = { + PyObject_HEAD_INIT(NULL) + 0, + "psycopg2._psycopg.lobject", + sizeof(lobjectObject), + 0, + lobject_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + (reprfunc)lobject_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + + 0, /*tp_call*/ + (reprfunc)lobject_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*/ + lobjectType_doc, /*tp_doc*/ + + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + + /* Attribute descriptor and subclassing stuff */ + + lobjectObject_methods, /*tp_methods*/ + lobjectObject_members, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + + lobject_init, /*tp_init*/ + 0, /*tp_alloc Will be set to PyType_GenericAlloc in module init*/ + lobject_new, /*tp_new*/ + (freefunc)lobject_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*/ +}; + +#endif + diff --git a/psycopg/pqpath.c b/psycopg/pqpath.c index 561d4f5..23d3741 100644 --- a/psycopg/pqpath.c +++ b/psycopg/pqpath.c @@ -256,6 +256,8 @@ pq_commit(connectionObject *conn) return 0; } + conn->mark += 1; + pq_clear_async(conn); pgres = PQexec(conn->pgconn, query); if (pgres == NULL) { @@ -299,6 +301,8 @@ pq_abort(connectionObject *conn) return 0; } + conn->mark += 1; + pq_clear_async(conn); pgres = PQexec(conn->pgconn, query); if (pgres == NULL) { diff --git a/psycopg/pqpath.h b/psycopg/pqpath.h index 64d113e..3faa772 100644 --- a/psycopg/pqpath.h +++ b/psycopg/pqpath.h @@ -37,5 +37,7 @@ extern int pq_commit(connectionObject *conn); extern int pq_abort(connectionObject *conn); extern int pq_is_busy(connectionObject *conn); extern void pq_set_critical(connectionObject *conn, const char *msg); +extern void pq_raise(connectionObject *conn, cursorObject *curs, + PyObject *exc, char *msg); #endif /* !defined(PSYCOPG_PQPATH_H) */ diff --git a/psycopg/psycopgmodule.c b/psycopg/psycopgmodule.c index 5f37856..b4ea5cb 100644 --- a/psycopg/psycopgmodule.c +++ b/psycopg/psycopgmodule.c @@ -27,6 +27,7 @@ #include "psycopg/psycopg.h" #include "psycopg/connection.h" #include "psycopg/cursor.h" +#include "psycopg/lobject.h" #include "psycopg/typecast.h" #include "psycopg/microprotocols.h" #include "psycopg/microprotocols_proto.h" @@ -551,7 +552,7 @@ init_psycopg(void) asisType.ob_type = &PyType_Type; listType.ob_type = &PyType_Type; chunkType.ob_type = &PyType_Type; - + if (PyType_Ready(&connectionType) == -1) return; if (PyType_Ready(&cursorType) == -1) return; if (PyType_Ready(&typecastType) == -1) return; @@ -562,6 +563,11 @@ init_psycopg(void) if (PyType_Ready(&listType) == -1) return; if (PyType_Ready(&chunkType) == -1) return; +#ifdef PSYCOPG_EXTENSIONS + lobjectType.ob_type = &PyType_Type; + if (PyType_Ready(&lobjectType) == -1) return; +#endif + #ifdef HAVE_PYBOOL pbooleanType.ob_type = &PyType_Type; if (PyType_Ready(&pbooleanType) == -1) return; @@ -638,7 +644,10 @@ init_psycopg(void) PyModule_AddObject(module, "connection", (PyObject*)&connectionType); PyModule_AddObject(module, "cursor", (PyObject*)&cursorType); PyModule_AddObject(module, "ISQLQuote", (PyObject*)&isqlquoteType); - +#ifdef PSYCOPG_EXTENSIONS + PyModule_AddObject(module, "lobject", (PyObject*)&lobjectType); +#endif + /* encodings dictionary in module dictionary */ PyModule_AddObject(module, "encodings", psycoEncodings); @@ -664,6 +673,10 @@ init_psycopg(void) listType.tp_alloc = PyType_GenericAlloc; chunkType.tp_alloc = PyType_GenericAlloc; +#ifdef PSYCOPG_EXTENSIONS + lobjectType.tp_alloc = PyType_GenericAlloc; +#endif + #ifdef HAVE_PYDATETIME pydatetimeType.tp_alloc = PyType_GenericAlloc; #endif @@ -193,6 +193,7 @@ sources = [ 'psycopgmodule.c', 'pqpath.c', 'typecast.c', 'microprotocols.c', 'microprotocols_proto.c', 'connection_type.c', 'connection_int.c', 'cursor_type.c', 'cursor_int.c', + 'lobject_type.c', 'lobject_int.c', 'adapter_qstring.c', 'adapter_pboolean.c', 'adapter_binary.c', 'adapter_asis.c', 'adapter_list.c'] |
