mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-14 04:56:33 +03:00
Merge lobject work from trunk, plus fixes to make it work with current
psycopg2.
This commit is contained in:
commit
975d2286e8
3
.bzrignore
Normal file
3
.bzrignore
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
./dist
|
||||||
|
./build
|
||||||
|
./MANIFEST
|
13
ChangeLog
13
ChangeLog
|
@ -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.
|
||||||
|
|
||||||
2008-04-21 James Henstridge <james@jamesh.id.au>
|
2008-04-21 James Henstridge <james@jamesh.id.au>
|
||||||
|
|
||||||
* tests/test_quote.py (QuotingTestCase.test_unicode): If the
|
* tests/test_quote.py (QuotingTestCase.test_unicode): If the
|
||||||
|
|
79
examples/lobject.py
Normal file
79
examples/lobject.py
Normal file
|
@ -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!"
|
|
@ -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
|
- `connection` -- the new-type inheritable connection class
|
||||||
- `cursor` -- the new-type inheritable cursor 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
|
- `adapt()` -- exposes the PEP-246_ compatible adapting mechanism used
|
||||||
by psycopg to adapt Python types to PostgreSQL ones
|
by psycopg to adapt Python types to PostgreSQL ones
|
||||||
|
|
||||||
|
@ -38,7 +39,7 @@ try:
|
||||||
except:
|
except:
|
||||||
pass
|
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 string_types, binary_types, new_type, register_type
|
||||||
from _psycopg import ISQLQuote
|
from _psycopg import ISQLQuote
|
||||||
|
|
||||||
|
|
|
@ -246,7 +246,6 @@ conn_commit(connectionObject *self)
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
res = pq_commit(self);
|
res = pq_commit(self);
|
||||||
self->mark++;
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,7 +257,6 @@ conn_rollback(connectionObject *self)
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
res = pq_abort(self);
|
res = pq_abort(self);
|
||||||
self->mark++;
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,7 +281,6 @@ conn_switch_isolation_level(connectionObject *self, int level)
|
||||||
res = pq_abort_locked(self, &pgres, &error);
|
res = pq_abort_locked(self, &pgres, &error);
|
||||||
}
|
}
|
||||||
self->isolation_level = level;
|
self->isolation_level = level;
|
||||||
self->mark++;
|
|
||||||
|
|
||||||
Dprintf("conn_switch_isolation_level: switched to level %d", level);
|
Dprintf("conn_switch_isolation_level: switched to level %d", level);
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include "psycopg/psycopg.h"
|
#include "psycopg/psycopg.h"
|
||||||
#include "psycopg/connection.h"
|
#include "psycopg/connection.h"
|
||||||
#include "psycopg/cursor.h"
|
#include "psycopg/cursor.h"
|
||||||
|
#include "psycopg/lobject.h"
|
||||||
|
|
||||||
/** DBAPI methods **/
|
/** DBAPI methods **/
|
||||||
|
|
||||||
|
@ -178,7 +179,7 @@ psyco_conn_set_isolation_level(connectionObject *self, PyObject *args)
|
||||||
return Py_None;
|
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 \
|
#define psyco_conn_set_client_encoding_doc \
|
||||||
"set_client_encoding(encoding) -- Set client encoding to ``encoding``."
|
"set_client_encoding(encoding) -- Set client encoding to ``encoding``."
|
||||||
|
@ -215,7 +216,7 @@ psyco_conn_set_client_encoding(connectionObject *self, PyObject *args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set_isolation_level method - switch connection isolation level */
|
/* get_transaction_status method - Get backend transaction status */
|
||||||
|
|
||||||
#define psyco_conn_get_transaction_status_doc \
|
#define psyco_conn_get_transaction_status_doc \
|
||||||
"get_transaction_status() -- Get backend transaction status."
|
"get_transaction_status() -- Get backend transaction status."
|
||||||
|
@ -230,6 +231,82 @@ psyco_conn_get_transaction_status(connectionObject *self, PyObject *args)
|
||||||
return PyInt_FromLong((long)PQtransactionStatus(self->pgconn));
|
return PyInt_FromLong((long)PQtransactionStatus(self->pgconn));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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
|
#endif
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -262,6 +339,8 @@ static struct PyMethodDef connectionObject_methods[] = {
|
||||||
METH_VARARGS, psyco_conn_set_client_encoding_doc},
|
METH_VARARGS, psyco_conn_set_client_encoding_doc},
|
||||||
{"get_transaction_status", (PyCFunction)psyco_conn_get_transaction_status,
|
{"get_transaction_status", (PyCFunction)psyco_conn_get_transaction_status,
|
||||||
METH_VARARGS, psyco_conn_get_transaction_status_doc},
|
METH_VARARGS, psyco_conn_get_transaction_status_doc},
|
||||||
|
{"lobject", (PyCFunction)psyco_conn_lobject,
|
||||||
|
METH_VARARGS|METH_KEYWORDS, psyco_conn_lobject_doc},
|
||||||
#endif
|
#endif
|
||||||
{NULL}
|
{NULL}
|
||||||
};
|
};
|
||||||
|
|
90
psycopg/lobject.h
Normal file
90
psycopg/lobject.h
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
/* 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/config.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 */
|
||||||
|
|
||||||
|
HIDDEN int lobject_open(lobjectObject *self, connectionObject *conn,
|
||||||
|
Oid oid, int mode, Oid new_oid, char *new_file);
|
||||||
|
HIDDEN int lobject_unlink(lobjectObject *self);
|
||||||
|
HIDDEN int lobject_export(lobjectObject *self, char *filename);
|
||||||
|
|
||||||
|
HIDDEN Py_ssize_t lobject_read(lobjectObject *self, char *buf, size_t len);
|
||||||
|
HIDDEN Py_ssize_t lobject_write(lobjectObject *self, char *buf, size_t len);
|
||||||
|
HIDDEN int lobject_seek(lobjectObject *self, int pos, int whence);
|
||||||
|
HIDDEN int lobject_tell(lobjectObject *self);
|
||||||
|
HIDDEN int 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) */
|
330
psycopg/lobject_int.c
Normal file
330
psycopg/lobject_int.c
Normal file
|
@ -0,0 +1,330 @@
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define PY_SSIZE_T_CLEAN
|
||||||
|
#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
|
||||||
|
|
||||||
|
static void
|
||||||
|
collect_error(connectionObject *conn, char **error)
|
||||||
|
{
|
||||||
|
const char *msg = PQerrorMessage(conn->pgconn);
|
||||||
|
|
||||||
|
if (msg)
|
||||||
|
*error = strdup(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
{
|
||||||
|
int retvalue = -1;
|
||||||
|
PGresult *pgres = NULL;
|
||||||
|
char *error = NULL;
|
||||||
|
|
||||||
|
Py_BEGIN_ALLOW_THREADS;
|
||||||
|
pthread_mutex_lock(&(self->conn->lock));
|
||||||
|
|
||||||
|
retvalue = pq_begin_locked(self->conn, &pgres, &error);
|
||||||
|
if (retvalue < 0)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
/* 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) {
|
||||||
|
collect_error(self->conn, &error);
|
||||||
|
retvalue = -1;
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (self->fd == -1) {
|
||||||
|
collect_error(self->conn, &error);
|
||||||
|
retvalue = -1;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* this is necessary to make sure no function that needs and
|
||||||
|
fd is called on unopened lobjects */
|
||||||
|
self->closed = 1;
|
||||||
|
}
|
||||||
|
/* set the mode for future reference */
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
retvalue = 0;
|
||||||
|
|
||||||
|
end:
|
||||||
|
pthread_mutex_unlock(&(self->conn->lock));
|
||||||
|
Py_END_ALLOW_THREADS;
|
||||||
|
|
||||||
|
if (retvalue < 0)
|
||||||
|
pq_complete_error(self->conn, &pgres, &error);
|
||||||
|
return retvalue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* lobject_close - close an existing lo */
|
||||||
|
|
||||||
|
static int
|
||||||
|
lobject_close_locked(lobjectObject *self, char **error)
|
||||||
|
{
|
||||||
|
int retvalue;
|
||||||
|
|
||||||
|
if (self->conn->isolation_level == 0 ||
|
||||||
|
self->conn->mark != self->mark ||
|
||||||
|
self->fd == -1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
retvalue = lo_close(self->conn->pgconn, self->fd);
|
||||||
|
self->fd = -1;
|
||||||
|
if (retvalue < 0)
|
||||||
|
collect_error(self->conn, error);
|
||||||
|
|
||||||
|
return retvalue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
lobject_close(lobjectObject *self)
|
||||||
|
{
|
||||||
|
PGresult *pgres = NULL;
|
||||||
|
char *error = NULL;
|
||||||
|
int retvalue;
|
||||||
|
|
||||||
|
Py_BEGIN_ALLOW_THREADS;
|
||||||
|
pthread_mutex_lock(&(self->conn->lock));
|
||||||
|
|
||||||
|
retvalue = lobject_close_locked(self, &error);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&(self->conn->lock));
|
||||||
|
Py_END_ALLOW_THREADS;
|
||||||
|
|
||||||
|
if (retvalue < 0)
|
||||||
|
pq_complete_error(self->conn, &pgres, &error);
|
||||||
|
return retvalue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* lobject_unlink - remove an lo from database */
|
||||||
|
|
||||||
|
int
|
||||||
|
lobject_unlink(lobjectObject *self)
|
||||||
|
{
|
||||||
|
PGresult *pgres = NULL;
|
||||||
|
char *error = NULL;
|
||||||
|
int retvalue = -1;
|
||||||
|
|
||||||
|
Py_BEGIN_ALLOW_THREADS;
|
||||||
|
pthread_mutex_lock(&(self->conn->lock));
|
||||||
|
|
||||||
|
retvalue = pq_begin_locked(self->conn, &pgres, &error);
|
||||||
|
if (retvalue < 0)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
/* first we make sure the lobject is closed and then we unlink */
|
||||||
|
retvalue = lobject_close_locked(self, &error);
|
||||||
|
if (retvalue < 0)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
retvalue = lo_unlink(self->conn->pgconn, self->oid);
|
||||||
|
if (retvalue < 0)
|
||||||
|
collect_error(self->conn, &error);
|
||||||
|
|
||||||
|
end:
|
||||||
|
pthread_mutex_unlock(&(self->conn->lock));
|
||||||
|
Py_END_ALLOW_THREADS;
|
||||||
|
|
||||||
|
if (retvalue < 0)
|
||||||
|
pq_complete_error(self->conn, &pgres, &error);
|
||||||
|
return retvalue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* lobject_write - write bytes to a lo */
|
||||||
|
|
||||||
|
Py_ssize_t
|
||||||
|
lobject_write(lobjectObject *self, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
Py_ssize_t written;
|
||||||
|
PGresult *pgres = NULL;
|
||||||
|
char *error = NULL;
|
||||||
|
|
||||||
|
Py_BEGIN_ALLOW_THREADS;
|
||||||
|
pthread_mutex_lock(&(self->conn->lock));
|
||||||
|
|
||||||
|
written = lo_write(self->conn->pgconn, self->fd, buf, len);
|
||||||
|
if (written < 0)
|
||||||
|
collect_error(self->conn, &error);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&(self->conn->lock));
|
||||||
|
Py_END_ALLOW_THREADS;
|
||||||
|
|
||||||
|
if (written < 0)
|
||||||
|
pq_complete_error(self->conn, &pgres, &error);
|
||||||
|
return written;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* lobject_read - read bytes from a lo */
|
||||||
|
|
||||||
|
Py_ssize_t
|
||||||
|
lobject_read(lobjectObject *self, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
Py_ssize_t n_read;
|
||||||
|
PGresult *pgres = NULL;
|
||||||
|
char *error = NULL;
|
||||||
|
|
||||||
|
Py_BEGIN_ALLOW_THREADS;
|
||||||
|
pthread_mutex_lock(&(self->conn->lock));
|
||||||
|
|
||||||
|
n_read = lo_read(self->conn->pgconn, self->fd, buf, len);
|
||||||
|
if (n_read < 0)
|
||||||
|
collect_error(self->conn, &error);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&(self->conn->lock));
|
||||||
|
Py_END_ALLOW_THREADS;
|
||||||
|
|
||||||
|
if (n_read < 0)
|
||||||
|
pq_complete_error(self->conn, &pgres, &error);
|
||||||
|
return n_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* lobject_seek - move the current position in the lo */
|
||||||
|
|
||||||
|
int
|
||||||
|
lobject_seek(lobjectObject *self, int pos, int whence)
|
||||||
|
{
|
||||||
|
PGresult *pgres = NULL;
|
||||||
|
char *error = NULL;
|
||||||
|
int where;
|
||||||
|
|
||||||
|
Dprintf("lobject_seek: fd = %d, pos = %d, whence = %d",
|
||||||
|
self->fd, pos, whence);
|
||||||
|
|
||||||
|
Py_BEGIN_ALLOW_THREADS;
|
||||||
|
pthread_mutex_lock(&(self->conn->lock));
|
||||||
|
|
||||||
|
where = lo_lseek(self->conn->pgconn, self->fd, pos, whence);
|
||||||
|
Dprintf("lobject_seek: where = %d", where);
|
||||||
|
if (where < 0)
|
||||||
|
collect_error(self->conn, &error);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&(self->conn->lock));
|
||||||
|
Py_END_ALLOW_THREADS;
|
||||||
|
|
||||||
|
if (where < 0)
|
||||||
|
pq_complete_error(self->conn, &pgres, &error);
|
||||||
|
return where;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* lobject_tell - tell the current position in the lo */
|
||||||
|
|
||||||
|
int
|
||||||
|
lobject_tell(lobjectObject *self)
|
||||||
|
{
|
||||||
|
PGresult *pgres = NULL;
|
||||||
|
char *error = NULL;
|
||||||
|
int where;
|
||||||
|
|
||||||
|
Dprintf("lobject_tell: fd = %d", self->fd);
|
||||||
|
|
||||||
|
Py_BEGIN_ALLOW_THREADS;
|
||||||
|
pthread_mutex_lock(&(self->conn->lock));
|
||||||
|
|
||||||
|
where = lo_tell(self->conn->pgconn, self->fd);
|
||||||
|
Dprintf("lobject_tell: where = %d", where);
|
||||||
|
if (where < 0)
|
||||||
|
collect_error(self->conn, &error);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&(self->conn->lock));
|
||||||
|
Py_END_ALLOW_THREADS;
|
||||||
|
|
||||||
|
if (where < 0)
|
||||||
|
pq_complete_error(self->conn, &pgres, &error);
|
||||||
|
return where;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* lobject_export - export to a local file */
|
||||||
|
|
||||||
|
int
|
||||||
|
lobject_export(lobjectObject *self, char *filename)
|
||||||
|
{
|
||||||
|
PGresult *pgres = NULL;
|
||||||
|
char *error = NULL;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
Py_BEGIN_ALLOW_THREADS;
|
||||||
|
pthread_mutex_lock(&(self->conn->lock));
|
||||||
|
|
||||||
|
res = lo_export(self->conn->pgconn, self->oid, filename);
|
||||||
|
if (res < 0)
|
||||||
|
collect_error(self->conn, &error);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&(self->conn->lock));
|
||||||
|
Py_END_ALLOW_THREADS;
|
||||||
|
|
||||||
|
if (res < 0)
|
||||||
|
pq_complete_error(self->conn, &pgres, &error);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
391
psycopg/lobject_type.c
Normal file
391
psycopg/lobject_type.c
Normal file
|
@ -0,0 +1,391 @@
|
||||||
|
/* 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;
|
||||||
|
if (lobject_close(self) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
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, offset, 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_UINT, offsetof(lobjectObject, oid), RO,
|
||||||
|
"The backend OID associated to this lobject."},
|
||||||
|
{"closed", T_INT, 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;
|
||||||
|
|
||||||
|
if (lobject_close(self) < 0)
|
||||||
|
PyErr_Print();
|
||||||
|
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
|
||||||
|
|
|
@ -348,7 +348,6 @@ pq_complete_error(connectionObject *conn, PGresult **pgres, char **error)
|
||||||
pq_raise(conn, NULL, *pgres);
|
pq_raise(conn, NULL, *pgres);
|
||||||
else if (*error != NULL) {
|
else if (*error != NULL) {
|
||||||
PyErr_SetString(OperationalError, *error);
|
PyErr_SetString(OperationalError, *error);
|
||||||
free(*error);
|
|
||||||
} else {
|
} else {
|
||||||
PyErr_SetString(OperationalError, "unknown error");
|
PyErr_SetString(OperationalError, "unknown error");
|
||||||
}
|
}
|
||||||
|
@ -368,7 +367,7 @@ pq_complete_error(connectionObject *conn, PGresult **pgres, char **error)
|
||||||
On error, -1 is returned, and the pgres argument will hold the
|
On error, -1 is returned, and the pgres argument will hold the
|
||||||
relevant result structure.
|
relevant result structure.
|
||||||
*/
|
*/
|
||||||
static int
|
int
|
||||||
pq_begin_locked(connectionObject *conn, PGresult **pgres, char **error)
|
pq_begin_locked(connectionObject *conn, PGresult **pgres, char **error)
|
||||||
{
|
{
|
||||||
const char *query[] = {
|
const char *query[] = {
|
||||||
|
@ -416,6 +415,7 @@ pq_commit(connectionObject *conn)
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
Py_BEGIN_ALLOW_THREADS;
|
||||||
pthread_mutex_lock(&conn->lock);
|
pthread_mutex_lock(&conn->lock);
|
||||||
|
conn->mark += 1;
|
||||||
|
|
||||||
pq_clear_async(conn);
|
pq_clear_async(conn);
|
||||||
retvalue = pq_execute_command_locked(conn, "COMMIT", &pgres, &error);
|
retvalue = pq_execute_command_locked(conn, "COMMIT", &pgres, &error);
|
||||||
|
@ -446,6 +446,7 @@ pq_abort_locked(connectionObject *conn, PGresult **pgres, char **error)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conn->mark += 1;
|
||||||
pq_clear_async(conn);
|
pq_clear_async(conn);
|
||||||
retvalue = pq_execute_command_locked(conn, "ROLLBACK", pgres, error);
|
retvalue = pq_execute_command_locked(conn, "ROLLBACK", pgres, error);
|
||||||
if (retvalue == 0)
|
if (retvalue == 0)
|
||||||
|
|
|
@ -33,6 +33,8 @@
|
||||||
/* exported functions */
|
/* exported functions */
|
||||||
HIDDEN int pq_fetch(cursorObject *curs);
|
HIDDEN int pq_fetch(cursorObject *curs);
|
||||||
HIDDEN int pq_execute(cursorObject *curs, const char *query, int async);
|
HIDDEN int pq_execute(cursorObject *curs, const char *query, int async);
|
||||||
|
HIDDEN int pq_begin_locked(connectionObject *conn, PGresult **pgres,
|
||||||
|
char **error);
|
||||||
HIDDEN int pq_commit(connectionObject *conn);
|
HIDDEN int pq_commit(connectionObject *conn);
|
||||||
HIDDEN int pq_abort_locked(connectionObject *conn, PGresult **pgres,
|
HIDDEN int pq_abort_locked(connectionObject *conn, PGresult **pgres,
|
||||||
char **error);
|
char **error);
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include "psycopg/psycopg.h"
|
#include "psycopg/psycopg.h"
|
||||||
#include "psycopg/connection.h"
|
#include "psycopg/connection.h"
|
||||||
#include "psycopg/cursor.h"
|
#include "psycopg/cursor.h"
|
||||||
|
#include "psycopg/lobject.h"
|
||||||
#include "psycopg/typecast.h"
|
#include "psycopg/typecast.h"
|
||||||
#include "psycopg/microprotocols.h"
|
#include "psycopg/microprotocols.h"
|
||||||
#include "psycopg/microprotocols_proto.h"
|
#include "psycopg/microprotocols_proto.h"
|
||||||
|
@ -719,6 +720,11 @@ init_psycopg(void)
|
||||||
if (PyType_Ready(&listType) == -1) return;
|
if (PyType_Ready(&listType) == -1) return;
|
||||||
if (PyType_Ready(&chunkType) == -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
|
#ifdef HAVE_PYBOOL
|
||||||
pbooleanType.ob_type = &PyType_Type;
|
pbooleanType.ob_type = &PyType_Type;
|
||||||
if (PyType_Ready(&pbooleanType) == -1) return;
|
if (PyType_Ready(&pbooleanType) == -1) return;
|
||||||
|
@ -794,6 +800,9 @@ init_psycopg(void)
|
||||||
PyModule_AddObject(module, "connection", (PyObject*)&connectionType);
|
PyModule_AddObject(module, "connection", (PyObject*)&connectionType);
|
||||||
PyModule_AddObject(module, "cursor", (PyObject*)&cursorType);
|
PyModule_AddObject(module, "cursor", (PyObject*)&cursorType);
|
||||||
PyModule_AddObject(module, "ISQLQuote", (PyObject*)&isqlquoteType);
|
PyModule_AddObject(module, "ISQLQuote", (PyObject*)&isqlquoteType);
|
||||||
|
#ifdef PSYCOPG_EXTENSIONS
|
||||||
|
PyModule_AddObject(module, "lobject", (PyObject*)&lobjectType);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* encodings dictionary in module dictionary */
|
/* encodings dictionary in module dictionary */
|
||||||
PyModule_AddObject(module, "encodings", psycoEncodings);
|
PyModule_AddObject(module, "encodings", psycoEncodings);
|
||||||
|
@ -820,6 +829,10 @@ init_psycopg(void)
|
||||||
listType.tp_alloc = PyType_GenericAlloc;
|
listType.tp_alloc = PyType_GenericAlloc;
|
||||||
chunkType.tp_alloc = PyType_GenericAlloc;
|
chunkType.tp_alloc = PyType_GenericAlloc;
|
||||||
|
|
||||||
|
#ifdef PSYCOPG_EXTENSIONS
|
||||||
|
lobjectType.tp_alloc = PyType_GenericAlloc;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_PYDATETIME
|
#ifdef HAVE_PYDATETIME
|
||||||
pydatetimeType.tp_alloc = PyType_GenericAlloc;
|
pydatetimeType.tp_alloc = PyType_GenericAlloc;
|
||||||
#endif
|
#endif
|
||||||
|
|
1
setup.py
1
setup.py
|
@ -326,6 +326,7 @@ sources = [
|
||||||
'psycopgmodule.c', 'pqpath.c', 'typecast.c',
|
'psycopgmodule.c', 'pqpath.c', 'typecast.c',
|
||||||
'microprotocols.c', 'microprotocols_proto.c',
|
'microprotocols.c', 'microprotocols_proto.c',
|
||||||
'connection_type.c', 'connection_int.c', 'cursor_type.c', 'cursor_int.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_qstring.c', 'adapter_pboolean.c', 'adapter_binary.c',
|
||||||
'adapter_asis.c', 'adapter_list.c']
|
'adapter_asis.c', 'adapter_list.c']
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ import test_quote
|
||||||
import test_connection
|
import test_connection
|
||||||
import test_transaction
|
import test_transaction
|
||||||
import types_basic
|
import types_basic
|
||||||
|
import test_lobject
|
||||||
|
|
||||||
def test_suite():
|
def test_suite():
|
||||||
suite = unittest.TestSuite()
|
suite = unittest.TestSuite()
|
||||||
|
@ -35,6 +36,7 @@ def test_suite():
|
||||||
suite.addTest(test_connection.test_suite())
|
suite.addTest(test_connection.test_suite())
|
||||||
suite.addTest(test_transaction.test_suite())
|
suite.addTest(test_transaction.test_suite())
|
||||||
suite.addTest(types_basic.test_suite())
|
suite.addTest(types_basic.test_suite())
|
||||||
|
suite.addTest(test_lobject.test_suite())
|
||||||
return suite
|
return suite
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
80
tests/test_lobject.py
Normal file
80
tests/test_lobject.py
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import psycopg2
|
||||||
|
import psycopg2.extensions
|
||||||
|
import tests
|
||||||
|
|
||||||
|
|
||||||
|
class LargeObjectTests(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.conn = psycopg2.connect(tests.dsn)
|
||||||
|
self.lo_oid = None
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
if self.lo_oid is not None:
|
||||||
|
self.conn.lobject(self.lo_oid).unlink()
|
||||||
|
|
||||||
|
def test_create(self):
|
||||||
|
lo = self.conn.lobject()
|
||||||
|
self.assertNotEqual(lo, None)
|
||||||
|
self.assertEqual(lo.mode, "w")
|
||||||
|
|
||||||
|
def test_open_non_existent(self):
|
||||||
|
# This test will give a false negative if Oid 42 is a large object.
|
||||||
|
self.assertRaises(psycopg2.OperationalError, self.conn.lobject, 42)
|
||||||
|
|
||||||
|
def test_open_existing(self):
|
||||||
|
lo = self.conn.lobject()
|
||||||
|
lo2 = self.conn.lobject(lo.oid)
|
||||||
|
self.assertNotEqual(lo2, None)
|
||||||
|
self.assertEqual(lo2.oid, lo.oid)
|
||||||
|
self.assertEqual(lo2.mode, "r")
|
||||||
|
|
||||||
|
def test_close(self):
|
||||||
|
lo = self.conn.lobject()
|
||||||
|
self.assertEqual(lo.closed, False)
|
||||||
|
lo.close()
|
||||||
|
self.assertEqual(lo.closed, True)
|
||||||
|
|
||||||
|
def test_write(self):
|
||||||
|
lo = self.conn.lobject()
|
||||||
|
self.assertEqual(lo.write("some data"), len("some data"))
|
||||||
|
|
||||||
|
def test_seek_tell(self):
|
||||||
|
lo = self.conn.lobject()
|
||||||
|
length = lo.write("some data")
|
||||||
|
self.assertEqual(lo.tell(), length)
|
||||||
|
lo.close()
|
||||||
|
lo = self.conn.lobject(lo.oid)
|
||||||
|
|
||||||
|
self.assertEqual(lo.seek(5, 0), 5)
|
||||||
|
self.assertEqual(lo.tell(), 5)
|
||||||
|
|
||||||
|
# SEEK_CUR: relative current location
|
||||||
|
self.assertEqual(lo.seek(2, 1), 7)
|
||||||
|
self.assertEqual(lo.tell(), 7)
|
||||||
|
|
||||||
|
# SEEK_END: relative to end of file
|
||||||
|
self.assertEqual(lo.seek(-2, 2), length - 2)
|
||||||
|
|
||||||
|
def test_read(self):
|
||||||
|
lo = self.conn.lobject()
|
||||||
|
length = lo.write("some data")
|
||||||
|
lo.close()
|
||||||
|
|
||||||
|
lo = self.conn.lobject(lo.oid)
|
||||||
|
self.assertEqual(lo.read(4), "some")
|
||||||
|
self.assertEqual(lo.read(), " data")
|
||||||
|
|
||||||
|
def test_unlink(self):
|
||||||
|
lo = self.conn.lobject()
|
||||||
|
lo.close()
|
||||||
|
lo.unlink()
|
||||||
|
|
||||||
|
# the object doesn't exist now, so we can't reopen it.
|
||||||
|
self.assertRaises(psycopg2.OperationalError, self.conn.lobject, lo.oid)
|
||||||
|
|
||||||
|
|
||||||
|
def test_suite():
|
||||||
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
Loading…
Reference in New Issue
Block a user