mirror of
https://github.com/psycopg/psycopg2.git
synced 2025-02-07 12:50:32 +03:00
Support large objects truncating.
The lobject.truncate(len=0) method will be available if psycopg2 has been built against libpq from 8.3 or later (which is when the lobject truncating support has been introduced).
This commit is contained in:
parent
0edd520593
commit
e0d789466a
|
@ -78,6 +78,19 @@ functionalities defined by the |DBAPI|_.
|
||||||
|
|
||||||
Return the lobject current position.
|
Return the lobject current position.
|
||||||
|
|
||||||
|
.. method:: truncate(len=0)
|
||||||
|
|
||||||
|
.. versionadded:: 2.0.15
|
||||||
|
|
||||||
|
Truncate the lobject to the given size.
|
||||||
|
|
||||||
|
The method will only be available if psycopg has been built against libpq
|
||||||
|
from PostgreSQL 8.3 or later and can only be used with PostgreSQL servers
|
||||||
|
running these versions. It uses the |lo_truncate|_ libpq function.
|
||||||
|
|
||||||
|
.. |lo_truncate| replace:: `!lo_truncate()`
|
||||||
|
.. _lo_truncate: http://www.postgresql.org/docs/8.4/static/lo-interfaces.html#AEN36420
|
||||||
|
|
||||||
.. method:: close()
|
.. method:: close()
|
||||||
|
|
||||||
Close the object.
|
Close the object.
|
||||||
|
|
|
@ -64,6 +64,7 @@ HIDDEN Py_ssize_t lobject_write(lobjectObject *self, const char *buf,
|
||||||
size_t len);
|
size_t len);
|
||||||
HIDDEN int lobject_seek(lobjectObject *self, int pos, int whence);
|
HIDDEN int lobject_seek(lobjectObject *self, int pos, int whence);
|
||||||
HIDDEN int lobject_tell(lobjectObject *self);
|
HIDDEN int lobject_tell(lobjectObject *self);
|
||||||
|
HIDDEN int lobject_truncate(lobjectObject *self, size_t len);
|
||||||
HIDDEN int lobject_close(lobjectObject *self);
|
HIDDEN int lobject_close(lobjectObject *self);
|
||||||
|
|
||||||
#define lobject_is_closed(self) \
|
#define lobject_is_closed(self) \
|
||||||
|
|
|
@ -333,6 +333,36 @@ lobject_export(lobjectObject *self, const char *filename)
|
||||||
return retvalue;
|
return retvalue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PG_VERSION_HEX >= 0x080300
|
||||||
|
|
||||||
|
int
|
||||||
|
lobject_truncate(lobjectObject *self, size_t len)
|
||||||
|
{
|
||||||
|
int retvalue;
|
||||||
|
PGresult *pgres = NULL;
|
||||||
|
char *error = NULL;
|
||||||
|
|
||||||
|
Dprintf("lobject_truncate: fd = %d, len = %d",
|
||||||
|
self->fd, len);
|
||||||
|
|
||||||
|
Py_BEGIN_ALLOW_THREADS;
|
||||||
|
pthread_mutex_lock(&(self->conn->lock));
|
||||||
|
|
||||||
|
retvalue = lo_truncate(self->conn->pgconn, self->fd, len);
|
||||||
|
Dprintf("lobject_truncate: result = %d", retvalue);
|
||||||
|
if (retvalue < 0)
|
||||||
|
collect_error(self->conn, &error);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&(self->conn->lock));
|
||||||
|
Py_END_ALLOW_THREADS;
|
||||||
|
|
||||||
|
if (retvalue < 0)
|
||||||
|
pq_complete_error(self->conn, &pgres, &error);
|
||||||
|
return retvalue;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* PG_VERSION_HEX >= 0x080300 */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -227,6 +227,32 @@ psyco_lobj_get_closed(lobjectObject *self, void *closure)
|
||||||
return closed;
|
return closed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PG_VERSION_HEX >= 0x080300
|
||||||
|
|
||||||
|
#define psyco_lobj_truncate_doc \
|
||||||
|
"truncate(len=0) -- Truncate large object to given size."
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
psyco_lobj_truncate(lobjectObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "|i", &len))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
EXC_IF_LOBJ_CLOSED(self);
|
||||||
|
EXC_IF_LOBJ_LEVEL0(self);
|
||||||
|
EXC_IF_LOBJ_UNMARKED(self);
|
||||||
|
|
||||||
|
if (lobject_truncate(self, len) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
Py_INCREF(Py_None);
|
||||||
|
return Py_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* PG_VERSION_HEX >= 0x080300 */
|
||||||
|
|
||||||
|
|
||||||
/** the lobject object **/
|
/** the lobject object **/
|
||||||
|
|
||||||
|
@ -247,6 +273,10 @@ static struct PyMethodDef lobjectObject_methods[] = {
|
||||||
METH_VARARGS, psyco_lobj_unlink_doc},
|
METH_VARARGS, psyco_lobj_unlink_doc},
|
||||||
{"export",(PyCFunction)psyco_lobj_export,
|
{"export",(PyCFunction)psyco_lobj_export,
|
||||||
METH_VARARGS, psyco_lobj_export_doc},
|
METH_VARARGS, psyco_lobj_export_doc},
|
||||||
|
#if PG_VERSION_HEX >= 0x080300
|
||||||
|
{"truncate",(PyCFunction)psyco_lobj_truncate,
|
||||||
|
METH_VARARGS, psyco_lobj_truncate_doc},
|
||||||
|
#endif /* PG_VERSION_HEX >= 0x080300 */
|
||||||
{NULL}
|
{NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import os
|
||||||
import shutil
|
import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
|
import warnings
|
||||||
|
|
||||||
import psycopg2
|
import psycopg2
|
||||||
import psycopg2.extensions
|
import psycopg2.extensions
|
||||||
|
@ -261,6 +262,69 @@ class LargeObjectTests(unittest.TestCase):
|
||||||
self.assertEqual(open(filename, "rb").read(), "some data")
|
self.assertEqual(open(filename, "rb").read(), "some data")
|
||||||
|
|
||||||
|
|
||||||
|
class LargeObjectTruncateTests(LargeObjectTests):
|
||||||
|
|
||||||
|
skip = None
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
LargeObjectTests.setUp(self)
|
||||||
|
|
||||||
|
if self.skip is None:
|
||||||
|
self.skip = False
|
||||||
|
if self.conn.server_version < 80300:
|
||||||
|
warnings.warn("Large object truncate tests skipped, "
|
||||||
|
"the server does not support them")
|
||||||
|
self.skip = True
|
||||||
|
|
||||||
|
if not hasattr(psycopg2.extensions.lobject, 'truncate'):
|
||||||
|
warnings.warn("Large object truncate tests skipped, "
|
||||||
|
"psycopg2 has been built against an old library")
|
||||||
|
self.skip = True
|
||||||
|
|
||||||
|
def test_truncate(self):
|
||||||
|
if self.skip:
|
||||||
|
return
|
||||||
|
|
||||||
|
lo = self.conn.lobject()
|
||||||
|
lo.write("some data")
|
||||||
|
lo.close()
|
||||||
|
|
||||||
|
lo = self.conn.lobject(lo.oid, "w")
|
||||||
|
lo.truncate(4)
|
||||||
|
|
||||||
|
# seek position unchanged
|
||||||
|
self.assertEqual(lo.tell(), 0)
|
||||||
|
# data truncated
|
||||||
|
self.assertEqual(lo.read(), "some")
|
||||||
|
|
||||||
|
lo.truncate(6)
|
||||||
|
lo.seek(0)
|
||||||
|
# large object extended with zeroes
|
||||||
|
self.assertEqual(lo.read(), "some\x00\x00")
|
||||||
|
|
||||||
|
lo.truncate()
|
||||||
|
lo.seek(0)
|
||||||
|
# large object empty
|
||||||
|
self.assertEqual(lo.read(), "")
|
||||||
|
|
||||||
|
def test_truncate_after_close(self):
|
||||||
|
if self.skip:
|
||||||
|
return
|
||||||
|
|
||||||
|
lo = self.conn.lobject()
|
||||||
|
lo.close()
|
||||||
|
self.assertRaises(psycopg2.InterfaceError, lo.truncate)
|
||||||
|
|
||||||
|
def test_truncate_after_commit(self):
|
||||||
|
if self.skip:
|
||||||
|
return
|
||||||
|
|
||||||
|
lo = self.conn.lobject()
|
||||||
|
self.lo_oid = lo.oid
|
||||||
|
self.conn.commit()
|
||||||
|
|
||||||
|
self.assertRaises(psycopg2.ProgrammingError, lo.truncate)
|
||||||
|
|
||||||
def test_suite():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user