Skip to content

Commit cd67d3d

Browse files
Blake Rousedvarrazzo
authored andcommitted
Modify truncate to use lo_truncate64. Use HAVE_LO64 define to use new lo_*64 methods. Check size of offset and length for versions without LO64.
1 parent e13ec67 commit cd67d3d

File tree

4 files changed

+86
-20
lines changed

4 files changed

+86
-20
lines changed

psycopg/lobject_int.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,11 @@ lobject_seek(lobjectObject *self, long pos, int whence)
389389
Py_BEGIN_ALLOW_THREADS;
390390
pthread_mutex_lock(&(self->conn->lock));
391391

392+
#if HAVE_LO64
392393
where = lo_lseek64(self->conn->pgconn, self->fd, pos, whence);
394+
#else
395+
where = (long)lo_lseek(self->conn->pgconn, self->fd, (int)pos, whence);
396+
#endif
393397
Dprintf("lobject_seek: where = %Ld", where);
394398
if (where < 0)
395399
collect_error(self->conn, &error);
@@ -416,7 +420,11 @@ lobject_tell(lobjectObject *self)
416420
Py_BEGIN_ALLOW_THREADS;
417421
pthread_mutex_lock(&(self->conn->lock));
418422

423+
#if HAVE_LO64
419424
where = lo_tell64(self->conn->pgconn, self->fd);
425+
#else
426+
where = (long)lo_tell(self->conn->pgconn, self->fd);
427+
#endif
420428
Dprintf("lobject_tell: where = %Ld", where);
421429
if (where < 0)
422430
collect_error(self->conn, &error);
@@ -473,7 +481,11 @@ lobject_truncate(lobjectObject *self, size_t len)
473481
Py_BEGIN_ALLOW_THREADS;
474482
pthread_mutex_lock(&(self->conn->lock));
475483

484+
#if HAVE_LO64
485+
retvalue = lo_truncate64(self->conn->pgconn, self->fd, len);
486+
#else
476487
retvalue = lo_truncate(self->conn->pgconn, self->fd, len);
488+
#endif
477489
Dprintf("lobject_truncate: result = %d", retvalue);
478490
if (retvalue < 0)
479491
collect_error(self->conn, &error);

psycopg/lobject_type.c

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,14 @@ psyco_lobj_seek(lobjectObject *self, PyObject *args)
175175
EXC_IF_LOBJ_LEVEL0(self);
176176
EXC_IF_LOBJ_UNMARKED(self);
177177

178+
#if !HAVE_LO64
179+
if (offset > INT_MAX) {
180+
psyco_set_error(InterfaceError, NULL,
181+
"offset out of range");
182+
return NULL;
183+
}
184+
#endif
185+
178186
if ((pos = lobject_seek(self, offset, whence)) < 0)
179187
return NULL;
180188

