lobj64-v4.patch
text/x-patch
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v4
| File | + | − |
|---|---|---|
| doc/src/sgml/lobj.sgml | 0 | 0 |
| src/backend/libpq/be-fsstubs.c | 0 | 0 |
| src/backend/storage/large_object/inv_api.c | 0 | 0 |
| src/backend/utils/errcodes.txt | 0 | 0 |
| src/include/catalog/pg_proc.h | 0 | 0 |
| src/include/libpq/be-fsstubs.h | 0 | 0 |
| src/include/postgres_ext.h | 0 | 0 |
| src/include/storage/large_object.h | 0 | 0 |
| src/interfaces/libpq/exports.txt | 0 | 0 |
| src/interfaces/libpq/fe-lobj.c | 0 | 0 |
| src/interfaces/libpq/libpq-fe.h | 0 | 0 |
| src/interfaces/libpq/libpq-int.h | 0 | 0 |
| src/test/examples/Makefile | 0 | 0 |
| src/test/regress/input/largeobject.source | 0 | 0 |
| src/test/regress/output/largeobject.source | 0 | 0 |
diff --git a/doc/src/sgml/lobj.sgml b/doc/src/sgml/lobj.sgml
new file mode 100644
index 291409f..d34190f
*** a/doc/src/sgml/lobj.sgml
--- b/doc/src/sgml/lobj.sgml
***************
*** 41,47 ****
larger than a single database page into a secondary storage area per table.
This makes the large object facility partially obsolete. One
remaining advantage of the large object facility is that it allows values
! up to 2 GB in size, whereas <acronym>TOAST</acronym>ed fields can be at
most 1 GB. Also, large objects can be randomly modified using a read/write
API that is more efficient than performing such operations using
<acronym>TOAST</acronym>.
--- 41,47 ----
larger than a single database page into a secondary storage area per table.
This makes the large object facility partially obsolete. One
remaining advantage of the large object facility is that it allows values
! up to 4 TB in size, whereas <acronym>TOAST</acronym>ed fields can be at
most 1 GB. Also, large objects can be randomly modified using a read/write
API that is more efficient than performing such operations using
<acronym>TOAST</acronym>.
*************** int lo_open(PGconn *conn, Oid lobjId, in
*** 237,243 ****
<function>lo_open</function> returns a (non-negative) large object
descriptor for later use in <function>lo_read</function>,
<function>lo_write</function>, <function>lo_lseek</function>,
! <function>lo_tell</function>, and <function>lo_close</function>.
The descriptor is only valid for
the duration of the current transaction.
On failure, -1 is returned.
--- 237,245 ----
<function>lo_open</function> returns a (non-negative) large object
descriptor for later use in <function>lo_read</function>,
<function>lo_write</function>, <function>lo_lseek</function>,
! <function>lo_lseek64</function>, <function>lo_tell</function>,
! <function>lo_tell64</function>, <function>lo_truncate</function>,
! <function>lo_truncate64</function>, and <function>lo_close</function>.
The descriptor is only valid for
the duration of the current transaction.
On failure, -1 is returned.
*************** int lo_read(PGconn *conn, int fd, char *
*** 312,317 ****
--- 314,320 ----
large object descriptor, call
<synopsis>
int lo_lseek(PGconn *conn, int fd, int offset, int whence);
+ pg_int64 lo_lseek64(PGconn *conn, int fd, pg_int64 offset, int whence);
</synopsis>
<indexterm><primary>lo_lseek</></> This function moves the
current location pointer for the large object descriptor identified by
*************** int lo_lseek(PGconn *conn, int fd, int o
*** 321,327 ****
--- 324,339 ----
<symbol>SEEK_CUR</> (seek from current position), and
<symbol>SEEK_END</> (seek from object end). The return value is
the new location pointer, or -1 on error.
+ <indexterm><primary>lo_lseek64</></> <function>lo_lseek64</function>
+ is a function for large objects larger than 2GB. <symbol>pg_int64</>
+ is defined as 8-byte integer type.
+ </para>
+ <para>
+ <function>lo_lseek64</> is new as of <productname>PostgreSQL</productname>
+ 9.3; if this function is run against an older server version, it will
+ fail and return a negative value.
</para>
+
</sect2>
<sect2 id="lo-tell">
*************** int lo_lseek(PGconn *conn, int fd, int o
*** 332,340 ****
--- 344,360 ----
call
<synopsis>
int lo_tell(PGconn *conn, int fd);
+ pg_int64 lo_tell64(PGconn *conn, int fd);
</synopsis>
<indexterm><primary>lo_tell</></> If there is an error, the
return value is negative.
+ <indexterm><primary>lo_tell64</></> <function>lo_tell64</function> is
+ a function for large objects larger than 2GB.
+ </para>
+ <para>
+ <function>lo_tell64</> is new as of <productname>PostgreSQL</productname>
+ 9.3; if this function is run against an older server version, it will
+ fail and return a negative value.
</para>
</sect2>
*************** int lo_tell(PGconn *conn, int fd);
*** 345,350 ****
--- 365,371 ----
To truncate a large object to a given length, call
<synopsis>
int lo_truncate(PGcon *conn, int fd, size_t len);
+ int lo_truncate64(PGcon *conn, int fd, pg_int64 len);
</synopsis>
<indexterm><primary>lo_truncate</></> truncates the large object
descriptor <parameter>fd</> to length <parameter>len</>. The
*************** int lo_truncate(PGcon *conn, int fd, siz
*** 352,357 ****
--- 373,380 ----
previous <function>lo_open</function>. If <parameter>len</> is
greater than the current large object length, the large object
is extended with null bytes ('\0').
+ <indexterm><primary>lo_truncate64</></> <function>lo_truncate64</function>
+ is a function for large objects larger than 2GB.
</para>
<para>
*************** int lo_truncate(PGcon *conn, int fd, siz
*** 359,365 ****
</para>
<para>
! On success <function>lo_truncate</function> returns
zero. On error, the return value is negative.
</para>
--- 382,388 ----
</para>
<para>
! On success <function>lo_truncate</function>, <function>lo_truncate64</function> returns
zero. On error, the return value is negative.
</para>
*************** int lo_truncate(PGcon *conn, int fd, siz
*** 368,373 ****
--- 391,401 ----
8.3; if this function is run against an older server version, it will
fail and return a negative value.
</para>
+ <para>
+ <function>lo_truncate64</> is new as of <productname>PostgreSQL</productname>
+ 9.3; if this function is run against an older server version, it will
+ fail and return a negative value.
+ </para>
</sect2>
<sect2 id="lo-close">
diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c
new file mode 100644
index 6f7e474..4bc81ba
*** a/src/backend/libpq/be-fsstubs.c
--- b/src/backend/libpq/be-fsstubs.c
***************
*** 39,44 ****
--- 39,45 ----
#include "postgres.h"
#include <fcntl.h>
+ #include <limits.h>
#include <sys/stat.h>
#include <unistd.h>
*************** lo_lseek(PG_FUNCTION_ARGS)
*** 216,222 ****
int32 fd = PG_GETARG_INT32(0);
int32 offset = PG_GETARG_INT32(1);
int32 whence = PG_GETARG_INT32(2);
! int status;
if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
ereport(ERROR,
--- 217,223 ----
int32 fd = PG_GETARG_INT32(0);
int32 offset = PG_GETARG_INT32(1);
int32 whence = PG_GETARG_INT32(2);
! int64 status;
if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
ereport(ERROR,
*************** lo_lseek(PG_FUNCTION_ARGS)
*** 225,233 ****
--- 226,270 ----
status = inv_seek(cookies[fd], offset, whence);
+ if (INT_MAX < status)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_BLOB_OFFSET_OVERFLOW),
+ errmsg("offset overflow: %d", fd)));
+ PG_RETURN_INT32(-1);
+ }
+
PG_RETURN_INT32(status);
}
+
+ Datum
+ lo_lseek64(PG_FUNCTION_ARGS)
+ {
+ int32 fd = PG_GETARG_INT32(0);
+ int64 offset = PG_GETARG_INT64(1);
+ int32 whence = PG_GETARG_INT32(2);
+ MemoryContext currentContext;
+ int64 status;
+
+ if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("invalid large-object descriptor: %d", fd)));
+ PG_RETURN_INT64(-1);
+ }
+
+ Assert(fscxt != NULL);
+ currentContext = MemoryContextSwitchTo(fscxt);
+
+ status = inv_seek(cookies[fd], offset, whence);
+
+ MemoryContextSwitchTo(currentContext);
+
+ PG_RETURN_INT64(status);
+ }
+
Datum
lo_creat(PG_FUNCTION_ARGS)
{
*************** Datum
*** 264,276 ****
lo_tell(PG_FUNCTION_ARGS)
{
int32 fd = PG_GETARG_INT32(0);
if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("invalid large-object descriptor: %d", fd)));
! PG_RETURN_INT32(inv_tell(cookies[fd]));
}
Datum
--- 301,346 ----
lo_tell(PG_FUNCTION_ARGS)
{
int32 fd = PG_GETARG_INT32(0);
+ int64 offset = 0;
if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("invalid large-object descriptor: %d", fd)));
! offset = inv_tell(cookies[fd]);
!
! if (INT_MAX < offset)
! {
! ereport(ERROR,
! (errcode(ERRCODE_BLOB_OFFSET_OVERFLOW),
! errmsg("offset overflow: %d", fd)));
! PG_RETURN_INT32(-1);
! }
!
! PG_RETURN_INT32(offset);
! }
!
!
! Datum
! lo_tell64(PG_FUNCTION_ARGS)
! {
! int32 fd = PG_GETARG_INT32(0);
!
! if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
! {
! ereport(ERROR,
! (errcode(ERRCODE_UNDEFINED_OBJECT),
! errmsg("invalid large-object descriptor: %d", fd)));
! PG_RETURN_INT64(-1);
! }
!
! /*
! * We assume we do not need to switch contexts for inv_tell. That is
! * true for now, but is probably more than this module ought to
! * assume...
! */
! PG_RETURN_INT64(inv_tell(cookies[fd]));
}
Datum
*************** lo_truncate(PG_FUNCTION_ARGS)
*** 533,538 ****
--- 603,635 ----
PG_RETURN_INT32(0);
}
+ Datum
+ lo_truncate64(PG_FUNCTION_ARGS)
+ {
+ int32 fd = PG_GETARG_INT32(0);
+ int64 len = PG_GETARG_INT64(1);
+
+ if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("invalid large-object descriptor: %d", fd)));
+
+ /* Permission checks */
+ if (!lo_compat_privileges &&
+ pg_largeobject_aclcheck_snapshot(cookies[fd]->id,
+ GetUserId(),
+ ACL_UPDATE,
+ cookies[fd]->snapshot) != ACLCHECK_OK)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied for large object %u",
+ cookies[fd]->id)));
+
+ inv_truncate(cookies[fd], len);
+
+ PG_RETURN_INT32(0);
+ }
+
/*
* AtEOXact_LargeObject -
* prepares large objects for transaction commit
diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c
new file mode 100644
index 3adfb15..3f5688b
*** a/src/backend/storage/large_object/inv_api.c
--- b/src/backend/storage/large_object/inv_api.c
*************** inv_drop(Oid lobjId)
*** 324,333 ****
* NOTE: LOs can contain gaps, just like Unix files. We actually return
* the offset of the last byte + 1.
*/
! static uint32
inv_getsize(LargeObjectDesc *obj_desc)
{
! uint32 lastbyte = 0;
ScanKeyData skey[1];
SysScanDesc sd;
HeapTuple tuple;
--- 324,333 ----
* NOTE: LOs can contain gaps, just like Unix files. We actually return
* the offset of the last byte + 1.
*/
! static uint64
inv_getsize(LargeObjectDesc *obj_desc)
{
! uint64 lastbyte = 0;
ScanKeyData skey[1];
SysScanDesc sd;
HeapTuple tuple;
*************** inv_getsize(LargeObjectDesc *obj_desc)
*** 368,374 ****
heap_tuple_untoast_attr((struct varlena *) datafield);
pfreeit = true;
}
! lastbyte = data->pageno * LOBLKSIZE + getbytealen(datafield);
if (pfreeit)
pfree(datafield);
}
--- 368,374 ----
heap_tuple_untoast_attr((struct varlena *) datafield);
pfreeit = true;
}
! lastbyte = (uint64) data->pageno * LOBLKSIZE + getbytealen(datafield);
if (pfreeit)
pfree(datafield);
}
*************** inv_getsize(LargeObjectDesc *obj_desc)
*** 378,407 ****
return lastbyte;
}
! int
! inv_seek(LargeObjectDesc *obj_desc, int offset, int whence)
{
Assert(PointerIsValid(obj_desc));
switch (whence)
{
case SEEK_SET:
! if (offset < 0)
! elog(ERROR, "invalid seek offset: %d", offset);
obj_desc->offset = offset;
break;
case SEEK_CUR:
! if (offset < 0 && obj_desc->offset < ((uint32) (-offset)))
! elog(ERROR, "invalid seek offset: %d", offset);
obj_desc->offset += offset;
break;
case SEEK_END:
{
! uint32 size = inv_getsize(obj_desc);
! if (offset < 0 && size < ((uint32) (-offset)))
! elog(ERROR, "invalid seek offset: %d", offset);
! obj_desc->offset = size + offset;
}
break;
default:
--- 378,408 ----
return lastbyte;
}
! int64
! inv_seek(LargeObjectDesc *obj_desc, int64 offset, int whence)
{
Assert(PointerIsValid(obj_desc));
switch (whence)
{
case SEEK_SET:
! if (offset < 0 || offset >= MAX_LARGE_OBJECT_SIZE)
! elog(ERROR, "invalid seek offset: " INT64_FORMAT, offset);
obj_desc->offset = offset;
break;
case SEEK_CUR:
! if ((offset + obj_desc->offset) < 0 ||
! (offset + obj_desc->offset) >= MAX_LARGE_OBJECT_SIZE)
! elog(ERROR, "invalid seek offset: " INT64_FORMAT, offset);
obj_desc->offset += offset;
break;
case SEEK_END:
{
! int64 pos = inv_getsize(obj_desc) + offset;
! if (pos < 0 || pos >= MAX_LARGE_OBJECT_SIZE)
! elog(ERROR, "invalid seek offset: " INT64_FORMAT, offset);
! obj_desc->offset = pos;
}
break;
default:
*************** inv_seek(LargeObjectDesc *obj_desc, int
*** 410,416 ****
return obj_desc->offset;
}
! int
inv_tell(LargeObjectDesc *obj_desc)
{
Assert(PointerIsValid(obj_desc));
--- 411,417 ----
return obj_desc->offset;
}
! int64
inv_tell(LargeObjectDesc *obj_desc)
{
Assert(PointerIsValid(obj_desc));
*************** int
*** 422,432 ****
inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
{
int nread = 0;
! int n;
! int off;
int len;
int32 pageno = (int32) (obj_desc->offset / LOBLKSIZE);
! uint32 pageoff;
ScanKeyData skey[2];
SysScanDesc sd;
HeapTuple tuple;
--- 423,433 ----
inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
{
int nread = 0;
! int64 n;
! int64 off;
int len;
int32 pageno = (int32) (obj_desc->offset / LOBLKSIZE);
! uint64 pageoff;
ScanKeyData skey[2];
SysScanDesc sd;
HeapTuple tuple;
*************** inv_read(LargeObjectDesc *obj_desc, char
*** 437,442 ****
--- 438,446 ----
if (nbytes <= 0)
return 0;
+ if ((nbytes + obj_desc->offset) > MAX_LARGE_OBJECT_SIZE)
+ elog(ERROR, "invalid read request size: %d", nbytes);
+
open_lo_relation();
ScanKeyInit(&skey[0],
*************** inv_read(LargeObjectDesc *obj_desc, char
*** 467,473 ****
* there may be missing pages if the LO contains unwritten "holes". We
* want missing sections to read out as zeroes.
*/
! pageoff = ((uint32) data->pageno) * LOBLKSIZE;
if (pageoff > obj_desc->offset)
{
n = pageoff - obj_desc->offset;
--- 471,477 ----
* there may be missing pages if the LO contains unwritten "holes". We
* want missing sections to read out as zeroes.
*/
! pageoff = ((uint64) data->pageno) * LOBLKSIZE;
if (pageoff > obj_desc->offset)
{
n = pageoff - obj_desc->offset;
*************** inv_write(LargeObjectDesc *obj_desc, con
*** 560,565 ****
--- 564,572 ----
if (nbytes <= 0)
return 0;
+ if ((nbytes + obj_desc->offset) > MAX_LARGE_OBJECT_SIZE)
+ elog(ERROR, "invalid write request size: %d", nbytes);
+
open_lo_relation();
indstate = CatalogOpenIndexes(lo_heap_r);
*************** inv_write(LargeObjectDesc *obj_desc, con
*** 718,727 ****
}
void
! inv_truncate(LargeObjectDesc *obj_desc, int len)
{
int32 pageno = (int32) (len / LOBLKSIZE);
! int off;
ScanKeyData skey[2];
SysScanDesc sd;
HeapTuple oldtuple;
--- 725,734 ----
}
void
! inv_truncate(LargeObjectDesc *obj_desc, int64 len)
{
int32 pageno = (int32) (len / LOBLKSIZE);
! int32 off;
ScanKeyData skey[2];
SysScanDesc sd;
HeapTuple oldtuple;
diff --git a/src/backend/utils/errcodes.txt b/src/backend/utils/errcodes.txt
new file mode 100644
index 3e04164..db8ab53
*** a/src/backend/utils/errcodes.txt
--- b/src/backend/utils/errcodes.txt
*************** Section: Class 22 - Data Exception
*** 199,204 ****
--- 199,205 ----
2200N E ERRCODE_INVALID_XML_CONTENT invalid_xml_content
2200S E ERRCODE_INVALID_XML_COMMENT invalid_xml_comment
2200T E ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION invalid_xml_processing_instruction
+ 22P07 E ERRCODE_BLOB_OFFSET_OVERFLOW blob_offset_overflow
Section: Class 23 - Integrity Constraint Violation
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
new file mode 100644
index 77a3b41..a2da836
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DATA(insert OID = 955 ( lowrite PGN
*** 1040,1053 ****
--- 1040,1059 ----
DESCR("large object write");
DATA(insert OID = 956 ( lo_lseek PGNSP PGUID 12 1 0 0 0 f f f f t f v 3 0 23 "23 23 23" _null_ _null_ _null_ _null_ lo_lseek _null_ _null_ _null_ ));
DESCR("large object seek");
+ DATA(insert OID = 3170 ( lo_lseek64 PGNSP PGUID 12 1 0 0 0 f f f f t f v 3 0 20 "23 20 23" _null_ _null_ _null_ _null_ lo_lseek64 _null_ _null_ _null_ ));
+ DESCR("large object seek (64 bit)");
DATA(insert OID = 957 ( lo_creat PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 26 "23" _null_ _null_ _null_ _null_ lo_creat _null_ _null_ _null_ ));
DESCR("large object create");
DATA(insert OID = 715 ( lo_create PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 26 "26" _null_ _null_ _null_ _null_ lo_create _null_ _null_ _null_ ));
DESCR("large object create");
DATA(insert OID = 958 ( lo_tell PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 23 "23" _null_ _null_ _null_ _null_ lo_tell _null_ _null_ _null_ ));
DESCR("large object position");
+ DATA(insert OID = 3171 ( lo_tell64 PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 20 "23" _null_ _null_ _null_ _null_ lo_tell64 _null_ _null_ _null_ ));
+ DESCR("large object position (64 bit)");
DATA(insert OID = 1004 ( lo_truncate PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 23 "23 23" _null_ _null_ _null_ _null_ lo_truncate _null_ _null_ _null_ ));
DESCR("truncate large object");
+ DATA(insert OID = 3172 ( lo_truncate64 PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 23 "23 20" _null_ _null_ _null_ _null_ lo_truncate64 _null_ _null_ _null_ ));
+ DESCR("truncate large object (64 bit)");
DATA(insert OID = 959 ( on_pl PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "600 628" _null_ _null_ _null_ _null_ on_pl _null_ _null_ _null_ ));
DATA(insert OID = 960 ( on_sl PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "601 628" _null_ _null_ _null_ _null_ on_sl _null_ _null_ _null_ ));
diff --git a/src/include/libpq/be-fsstubs.h b/src/include/libpq/be-fsstubs.h
new file mode 100644
index 0c832da..d74ea0e
*** a/src/include/libpq/be-fsstubs.h
--- b/src/include/libpq/be-fsstubs.h
*************** extern Datum lowrite(PG_FUNCTION_ARGS);
*** 34,41 ****
--- 34,44 ----
extern Datum lo_lseek(PG_FUNCTION_ARGS);
extern Datum lo_tell(PG_FUNCTION_ARGS);
+ extern Datum lo_lseek64(PG_FUNCTION_ARGS);
+ extern Datum lo_tell64(PG_FUNCTION_ARGS);
extern Datum lo_unlink(PG_FUNCTION_ARGS);
extern Datum lo_truncate(PG_FUNCTION_ARGS);
+ extern Datum lo_truncate64(PG_FUNCTION_ARGS);
/*
* compatibility option for access control
diff --git a/src/include/postgres_ext.h b/src/include/postgres_ext.h
new file mode 100644
index b6ebb7a..76502de
*** a/src/include/postgres_ext.h
--- b/src/include/postgres_ext.h
*************** typedef unsigned int Oid;
*** 56,59 ****
--- 56,64 ----
#define PG_DIAG_SOURCE_LINE 'L'
#define PG_DIAG_SOURCE_FUNCTION 'R'
+ #ifndef NO_PG_INT64
+ #define HAVE_PG_INT64 1
+ typedef long long int pg_int64;
+ #endif
+
#endif
diff --git a/src/include/storage/large_object.h b/src/include/storage/large_object.h
new file mode 100644
index 1fe07ee..52f01c6
*** a/src/include/storage/large_object.h
--- b/src/include/storage/large_object.h
*************** typedef struct LargeObjectDesc
*** 37,43 ****
Oid id; /* LO's identifier */
Snapshot snapshot; /* snapshot to use */
SubTransactionId subid; /* owning subtransaction ID */
! uint32 offset; /* current seek pointer */
int flags; /* locking info, etc */
/* flag bits: */
--- 37,43 ----
Oid id; /* LO's identifier */
Snapshot snapshot; /* snapshot to use */
SubTransactionId subid; /* owning subtransaction ID */
! uint64 offset; /* current seek pointer */
int flags; /* locking info, etc */
/* flag bits: */
*************** typedef struct LargeObjectDesc
*** 62,68 ****
* This avoids unnecessary tuple updates caused by partial-page writes.
*/
#define LOBLKSIZE (BLCKSZ / 4)
!
/*
* Function definitions...
--- 62,71 ----
* This avoids unnecessary tuple updates caused by partial-page writes.
*/
#define LOBLKSIZE (BLCKSZ / 4)
! /*
! * Maximum byte length for each large object
! */
! #define MAX_LARGE_OBJECT_SIZE INT64CONST(INT_MAX * LOBLKSIZE)
/*
* Function definitions...
*************** extern Oid inv_create(Oid lobjId);
*** 74,83 ****
extern LargeObjectDesc *inv_open(Oid lobjId, int flags, MemoryContext mcxt);
extern void inv_close(LargeObjectDesc *obj_desc);
extern int inv_drop(Oid lobjId);
! extern int inv_seek(LargeObjectDesc *obj_desc, int offset, int whence);
! extern int inv_tell(LargeObjectDesc *obj_desc);
extern int inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes);
extern int inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes);
! extern void inv_truncate(LargeObjectDesc *obj_desc, int len);
#endif /* LARGE_OBJECT_H */
--- 77,86 ----
extern LargeObjectDesc *inv_open(Oid lobjId, int flags, MemoryContext mcxt);
extern void inv_close(LargeObjectDesc *obj_desc);
extern int inv_drop(Oid lobjId);
! extern int64 inv_seek(LargeObjectDesc *obj_desc, int64 offset, int whence);
! extern int64 inv_tell(LargeObjectDesc *obj_desc);
extern int inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes);
extern int inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes);
! extern void inv_truncate(LargeObjectDesc *obj_desc, int64 len);
#endif /* LARGE_OBJECT_H */
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
new file mode 100644
index 9d95e26..56d0bb8
*** a/src/interfaces/libpq/exports.txt
--- b/src/interfaces/libpq/exports.txt
*************** PQping 158
*** 161,163 ****
--- 161,166 ----
PQpingParams 159
PQlibVersion 160
PQsetSingleRowMode 161
+ lo_lseek64 162
+ lo_tell64 163
+ lo_truncate64 164
diff --git a/src/interfaces/libpq/fe-lobj.c b/src/interfaces/libpq/fe-lobj.c
new file mode 100644
index f3a6d03..fb17ac8
*** a/src/interfaces/libpq/fe-lobj.c
--- b/src/interfaces/libpq/fe-lobj.c
***************
*** 37,46 ****
--- 37,52 ----
#include "libpq-int.h"
#include "libpq/libpq-fs.h" /* must come after sys/stat.h */
+ /* for ntohl/htonl */
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+
#define LO_BUFSIZE 8192
static int lo_initialize(PGconn *conn);
static Oid lo_import_internal(PGconn *conn, const char *filename, Oid oid);
+ static pg_int64 lo_hton64(pg_int64 host64);
+ static pg_int64 lo_ntoh64(pg_int64 net64);
/*
* lo_open
*************** lo_truncate(PGconn *conn, int fd, size_t
*** 174,179 ****
--- 180,238 ----
}
}
+ /*
+ * lo_truncate64
+ * truncates an existing large object to the given size
+ *
+ * returns 0 upon success
+ * returns -1 upon failure
+ */
+ #ifdef HAVE_PG_INT64
+ int
+ lo_truncate64(PGconn *conn, int fd, pg_int64 len)
+ {
+ PQArgBlock argv[2];
+ PGresult *res;
+ int retval;
+ int result_len;
+
+ if (conn == NULL || conn->lobjfuncs == NULL)
+ {
+ if (lo_initialize(conn) < 0)
+ return -1;
+ }
+
+ if (conn->lobjfuncs->fn_lo_truncate64 == 0)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("cannot determine OID of function lo_truncate64\n"));
+ return -1;
+ }
+
+ argv[0].isint = 1;
+ argv[0].len = 4;
+ argv[0].u.integer = fd;
+
+ len = lo_hton64(len);
+ argv[1].isint = 0;
+ argv[1].len = 8;
+ argv[1].u.ptr = (int *) &len;
+
+ res = PQfn(conn, conn->lobjfuncs->fn_lo_truncate64,
+ &retval, &result_len, 1, argv, 2);
+
+ if (PQresultStatus(res) == PGRES_COMMAND_OK)
+ {
+ PQclear(res);
+ return retval;
+ }
+ else
+ {
+ PQclear(res);
+ return -1;
+ }
+ }
+ #endif
/*
* lo_read
*************** lo_lseek(PGconn *conn, int fd, int offse
*** 311,316 ****
--- 370,432 ----
}
/*
+ * lo_lseek64
+ * change the current read or write location on a large object
+ * currently, only L_SET is a legal value for whence
+ *
+ */
+
+ #ifdef HAVE_PG_INT64
+ pg_int64
+ lo_lseek64(PGconn *conn, int fd, pg_int64 offset, int whence)
+ {
+ PQArgBlock argv[3];
+ PGresult *res;
+ pg_int64 retval;
+ int result_len;
+
+ if (conn == NULL || conn->lobjfuncs == NULL)
+ {
+ if (lo_initialize(conn) < 0)
+ return -1;
+ }
+
+ if (conn->lobjfuncs->fn_lo_lseek64 == 0)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("cannot determine OID of function lo_lseek64\n"));
+ return -1;
+ }
+
+ argv[0].isint = 1;
+ argv[0].len = 4;
+ argv[0].u.integer = fd;
+
+ offset = lo_hton64(offset);
+ argv[1].isint = 0;
+ argv[1].len = 8;
+ argv[1].u.ptr = (int *) &offset;
+
+ argv[2].isint = 1;
+ argv[2].len = 4;
+ argv[2].u.integer = whence;
+
+ res = PQfn(conn, conn->lobjfuncs->fn_lo_lseek64,
+ (int *)&retval, &result_len, 0, argv, 3);
+ if (PQresultStatus(res) == PGRES_COMMAND_OK)
+ {
+ PQclear(res);
+ return lo_ntoh64((pg_int64)retval);
+ }
+ else
+ {
+ PQclear(res);
+ return -1;
+ }
+ }
+ #endif
+
+ /*
* lo_creat
* create a new large object
* the mode is ignored (once upon a time it had a use)
*************** lo_tell(PGconn *conn, int fd)
*** 436,441 ****
--- 552,603 ----
}
/*
+ * lo_tell64
+ * returns the current seek location of the large object
+ *
+ */
+ #ifdef HAVE_PG_INT64
+ pg_int64
+ lo_tell64(PGconn *conn, int fd)
+ {
+ pg_int64 retval;
+ PQArgBlock argv[1];
+ PGresult *res;
+ int result_len;
+
+ if (conn == NULL || conn->lobjfuncs == NULL)
+ {
+ if (lo_initialize(conn) < 0)
+ return -1;
+ }
+
+ if (conn->lobjfuncs->fn_lo_tell64 == 0)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("cannot determine OID of function lo_tell64\n"));
+ return -1;
+ }
+
+ argv[0].isint = 1;
+ argv[0].len = 4;
+ argv[0].u.integer = fd;
+
+ res = PQfn(conn, conn->lobjfuncs->fn_lo_tell64,
+ (int *) &retval, &result_len, 0, argv, 1);
+ if (PQresultStatus(res) == PGRES_COMMAND_OK)
+ {
+ PQclear(res);
+ return lo_ntoh64((pg_int64) retval);
+ }
+ else
+ {
+ PQclear(res);
+ return -1;
+ }
+ }
+ #endif
+
+ /*
* lo_unlink
* delete a file
*
*************** lo_initialize(PGconn *conn)
*** 713,720 ****
--- 875,885 ----
"'lo_create', "
"'lo_unlink', "
"'lo_lseek', "
+ "'lo_lseek64', "
"'lo_tell', "
+ "'lo_tell64', "
"'lo_truncate', "
+ "'lo_truncate64', "
"'loread', "
"'lowrite') "
"and pronamespace = (select oid from pg_catalog.pg_namespace "
*************** lo_initialize(PGconn *conn)
*** 765,774 ****
--- 930,945 ----
lobjfuncs->fn_lo_unlink = foid;
else if (strcmp(fname, "lo_lseek") == 0)
lobjfuncs->fn_lo_lseek = foid;
+ else if (strcmp(fname, "lo_lseek64") == 0)
+ lobjfuncs->fn_lo_lseek64 = foid;
else if (strcmp(fname, "lo_tell") == 0)
lobjfuncs->fn_lo_tell = foid;
+ else if (strcmp(fname, "lo_tell64") == 0)
+ lobjfuncs->fn_lo_tell64 = foid;
else if (strcmp(fname, "lo_truncate") == 0)
lobjfuncs->fn_lo_truncate = foid;
+ else if (strcmp(fname, "lo_truncate64") == 0)
+ lobjfuncs->fn_lo_truncate64 = foid;
else if (strcmp(fname, "loread") == 0)
lobjfuncs->fn_lo_read = foid;
else if (strcmp(fname, "lowrite") == 0)
*************** lo_initialize(PGconn *conn)
*** 836,845 ****
free(lobjfuncs);
return -1;
}
!
/*
* Put the structure into the connection control
*/
conn->lobjfuncs = lobjfuncs;
return 0;
}
--- 1007,1082 ----
free(lobjfuncs);
return -1;
}
! if (conn->sversion >= 90300)
! {
! if (lobjfuncs->fn_lo_lseek64 == 0)
! {
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("cannot determine OID of function lo_lseek64\n"));
! free(lobjfuncs);
! return -1;
! }
! if (lobjfuncs->fn_lo_tell64 == 0)
! {
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("cannot determine OID of function lo_tell64\n"));
! free(lobjfuncs);
! return -1;
! }
! if (lobjfuncs->fn_lo_truncate64 == 0)
! {
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("cannot determine OID of function lo_truncate64\n"));
! free(lobjfuncs);
! return -1;
! }
! }
/*
* Put the structure into the connection control
*/
conn->lobjfuncs = lobjfuncs;
return 0;
}
+
+ /*
+ * lo_hton64
+ * converts an 64-bit integer from host byte order to network byte order
+ */
+ static pg_int64
+ lo_hton64(pg_int64 host64)
+ {
+ pg_int64 result;
+ uint32_t h32, l32;
+
+ /* High order half first, since we're doing MSB-first */
+ h32 = (uint32_t) (host64 >> 32);
+
+ /* Now the low order half */
+ l32 = (uint32_t) (host64 & 0xffffffff);
+
+ result = htonl(l32);
+ result <<= 32;
+ result |= htonl(h32);
+
+ return result;
+ }
+
+ /*
+ * lo_ntoh64
+ * converts an 64-bit integer from network byte order to host byte order
+ */
+ static pg_int64
+ lo_ntoh64(pg_int64 net64)
+ {
+ pg_int64 result;
+ uint32_t h32, l32;
+
+ l32 = (uint32_t) (net64 >> 32);
+ h32 = (uint32_t) (net64 & 0xffffffff);
+
+ result = ntohl(h32);
+ result <<= 32;
+ result |= ntohl(l32);
+
+ return result;
+ }
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
new file mode 100644
index 9d05dd2..73568ca
*** a/src/interfaces/libpq/libpq-fe.h
--- b/src/interfaces/libpq/libpq-fe.h
*************** extern Oid lo_import(PGconn *conn, const
*** 548,553 ****
--- 548,559 ----
extern Oid lo_import_with_oid(PGconn *conn, const char *filename, Oid lobjId);
extern int lo_export(PGconn *conn, Oid lobjId, const char *filename);
+ #ifdef HAVE_PG_INT64
+ extern pg_int64 lo_lseek64(PGconn *conn, int fd, pg_int64 offset, int whence);
+ extern pg_int64 lo_tell64(PGconn *conn, int fd);
+ extern int lo_truncate64(PGconn *conn, int fd, pg_int64 len);
+ #endif
+
/* === in fe-misc.c === */
/* Get the version of the libpq library in use */
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
new file mode 100644
index 4a6c8fe..375821e
*** a/src/interfaces/libpq/libpq-int.h
--- b/src/interfaces/libpq/libpq-int.h
*************** typedef struct pgLobjfuncs
*** 271,278 ****
--- 271,281 ----
Oid fn_lo_create; /* OID of backend function lo_create */
Oid fn_lo_unlink; /* OID of backend function lo_unlink */
Oid fn_lo_lseek; /* OID of backend function lo_lseek */
+ Oid fn_lo_lseek64; /* OID of backend function lo_lseek64 */
Oid fn_lo_tell; /* OID of backend function lo_tell */
+ Oid fn_lo_tell64; /* OID of backend function lo_tell64 */
Oid fn_lo_truncate; /* OID of backend function lo_truncate */
+ Oid fn_lo_truncate64; /* OID of backend function lo_truncate64 */
Oid fn_lo_read; /* OID of backend function LOread */
Oid fn_lo_write; /* OID of backend function LOwrite */
} PGlobjfuncs;
diff --git a/src/test/examples/Makefile b/src/test/examples/Makefile
new file mode 100644
index bbc6ee1..aee5c04
*** a/src/test/examples/Makefile
--- b/src/test/examples/Makefile
*************** override CPPFLAGS := -I$(libpq_srcdir) $
*** 14,20 ****
override LDLIBS := $(libpq_pgport) $(LDLIBS)
! PROGS = testlibpq testlibpq2 testlibpq3 testlibpq4 testlo
all: $(PROGS)
--- 14,20 ----
override LDLIBS := $(libpq_pgport) $(LDLIBS)
! PROGS = testlibpq testlibpq2 testlibpq3 testlibpq4 testlo testlo64
all: $(PROGS)
diff --git a/src/test/regress/input/largeobject.source b/src/test/regress/input/largeobject.source
new file mode 100644
index 40f40f8..4984d78
*** a/src/test/regress/input/largeobject.source
--- b/src/test/regress/input/largeobject.source
*************** SELECT lo_tell(fd) FROM lotest_stash_val
*** 125,130 ****
--- 125,153 ----
SELECT lo_close(fd) FROM lotest_stash_values;
END;
+ -- Test 64-bit largelbject functions.
+ BEGIN;
+ UPDATE lotest_stash_values SET fd = lo_open(loid, CAST(x'20000' | x'40000' AS integer));
+
+ SELECT lo_lseek64(fd, 4294967296, 0) FROM lotest_stash_values;
+ SELECT lowrite(fd, 'offset:4GB') FROM lotest_stash_values;
+ SELECT lo_tell64(fd) FROM lotest_stash_values;
+
+ SELECT lo_lseek64(fd, -10, 1) FROM lotest_stash_values;
+ SELECT lo_tell64(fd) FROM lotest_stash_values;
+ SELECT loread(fd, 10) FROM lotest_stash_values;
+
+ SELECT lo_truncate64(fd, 5000000000) FROM lotest_stash_values;
+ SELECT lo_lseek64(fd, 0, 2) FROM lotest_stash_values;
+ SELECT lo_tell64(fd) FROM lotest_stash_values;
+
+ SELECT lo_truncate64(fd, 3000000000) FROM lotest_stash_values;
+ SELECT lo_lseek64(fd, 0, 2) FROM lotest_stash_values;
+ SELECT lo_tell64(fd) FROM lotest_stash_values;
+
+ SELECT lo_close(fd) FROM lotest_stash_values;
+ END;
+
-- lo_unlink(lobjId oid) returns integer
-- return value appears to always be 1
SELECT lo_unlink(loid) from lotest_stash_values;
diff --git a/src/test/regress/output/largeobject.source b/src/test/regress/output/largeobject.source
new file mode 100644
index 55aaf8f..74c4772
*** a/src/test/regress/output/largeobject.source
--- b/src/test/regress/output/largeobject.source
*************** SELECT lo_close(fd) FROM lotest_stash_va
*** 210,215 ****
--- 210,297 ----
(1 row)
END;
+ -- Test 64-bit largelbject functions.
+ BEGIN;
+ UPDATE lotest_stash_values SET fd = lo_open(loid, CAST(x'20000' | x'40000' AS integer));
+ SELECT lo_lseek64(fd, 4294967296, 0) FROM lotest_stash_values;
+ lo_lseek64
+ ------------
+ 4294967296
+ (1 row)
+
+ SELECT lowrite(fd, 'offset:4GB') FROM lotest_stash_values;
+ lowrite
+ ---------
+ 10
+ (1 row)
+
+ SELECT lo_tell64(fd) FROM lotest_stash_values;
+ lo_tell64
+ ------------
+ 4294967306
+ (1 row)
+
+ SELECT lo_lseek64(fd, -10, 1) FROM lotest_stash_values;
+ lo_lseek64
+ ------------
+ 4294967296
+ (1 row)
+
+ SELECT lo_tell64(fd) FROM lotest_stash_values;
+ lo_tell64
+ ------------
+ 4294967296
+ (1 row)
+
+ SELECT loread(fd, 10) FROM lotest_stash_values;
+ loread
+ ------------
+ offset:4GB
+ (1 row)
+
+ SELECT lo_truncate64(fd, 5000000000) FROM lotest_stash_values;
+ lo_truncate64
+ ---------------
+ 0
+ (1 row)
+
+ SELECT lo_lseek64(fd, 0, 2) FROM lotest_stash_values;
+ lo_lseek64
+ ------------
+ 5000000000
+ (1 row)
+
+ SELECT lo_tell64(fd) FROM lotest_stash_values;
+ lo_tell64
+ ------------
+ 5000000000
+ (1 row)
+
+ SELECT lo_truncate64(fd, 3000000000) FROM lotest_stash_values;
+ lo_truncate64
+ ---------------
+ 0
+ (1 row)
+
+ SELECT lo_lseek64(fd, 0, 2) FROM lotest_stash_values;
+ lo_lseek64
+ ------------
+ 3000000000
+ (1 row)
+
+ SELECT lo_tell64(fd) FROM lotest_stash_values;
+ lo_tell64
+ ------------
+ 3000000000
+ (1 row)
+
+ SELECT lo_close(fd) FROM lotest_stash_values;
+ lo_close
+ ----------
+ 0
+ (1 row)
+
+ END;
-- lo_unlink(lobjId oid) returns integer
-- return value appears to always be 1
SELECT lo_unlink(loid) from lotest_stash_values;