#include "postgres.h"
#include <fcntl.h>
-#include <limits.h>
#include <sys/stat.h>
#include <unistd.h>
*/
bool lo_compat_privileges;
-/*#define FSDB 1*/
+/* define this to enable debug logging */
+/* #define FSDB 1 */
+/* chunk size for lo_import/lo_export transfers */
#define BUFSIZE 8192
/*
return status;
}
-
Datum
lo_lseek(PG_FUNCTION_ARGS)
{
status = inv_seek(cookies[fd], offset, whence);
- if (INT_MAX < status)
- {
+ /* guard against result overflow */
+ if (status != (int32) status)
ereport(ERROR,
- (errcode(ERRCODE_BLOB_OFFSET_OVERFLOW),
- errmsg("offset overflow: %d", fd)));
- PG_RETURN_INT32(-1);
- }
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("lo_lseek result out of range for large-object descriptor %d",
+ fd)));
- PG_RETURN_INT32(status);
+ PG_RETURN_INT32((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;
+ 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);
}
lo_tell(PG_FUNCTION_ARGS)
{
int32 fd = PG_GETARG_INT32(0);
- int64 offset = 0;
+ int64 offset;
if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
ereport(ERROR,
offset = inv_tell(cookies[fd]);
- if (INT_MAX < offset)
- {
+ /* guard against result overflow */
+ if (offset != (int32) offset)
ereport(ERROR,
- (errcode(ERRCODE_BLOB_OFFSET_OVERFLOW),
- errmsg("offset overflow: %d", fd)));
- PG_RETURN_INT32(-1);
- }
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("lo_tell result out of range for large-object descriptor %d",
+ fd)));
- PG_RETURN_INT32(offset);
+ PG_RETURN_INT32((int32) offset);
}
-
Datum
lo_tell64(PG_FUNCTION_ARGS)
{
int32 fd = PG_GETARG_INT32(0);
+ int64 offset;
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]));
+ offset = inv_tell(cookies[fd]);
+
+ PG_RETURN_INT64(offset);
}
Datum
*/
#include "postgres.h"
+#include <limits.h>
+
#include "access/genam.h"
#include "access/heapam.h"
#include "access/sysattr.h"
retval->flags = IFS_RDLOCK;
}
else
- elog(ERROR, "invalid flags: %d", flags);
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid flags for opening a large object: %d",
+ flags)));
/* Can't use LargeObjectExists here because it always uses SnapshotNow */
if (!myLargeObjectExists(lobjId, retval->snapshot))
int64
inv_seek(LargeObjectDesc *obj_desc, int64 offset, int whence)
{
+ int64 newoffset;
+
Assert(PointerIsValid(obj_desc));
+ /*
+ * Note: overflow in the additions is possible, but since we will reject
+ * negative results, we don't need any extra test for that.
+ */
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;
+ newoffset = 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;
+ newoffset = 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;
- }
+ newoffset = inv_getsize(obj_desc) + offset;
break;
default:
- elog(ERROR, "invalid whence: %d", whence);
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid whence setting: %d", whence)));
+ newoffset = 0; /* keep compiler quiet */
+ break;
}
- return obj_desc->offset;
+
+ /*
+ * use errmsg_internal here because we don't want to expose INT64_FORMAT
+ * in translatable strings; doing better is not worth the trouble
+ */
+ if (newoffset < 0 || newoffset > MAX_LARGE_OBJECT_SIZE)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg_internal("invalid large object seek target: " INT64_FORMAT,
+ newoffset)));
+
+ obj_desc->offset = newoffset;
+ return newoffset;
}
int64
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],
if (!LargeObjectExists(obj_desc->id))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("large object %u was already dropped", obj_desc->id)));
+ errmsg("large object %u was already dropped",
+ obj_desc->id)));
if (nbytes <= 0)
return 0;
+ /* this addition can't overflow because nbytes is only int32 */
if ((nbytes + obj_desc->offset) > MAX_LARGE_OBJECT_SIZE)
- elog(ERROR, "invalid write request size: %d", nbytes);
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid large object write request size: %d",
+ nbytes)));
open_lo_relation();
if (!LargeObjectExists(obj_desc->id))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("large object %u was already dropped", obj_desc->id)));
+ errmsg("large object %u was already dropped",
+ obj_desc->id)));
+
+ /*
+ * use errmsg_internal here because we don't want to expose INT64_FORMAT
+ * in translatable strings; doing better is not worth the trouble
+ */
+ if (len < 0 || len > MAX_LARGE_OBJECT_SIZE)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg_internal("invalid large object truncation target: " INT64_FORMAT,
+ len)));
open_lo_relation();
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
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_ ));
+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");
#include <fcntl.h>
#include <sys/stat.h>
+#include <netinet/in.h> /* for ntohl/htonl */
+#include <arpa/inet.h>
#include "libpq-fe.h"
#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);
+static pg_int64 lo_hton64(pg_int64 host64);
+static pg_int64 lo_ntoh64(pg_int64 net64);
/*
* lo_open
{
PQArgBlock argv[3];
PGresult *res;
- pg_int64 retval;
+ pg_int64 retval;
int result_len;
if (conn == NULL || conn->lobjfuncs == NULL)
argv[2].u.integer = whence;
res = PQfn(conn, conn->lobjfuncs->fn_lo_lseek64,
- (int *)&retval, &result_len, 0, argv, 3);
+ (int *) &retval, &result_len, 0, argv, 3);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
PQclear(res);
- return lo_ntoh64((pg_int64)retval);
+ return lo_ntoh64(retval);
}
else
{
/*
* lo_tell
* returns the current seek location of the large object
- *
*/
-
int
lo_tell(PGconn *conn, int fd)
{
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
PQclear(res);
- return lo_ntoh64((pg_int64) retval);
+ return lo_ntoh64(retval);
}
else
{
PQclear(res);
/*
- * Finally check that we really got all large object interface functions
+ * Finally check that we got all required large object interface functions
+ * (ones that have been added later than the stone age are instead checked
+ * only if used)
*/
if (lobjfuncs->fn_lo_open == 0)
{
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
/*
* lo_hton64
- * converts an 64-bit integer from host byte order to network byte order
+ * converts a 64-bit integer from host byte order to network byte order
*/
static pg_int64
lo_hton64(pg_int64 host64)
{
- pg_int64 result;
- uint32 h32, l32;
+ union
+ {
+ pg_int64 i64;
+ uint32 i32[2];
+ } swap;
+ uint32 t;
/* High order half first, since we're doing MSB-first */
- h32 = (uint32) (host64 >> 32);
+ t = (uint32) (host64 >> 32);
+ swap.i32[0] = htonl(t);
/* Now the low order half */
- l32 = (uint32) (host64 & 0xffffffff);
-
- result = htonl(l32);
- result <<= 32;
- result |= htonl(h32);
+ t = (uint32) host64;
+ swap.i32[1] = htonl(t);
- return result;
+ return swap.i64;
}
/*
* lo_ntoh64
- * converts an 64-bit integer from network byte order to host byte order
+ * converts a 64-bit integer from network byte order to host byte order
*/
static pg_int64
lo_ntoh64(pg_int64 net64)
{
- pg_int64 result;
- uint32 h32, l32;
+ union
+ {
+ pg_int64 i64;
+ uint32 i32[2];
+ } swap;
+ pg_int64 result;
- l32 = (uint32) (net64 >> 32);
- h32 = (uint32) (net64 & 0xffffffff);
+ swap.i64 = net64;
- result = ntohl(h32);
+ result = (uint32) ntohl(swap.i32[0]);
result <<= 32;
- result |= ntohl(l32);
+ result |= (uint32) ntohl(swap.i32[1]);
return result;
}
* testlo64.c
* test using large objects with libpq using 64-bit APIs
*
- * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
fd = open(filename, O_RDONLY, 0666);
if (fd < 0)
{ /* error */
- fprintf(stderr, "can't open unix file\"%s\"\n", filename);
+ fprintf(stderr, "cannot open unix file\"%s\"\n", filename);
}
/*
*/
lobjId = lo_creat(conn, INV_READ | INV_WRITE);
if (lobjId == 0)
- fprintf(stderr, "can't create large object");
+ fprintf(stderr, "cannot create large object");
lobj_fd = lo_open(conn, lobjId, INV_WRITE);
char *buf;
int nbytes;
int nread;
- pg_int64 pos;
lobj_fd = lo_open(conn, lobjId, INV_READ);
if (lobj_fd < 0)
- fprintf(stderr, "can't open large object %u", lobjId);
+ fprintf(stderr, "cannot open large object %u", lobjId);
- if (lo_tell64(conn, lobj_fd) < 0)
- {
- fprintf(stderr, "error lo_tell64: %s\n", PQerrorMessage(conn));
- }
-
- if ((pos=lo_lseek64(conn, lobj_fd, start, SEEK_SET)) < 0)
- {
- fprintf(stderr, "error lo_lseek64: %s\n", PQerrorMessage(conn));
- return;
- }
+ if (lo_lseek64(conn, lobj_fd, start, SEEK_SET) < 0)
+ fprintf(stderr, "error in lo_lseek64: %s", PQerrorMessage(conn));
- fprintf(stderr, "before read: retval of lo_lseek64 : %lld\n", (long long int) pos);
+ if (lo_tell64(conn, lobj_fd) != start)
+ fprintf(stderr, "error in lo_tell64: %s", PQerrorMessage(conn));
buf = malloc(len + 1);
}
free(buf);
fprintf(stderr, "\n");
-
- pos = lo_tell64(conn, lobj_fd);
- fprintf(stderr, "after read: retval of lo_tell64 : %lld\n\n", (long long int) pos);
-
lo_close(conn, lobj_fd);
}
int nbytes;
int nwritten;
int i;
- pg_int64 pos;
- lobj_fd = lo_open(conn, lobjId, INV_READ | INV_WRITE);
+ lobj_fd = lo_open(conn, lobjId, INV_WRITE);
if (lobj_fd < 0)
- fprintf(stderr, "can't open large object %u", lobjId);
+ fprintf(stderr, "cannot open large object %u", lobjId);
- if ((pos=lo_lseek64(conn, lobj_fd, start, SEEK_SET)) < 0)
- {
- fprintf(stderr, "error lo_lseek64: %s\n", PQerrorMessage(conn));
- return;
- }
- fprintf(stderr, "before write: retval of lo_lseek64 : %lld\n", (long long int) pos);
+ if (lo_lseek64(conn, lobj_fd, start, SEEK_SET) < 0)
+ fprintf(stderr, "error in lo_lseek64: %s", PQerrorMessage(conn));
buf = malloc(len + 1);
}
}
free(buf);
-
- pos = lo_tell64(conn, lobj_fd);
- fprintf(stderr, "after write: retval of lo_tell64 : %lld\n\n", (long long int) pos);
-
+ fprintf(stderr, "\n");
lo_close(conn, lobj_fd);
}
static void
-my_truncate(PGconn *conn, Oid lobjId, size_t len)
+my_truncate(PGconn *conn, Oid lobjId, pg_int64 len)
{
int lobj_fd;
lobj_fd = lo_open(conn, lobjId, INV_READ | INV_WRITE);
if (lobj_fd < 0)
- fprintf(stderr, "can't open large object %u", lobjId);
+ fprintf(stderr, "cannot open large object %u", lobjId);
if (lo_truncate64(conn, lobj_fd, len) < 0)
- {
- fprintf(stderr, "error lo_truncate64: %s\n", PQerrorMessage(conn));
- return;
- }
+ fprintf(stderr, "error in lo_truncate64: %s", PQerrorMessage(conn));
-
- fprintf(stderr, "\n");
lo_close(conn, lobj_fd);
}
int fd;
/*
- * create an inversion "object"
+ * open the large object
*/
lobj_fd = lo_open(conn, lobjId, INV_READ);
if (lobj_fd < 0)
- fprintf(stderr, "can't open large object %u", lobjId);
+ fprintf(stderr, "cannot open large object %u", lobjId);
/*
* open the file to be written to
fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666);
if (fd < 0)
{ /* error */
- fprintf(stderr, "can't open unix file\"%s\"",
+ fprintf(stderr, "cannot open unix file\"%s\"",
filename);
}
/*
- * read in from the Unix file and write to the inversion file
+ * read in from the inversion file and write to the Unix file
*/
while ((nbytes = lo_read(conn, lobj_fd, buf, BUFSIZE)) > 0)
{
printf("\tas large object %u.\n", lobjOid);
printf("picking out bytes 4294967000-4294968000 of the large object\n");
- pickout(conn, lobjOid, 4294967000ULL, 1000);
+ pickout(conn, lobjOid, 4294967000U, 1000);
printf("overwriting bytes 4294967000-4294968000 of the large object with X's\n");
- overwrite(conn, lobjOid, 4294967000ULL, 1000);
-
+ overwrite(conn, lobjOid, 4294967000U, 1000);
printf("exporting large object to file \"%s\" ...\n", out_filename);
/* exportFile(conn, lobjOid, out_filename); */
if (!lo_export(conn, lobjOid, out_filename))
fprintf(stderr, "%s\n", PQerrorMessage(conn));
- printf("truncating to 3294968000 byte\n");
- my_truncate(conn, lobjOid, 3294968000ULL);
+ printf("truncating to 3294968000 bytes\n");
+ my_truncate(conn, lobjOid, 3294968000U);
printf("exporting truncated large object to file \"%s\" ...\n", out_filename2);
if (!lo_export(conn, lobjOid, out_filename2))
fprintf(stderr, "%s\n", PQerrorMessage(conn));
-
}
res = PQexec(conn, "end");
SELECT lo_close(fd) FROM lotest_stash_values;
END;
--- Test 64-bit largelbject functions.
+-- Test 64-bit large object functions.
BEGIN;
UPDATE lotest_stash_values SET fd = lo_open(loid, CAST(x'20000' | x'40000' AS integer));
(1 row)
END;
--- Test 64-bit largelbject functions.
+-- Test 64-bit large object 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;
0
(1 row)
+END;
+-- Test 64-bit large object 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