From 26fe56481c0f7baa705f0b3265b5a0676f894a94 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 8 Oct 2012 18:24:06 -0400 Subject: [PATCH] Code review for 64-bit-large-object patch. Fix broken-on-bigendian-machines byte-swapping functions, add missed update of alternate regression expected file, improve error reporting, remove some unnecessary code, sync testlo64.c with current testlo.c (it seems to have been cloned from a very old copy of that), assorted cosmetic improvements. --- src/backend/libpq/be-fsstubs.c | 60 +++++--------- src/backend/storage/large_object/inv_api.c | 75 +++++++++++------ src/backend/utils/errcodes.txt | 1 - src/include/catalog/pg_proc.h | 2 +- src/interfaces/libpq/fe-lobj.c | 87 ++++++++------------ src/test/examples/testlo64.c | 75 ++++++----------- src/test/regress/input/largeobject.source | 2 +- src/test/regress/output/largeobject.source | 2 +- src/test/regress/output/largeobject_1.source | 82 ++++++++++++++++++ 9 files changed, 215 insertions(+), 171 deletions(-) diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c index 4bc81ba9f4..c4ac1a650f 100644 --- a/src/backend/libpq/be-fsstubs.c +++ b/src/backend/libpq/be-fsstubs.c @@ -39,7 +39,6 @@ #include "postgres.h" #include -#include #include #include @@ -57,7 +56,9 @@ */ 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 /* @@ -210,7 +211,6 @@ lo_write(int fd, const char *buf, int len) return status; } - Datum lo_lseek(PG_FUNCTION_ARGS) { @@ -226,42 +226,31 @@ 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); } @@ -301,7 +290,7 @@ Datum 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, @@ -310,37 +299,30 @@ lo_tell(PG_FUNCTION_ARGS) 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 diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c index 3f5688b939..5dd17823c2 100644 --- a/src/backend/storage/large_object/inv_api.c +++ b/src/backend/storage/large_object/inv_api.c @@ -30,6 +30,8 @@ */ #include "postgres.h" +#include + #include "access/genam.h" #include "access/heapam.h" #include "access/sysattr.h" @@ -264,7 +266,10 @@ inv_open(Oid lobjId, int flags, MemoryContext mcxt) 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)) @@ -381,34 +386,45 @@ inv_getsize(LargeObjectDesc *obj_desc) 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 @@ -438,9 +454,6 @@ inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes) 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], @@ -559,13 +572,18 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes) 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(); @@ -759,7 +777,18 @@ inv_truncate(LargeObjectDesc *obj_desc, int64 len) 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(); diff --git a/src/backend/utils/errcodes.txt b/src/backend/utils/errcodes.txt index db8ab53af2..3e04164956 100644 --- a/src/backend/utils/errcodes.txt +++ b/src/backend/utils/errcodes.txt @@ -199,7 +199,6 @@ Section: Class 22 - Data Exception 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 index a2da836ff2..d8352f180d 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1040,7 +1040,7 @@ DATA(insert OID = 955 ( lowrite PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 23 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"); diff --git a/src/interfaces/libpq/fe-lobj.c b/src/interfaces/libpq/fe-lobj.c index 7c40100b86..cf6fe3e128 100644 --- a/src/interfaces/libpq/fe-lobj.c +++ b/src/interfaces/libpq/fe-lobj.c @@ -32,21 +32,19 @@ #include #include +#include /* for ntohl/htonl */ +#include #include "libpq-fe.h" #include "libpq-int.h" #include "libpq/libpq-fs.h" /* must come after sys/stat.h */ -/* for ntohl/htonl */ -#include -#include - #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 @@ -373,7 +371,7 @@ lo_lseek64(PGconn *conn, int fd, pg_int64 offset, int whence) { PQArgBlock argv[3]; PGresult *res; - pg_int64 retval; + pg_int64 retval; int result_len; if (conn == NULL || conn->lobjfuncs == NULL) @@ -403,11 +401,11 @@ lo_lseek64(PGconn *conn, int fd, pg_int64 offset, int whence) 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 { @@ -506,9 +504,7 @@ lo_create(PGconn *conn, Oid lobjId) /* * lo_tell * returns the current seek location of the large object - * */ - int lo_tell(PGconn *conn, int fd) { @@ -575,7 +571,7 @@ lo_tell64(PGconn *conn, int fd) if (PQresultStatus(res) == PGRES_COMMAND_OK) { PQclear(res); - return lo_ntoh64((pg_int64) retval); + return lo_ntoh64(retval); } else { @@ -935,7 +931,9 @@ lo_initialize(PGconn *conn) 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) { @@ -993,30 +991,6 @@ lo_initialize(PGconn *conn) 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 @@ -1027,43 +1001,48 @@ lo_initialize(PGconn *conn) /* * 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; } diff --git a/src/test/examples/testlo64.c b/src/test/examples/testlo64.c index 6ab7f524ca..c10c3e0138 100644 --- a/src/test/examples/testlo64.c +++ b/src/test/examples/testlo64.c @@ -3,7 +3,7 @@ * 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 * * @@ -46,7 +46,7 @@ importFile(PGconn *conn, char *filename) 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); } /* @@ -54,7 +54,7 @@ importFile(PGconn *conn, char *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); @@ -81,24 +81,16 @@ pickout(PGconn *conn, Oid lobjId, pg_int64 start, int len) 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); @@ -114,10 +106,6 @@ pickout(PGconn *conn, Oid lobjId, pg_int64 start, int len) } 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); } @@ -129,18 +117,13 @@ overwrite(PGconn *conn, Oid lobjId, pg_int64 start, int len) 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); @@ -160,30 +143,22 @@ overwrite(PGconn *conn, Oid lobjId, pg_int64 start, int len) } } 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); } @@ -203,11 +178,11 @@ exportFile(PGconn *conn, Oid lobjId, char *filename) 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 @@ -215,12 +190,12 @@ exportFile(PGconn *conn, Oid lobjId, char *filename) 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) { @@ -293,24 +268,22 @@ main(int argc, char **argv) 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"); diff --git a/src/test/regress/input/largeobject.source b/src/test/regress/input/largeobject.source index 4984d78a06..f0ea7a2e17 100644 --- a/src/test/regress/input/largeobject.source +++ b/src/test/regress/input/largeobject.source @@ -125,7 +125,7 @@ SELECT lo_tell(fd) FROM lotest_stash_values; 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)); diff --git a/src/test/regress/output/largeobject.source b/src/test/regress/output/largeobject.source index 74c4772b03..a25ac2a912 100644 --- a/src/test/regress/output/largeobject.source +++ b/src/test/regress/output/largeobject.source @@ -210,7 +210,7 @@ SELECT lo_close(fd) FROM lotest_stash_values; (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; diff --git a/src/test/regress/output/largeobject_1.source b/src/test/regress/output/largeobject_1.source index 5c69eff03b..bae74f6c26 100644 --- a/src/test/regress/output/largeobject_1.source +++ b/src/test/regress/output/largeobject_1.source @@ -209,6 +209,88 @@ SELECT lo_close(fd) 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 -- 2.40.0