@@ -255,15 +263,23 @@ psyco_lobj_get_closed(lobjectObject *self, void *closure)
255263
static PyObject *
256264
psyco_lobj_truncate(lobjectObject *self, PyObject *args)
257265
{
258-
int len = 0;
266+
long len = 0;
259267

260-
if (!PyArg_ParseTuple(args, "|i", &len))
268+
if (!PyArg_ParseTuple(args, "|l", &len))
261269
return NULL;
262270

263271
EXC_IF_LOBJ_CLOSED(self);
264272
EXC_IF_LOBJ_LEVEL0(self);
265273
EXC_IF_LOBJ_UNMARKED(self);
266274

275+
#if !HAVE_LO64
276+
if (len > INT_MAX) {
277+
psyco_set_error(InterfaceError, NULL,
278+
"len out of range");
279+
return NULL;
280+
}
281+
#endif
282+
267283
if (lobject_truncate(self, len) < 0)
268284
return NULL;
269285

setup.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,13 @@ def finalize_options(self):
415415

416416
define_macros.append(("PG_VERSION_HEX", "0x%02X%02X%02X" %
417417
(int(pgmajor), int(pgminor), int(pgpatch))))
418+
419+
# enable lo64 if postgres >= 9.3
420+
if int(pgmajor) >= 9 and int(pgminor) >= 3:
421+
define_macros.append(("HAVE_LO64", "1"))
422+
else:
423+
define_macros.append(("HAVE_LO64", "0"))
424+
418425
except Warning:
419426
w = sys.exc_info()[1] # work around py 2/3 different syntax
420427
sys.stderr.write("Error: %s\n" % w)

tests/test_lobject.py

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -225,24 +225,6 @@ def test_seek_tell(self):
225225
self.assertEqual(lo.seek(-2, 2), length - 2)
226226
self.assertEqual(lo.read(), "ta")
227227

228-
def test_seek_tell_greater_than_2gb(self):
229-
lo = self.conn.lobject()
230-
231-
# write chunks until its 3gb
232-
length = 0
233-
for _ in range(24):
234-
# each chunk is written with 128mb
235-
length += lo.write("data" * (1 << 25))
236-
self.assertEqual(lo.tell(), length)
237-
lo.close()
238-
lo = self.conn.lobject(lo.oid)
239-
240-
# seek to 3gb - 4, last written text should be data
241-
offset = (1 << 31) + (1 << 30) - 4 # 2gb + 1gb - 4
242-
self.assertEqual(lo.seek(offset, 0), offset)
243-
self.assertEqual(lo.tell(), offset)
244-
self.assertEqual(lo.read(), "data")
245-
246228
def test_unlink(self):
247229
lo = self.conn.lobject()
248230
lo.unlink()
@@ -458,6 +440,55 @@ def test_truncate_after_commit(self):
458440
skip_if_no_lo, skip_lo_if_green, skip_if_no_truncate)
459441

460442

443+
def skip_if_no_lo64(f):
444+
@wraps(f)
445+
def skip_if_no_lo64_(self):
446+
if self.conn.server_version < 90300:
447+
return self.skipTest("large objects 64bit only supported from PG 9.3")
448+
else:
449+
return f(self)
450+
451+
return skip_if_no_lo64_
452+
453+
class LargeObject64Tests(LargeObjectTestCase):
454+
def test_seek_tell_truncate_greater_than_2gb(self):
455+
lo = self.conn.lobject()
456+
457+
length = (1 << 31) + (1 << 30) # 2gb + 1gb = 3gb
458+
lo.truncate(length)
459+
460+
self.assertEqual(lo.seek(length, 0), length)
461+
self.assertEqual(lo.tell(), length)
462+
463+
decorate_all_tests(LargeObject64Tests,
464+
skip_if_no_lo, skip_lo_if_green, skip_if_no_truncate, skip_if_no_lo64)
465+
466+
467+
def skip_if_lo64(f):
468+
@wraps(f)
469+
def skip_if_lo64_(self):
470+
if self.conn.server_version >= 90300:
471+
return self.skipTest("large objects 64bit only supported from PG 9.3")
472+
else:
473+
return f(self)
474+
475+
return skip_if_lo64_
476+
477+
class LargeObjectNot64Tests(LargeObjectTestCase):
478+
def test_seek_larger_than_2gb(self):
479+
lo = self.conn.lobject()
480+
offset = 1 << 32 # 4gb
481+
self.assertRaises(psycopg2.InterfaceError, lo.seek, offset, 0)
482+
483+
def test_truncate_larger_than_2gb(self):
484+
lo = self.conn.lobject()
485+
length = 1 << 32 # 4gb
486+
self.assertRaises(psycopg2.InterfaceError, lo.truncate, length)
487+
488+
decorate_all_tests(LargeObjectNot64Tests,
489+
skip_if_no_lo, skip_lo_if_green, skip_if_no_truncate, skip_if_lo64)
490+
491+
461492
def test_suite():
462493
return unittest.TestLoader().loadTestsFromName(__name__)
463494

0 commit comments

Comments
 (0)