This patch implements the ability for large objects to be larger than 2GB.
I believe the limit to now be about 2TB, based on the fact that the large
object page size is 2048 bytes, and the page number is still 32 bits.
There are a few things about this patch which probably require tweaking or
at least a closer inspection from the list.
1) The lo_*64 functions are added to the catalog/pg_proc.h (spacing exact
location atm) with OID set to 0, all other entries in this file have OIDs
explicitly defined.
2) The lo_*64, in order to be convenient from the client end, have
functions added to libpq as the existing lo_* functions. The client side
of libpq did not previously know anything about int64 or how to
send/receive them. I added an include of postgres-fe.h (which according
to the comment in that file looks like it should go there) so int64 would
be defined, also implemented functions (code mostly stolen from the server
libpq format functions for same) to convert them to/from network byte
order. I did this in a somewhat inconsistent way between the get and put,
as I did not want to change the existing api at all, and existing code as
little as possible.
3) The 32 bit box I tested this on was a PII 300MHz laptop. Not exactly
the fastest. The "test" consisted entirely of making sure it compiled.
Perhaps someone with a fast IA32 box and spare cycles can test it? Also,
so far the only platforms I have tried to compile this on have been:
* Linux 2.6 (gentoo), AMD64, gcc-3.4.4
* Solaris 8, SPARCv9, gcc-3.4.2
* Linux 2.6 (debian unstable), i686, gcc-3.4.x (laptop, don't remember
exact version).
Would probably be a good idea to verify this on other platforms as well,
or at least other compilers.
Hopefully I did not break anything too badly with this. All of the
regression tests still pass after the patch, and I made a version of the
tests/examples/testlo which uses 64bit (in the patch) which works also. I
grepped in the regression tests, and I could not find any usage of large
objects in them, which I found to be rather odd, which is why I used
testlo and my new testlo64 to test them instead.
On Tue, 20 Sep 2005, Jonah H. Harris wrote:
> Cool. We look forward to it.
>
> On 9/19/05, Mark Dilger <[EMAIL PROTECTED]> wrote:
> >
> > Jonah H. Harris wrote:
> > > Mark,
> > >
> > > If you don't mind contributing the changes, we'd be glad to take a look
> > > at them. Thanks.
> > >
> > > -Jonah
> > >
> >
> > Ok, we will post it back soon. We have tested it on two different 64-bit
> > architectures (Sparc and AMD) and are now testing on pentium before
> > posting up
> > to the list.
> >
> > mark
> >
>
>
>
> --
> Respectfully,
>
> Jonah H. Harris, Database Internals Architect
> EnterpriseDB Corporation
> http://www.enterprisedb.com/
>
--
Mere nonexistence is a feeble excuse for declaring a thing unseeable. You
*can* see dragons. You just have to look in the right direction.
-- John Hasler
diff -Nur postgresql-8.0.3-orig/src/backend/libpq/be-fsstubs.c
postgresql-8.0.3/src/backend/libpq/be-fsstubs.c
--- postgresql-8.0.3-orig/src/backend/libpq/be-fsstubs.c 2004-12-31
13:59:50.000000000 -0800
+++ postgresql-8.0.3/src/backend/libpq/be-fsstubs.c 2005-09-18
17:22:17.000000000 -0700
@@ -233,6 +233,34 @@
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_seek64(cookies[fd], offset, whence);
+
+ MemoryContextSwitchTo(currentContext);
+
+ PG_RETURN_INT64(status);
+}
+
Datum
lo_creat(PG_FUNCTION_ARGS)
{
@@ -283,6 +311,28 @@
PG_RETURN_INT32(inv_tell(cookies[fd]));
}
+
+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_unlink(PG_FUNCTION_ARGS)
{
diff -Nur postgresql-8.0.3-orig/src/backend/storage/large_object/inv_api.c
postgresql-8.0.3/src/backend/storage/large_object/inv_api.c
--- postgresql-8.0.3-orig/src/backend/storage/large_object/inv_api.c
2004-12-31 14:00:59.000000000 -0800
+++ postgresql-8.0.3/src/backend/storage/large_object/inv_api.c 2005-09-18
19:28:24.000000000 -0700
@@ -255,11 +255,11 @@
* NOTE: LOs can contain gaps, just like Unix files. We actually return
* the offset of the last byte + 1.
*/
-static uint32
+static uint64
inv_getsize(LargeObjectDesc *obj_desc)
{
bool found = false;
- uint32 lastbyte = 0;
+ uint64 lastbyte = 0;
ScanKeyData skey[1];
IndexScanDesc sd;
HeapTuple tuple;
@@ -298,7 +298,7 @@
heap_tuple_untoast_attr((varattrib *)
datafield);
pfreeit = true;
}
- lastbyte = data->pageno * LOBLKSIZE + getbytealen(datafield);
+ lastbyte = (uint64) data->pageno * LOBLKSIZE +
getbytealen(datafield);
if (pfreeit)
pfree(datafield);
break;
@@ -353,15 +353,55 @@
return obj_desc->offset;
}
+int64
+inv_seek64(LargeObjectDesc *obj_desc, int64 offset, int whence)
+{
+ Assert(PointerIsValid(obj_desc));
+
+ switch (whence)
+ {
+ case SEEK_SET:
+ if (offset < 0)
+ elog(ERROR, "invalid seek offset: "
INT64_FORMAT, offset);
+ obj_desc->offset = offset;
+ break;
+ case SEEK_CUR:
+ if (offset < 0 && obj_desc->offset < ((uint64)
(-offset)))
+ elog(ERROR, "invalid seek offset: "
INT64_FORMAT, offset);
+ obj_desc->offset += offset;
+ break;
+ case SEEK_END:
+ {
+ uint64 size = inv_getsize(obj_desc);
+
+ if (offset < 0 && size < ((uint64) (-offset)))
+ elog(ERROR, "invalid seek offset: "
INT64_FORMAT, offset);
+ obj_desc->offset = size + offset;
+ }
+ break;
+ default:
+ elog(ERROR, "invalid whence: %d", whence);
+ }
+ return obj_desc->offset;
+}
+
+int64
+inv_tell64(LargeObjectDesc *obj_desc)
+{
+ Assert(PointerIsValid(obj_desc));
+
+ return obj_desc->offset;
+}
+
int
inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
{
int nread = 0;
- int n;
- int off;
+ int64 n;
+ int64 off;
int len;
int32 pageno = (int32) (obj_desc->offset / LOBLKSIZE);
- uint32 pageoff;
+ uint64 pageoff;
ScanKeyData skey[2];
IndexScanDesc sd;
HeapTuple tuple;
@@ -400,7 +440,7 @@
* 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;
+ pageoff = ((uint64) data->pageno) * LOBLKSIZE;
if (pageoff > obj_desc->offset)
{
n = pageoff - obj_desc->offset;
diff -Nur postgresql-8.0.3-orig/src/include/catalog/pg_proc.h
postgresql-8.0.3/src/include/catalog/pg_proc.h
--- postgresql-8.0.3-orig/src/include/catalog/pg_proc.h 2004-12-31
14:03:25.000000000 -0800
+++ postgresql-8.0.3/src/include/catalog/pg_proc.h 2005-09-17
23:18:47.000000000 -0700
@@ -1224,10 +1224,14 @@
DESCR("large object read");
DATA(insert OID = 955 ( lowrite PGNSP PGUID 12 f f t f v 2
23 "23 17" _null_ lowrite - _null_ ));
DESCR("large object write");
+DATA(insert OID = 0 ( lo_lseek64 PGNSP PGUID 12 f f t f v 3
20 "23 20 23" _null_ lo_lseek64 - _null_ ));
+DESCR("large object seek (64bit)");
DATA(insert OID = 956 ( lo_lseek PGNSP PGUID 12 f f t f v 3
23 "23 23 23" _null_ lo_lseek - _null_ ));
DESCR("large object seek");
DATA(insert OID = 957 ( lo_creat PGNSP PGUID 12 f f t f v 1
26 "23" _null_ lo_creat - _null_ ));
DESCR("large object create");
+DATA(insert OID = 0 ( lo_tell64 PGNSP PGUID 12 f f t f v 1
20 "23" _null_ lo_tell64 - _null_ ));
+DESCR("large object position (64bit)");
DATA(insert OID = 958 ( lo_tell PGNSP PGUID 12 f f t f v 1
23 "23" _null_ lo_tell - _null_ ));
DESCR("large object position");
diff -Nur postgresql-8.0.3-orig/src/include/libpq/be-fsstubs.h
postgresql-8.0.3/src/include/libpq/be-fsstubs.h
--- postgresql-8.0.3-orig/src/include/libpq/be-fsstubs.h 2004-12-31
14:03:32.000000000 -0800
+++ postgresql-8.0.3/src/include/libpq/be-fsstubs.h 2005-09-17
23:18:47.000000000 -0700
@@ -32,6 +32,8 @@
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);
/*
diff -Nur postgresql-8.0.3-orig/src/include/storage/large_object.h
postgresql-8.0.3/src/include/storage/large_object.h
--- postgresql-8.0.3-orig/src/include/storage/large_object.h 2004-12-31
14:03:42.000000000 -0800
+++ postgresql-8.0.3/src/include/storage/large_object.h 2005-09-17
23:18:47.000000000 -0700
@@ -33,7 +33,7 @@
{
Oid id; /* LO's
identifier */
SubTransactionId subid; /* owning subtransaction ID */
- uint32 offset; /* current seek pointer */
+ uint64 offset; /* current seek pointer */
int flags; /* locking info, etc */
/* flag bits: */
@@ -71,6 +71,8 @@
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 int64 inv_seek64(LargeObjectDesc *obj_desc, int64 offset, int whence);
+extern int64 inv_tell64(LargeObjectDesc *obj_desc);
extern int inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes);
extern int inv_write(LargeObjectDesc *obj_desc, char *buf, int nbytes);
diff -Nur postgresql-8.0.3-orig/src/interfaces/libpq/fe-lobj.c
postgresql-8.0.3/src/interfaces/libpq/fe-lobj.c
--- postgresql-8.0.3-orig/src/interfaces/libpq/fe-lobj.c 2004-12-31
14:03:50.000000000 -0800
+++ postgresql-8.0.3/src/interfaces/libpq/fe-lobj.c 2005-09-19
00:39:20.000000000 -0700
@@ -264,6 +264,61 @@
}
/*
+ * lo_lseek64
+ * change the current read or write location on a large object
+ * currently, only L_SET is a legal value for whence
+ *
+ */
+
+int64
+lo_lseek64(PGconn *conn, int fd, int64 offset, int whence)
+{
+ PQArgBlock argv[3];
+ PGresult *res;
+ int64 retval;
+ int result_len;
+
+ if (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;
+
+ argv[1].isint = 1;
+ 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, 1, argv, 3);
+ if (PQresultStatus(res) == PGRES_COMMAND_OK)
+ {
+ PQclear(res);
+ return retval;
+ }
+ else
+ {
+ PQclear(res);
+ return -1;
+ }
+}
+
+/*
* lo_creat
* create a new large object
* the mode is a bitmask describing different attributes of the new object
@@ -305,6 +360,53 @@
/*
+ * lo_tell64
+ * returns the current seek location of the large object
+ *
+ */
+
+int64
+lo_tell64(PGconn *conn, int fd)
+{
+ int64 retval;
+ PQArgBlock argv[1];
+ PGresult *res;
+ int result_len;
+
+ if (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, 1, argv, 1);
+ if (PQresultStatus(res) == PGRES_COMMAND_OK)
+ {
+ PQclear(res);
+ return retval;
+ }
+ else
+ {
+ PQclear(res);
+ return -1;
+ }
+}
+
+
+/*
* lo_tell
* returns the current seek location of the large object
*
@@ -570,7 +672,9 @@
"'lo_creat', "
"'lo_unlink', "
"'lo_lseek', "
+ "'lo_lseek64', "
"'lo_tell', "
+ "'lo_tell64', "
"'loread', "
"'lowrite') "
"and pronamespace = (select oid from
pg_catalog.pg_namespace "
@@ -583,6 +687,8 @@
"or proname = 'lo_unlink' "
"or proname = 'lo_lseek' "
"or proname = 'lo_tell' "
+ "or proname = 'lo_lseek64' "
+ "or proname = 'lo_tell64' "
"or proname = 'loread' "
"or proname = 'lowrite'";
@@ -621,6 +727,10 @@
lobjfuncs->fn_lo_lseek = foid;
else if (!strcmp(fname, "lo_tell"))
lobjfuncs->fn_lo_tell = foid;
+ else if (!strcmp(fname, "lo_lseek64"))
+ lobjfuncs->fn_lo_lseek64 = foid;
+ else if (!strcmp(fname, "lo_tell64"))
+ lobjfuncs->fn_lo_tell64 = foid;
else if (!strcmp(fname, "loread"))
lobjfuncs->fn_lo_read = foid;
else if (!strcmp(fname, "lowrite"))
diff -Nur postgresql-8.0.3-orig/src/interfaces/libpq/fe-misc.c
postgresql-8.0.3/src/interfaces/libpq/fe-misc.c
--- postgresql-8.0.3-orig/src/interfaces/libpq/fe-misc.c 2004-12-31
14:03:50.000000000 -0800
+++ postgresql-8.0.3/src/interfaces/libpq/fe-misc.c 2005-09-18
19:26:58.000000000 -0700
@@ -223,6 +223,27 @@
conn->inCursor += 4;
*result = (int) ntohl(tmp4);
break;
+ case 8:
+ if (conn->inCursor + 8 > conn->inEnd)
+ return EOF;
+ else
+ {
+ int64 * i64res = (int64 *)result;
+ uint32 h32;
+ memcpy(&h32, conn->inBuffer + conn->inCursor,
4);
+ conn->inCursor += 4;
+ memcpy(&tmp4, conn->inBuffer + conn->inCursor,
4);
+ conn->inCursor += 4;
+#ifdef INT64_IS_BUSTED
+ /* just lose the high half */
+ *i64res = (int) ntohl(tmp4);
+#else
+ *i64res = (int) ntohl(h32);
+ *i64res <<= 32;
+ *i64res |= (uint32) ntohl(tmp4);
+#endif
+ }
+ break;
default:
pqInternalNotice(&conn->noticeHooks,
"integer of size %lu not
supported by pqGetInt",
@@ -231,12 +252,23 @@
}
if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "From backend (#%lu)> %d\n", (unsigned
long) bytes, *result);
+ fprintf(conn->Pfdebug, "From backend (#%lu)> " INT64_FORMAT
"\n", (unsigned long) bytes, (bytes == 8) ? *(int64 *)result : (int64)*result);
return 0;
}
/*
+ * pqGetInt64
+ * read an 8 byte integer and convert from network byte order
+ * to local byte order
+ */
+int
+pqGetInt64(int64 *result, PGconn *conn)
+{
+ return pqGetInt((int *)result, 8, conn);
+}
+
+/*
* pqPutInt
* write an integer of 2 or 4 bytes, converting from host byte order
* to network byte order.
@@ -272,6 +304,41 @@
return 0;
}
+
+/*
+ * pqPutInt64
+ * write an integer of 8 bytes, converting from host byte order
+ * to network byte order.
+ */
+int
+pqPutInt64(int64 value, PGconn *conn)
+{
+ uint32 n32;
+
+ /* High order half first, since we're doing MSB-first */
+#ifdef INT64_IS_BUSTED
+ /* don't try a right shift of 32 on a 32-bit word */
+ n32 = (value < 0) ? -1 : 0;
+#else
+ n32 = (uint32) (value >> 32);
+#endif
+ n32 = htonl(n32);
+ if (pqPutMsgBytes((const char *) &n32, 4, conn))
+ return EOF;
+
+ /* Now the low order half */
+ n32 = (uint32) value;
+ n32 = htonl(n32);
+
+ if (pqPutMsgBytes((const char *) &n32, 4, conn))
+ return EOF;
+
+ if (conn->Pfdebug)
+ fprintf(conn->Pfdebug, "To backend (8#)> " INT64_FORMAT "\n",
value);
+
+ return 0;
+}
+
/*
* Make sure conn's output buffer can hold bytes_needed bytes (caller must
* include already-stored data into the value!)
diff -Nur postgresql-8.0.3-orig/src/interfaces/libpq/fe-protocol3.c
postgresql-8.0.3/src/interfaces/libpq/fe-protocol3.c
--- postgresql-8.0.3-orig/src/interfaces/libpq/fe-protocol3.c 2004-12-31
14:03:50.000000000 -0800
+++ postgresql-8.0.3/src/interfaces/libpq/fe-protocol3.c 2005-09-18
16:47:10.000000000 -0700
@@ -1233,11 +1233,24 @@
if (args[i].isint)
{
- if (pqPutInt(args[i].u.integer, args[i].len, conn))
+ if (args[i].len <= 4)
{
- pqHandleSendFailure(conn);
- return NULL;
+ if (pqPutInt(args[i].u.integer, args[i].len,
conn))
+ {
+ pqHandleSendFailure(conn);
+ return NULL;
+ }
+ }
+ else
+ {
+ if (pqPutInt64(*(int64 *)args[i].u.ptr, conn))
+ {
+ pqHandleSendFailure(conn);
+ return NULL;
+ }
}
+
+
}
else
{
diff -Nur postgresql-8.0.3-orig/src/interfaces/libpq/libpq-fe.h
postgresql-8.0.3/src/interfaces/libpq/libpq-fe.h
--- postgresql-8.0.3-orig/src/interfaces/libpq/libpq-fe.h 2004-12-31
14:03:50.000000000 -0800
+++ postgresql-8.0.3/src/interfaces/libpq/libpq-fe.h 2005-09-17
23:18:47.000000000 -0700
@@ -27,6 +27,7 @@
* such as Oid.
*/
#include "postgres_ext.h"
+#include "postgres_fe.h"
/* SSL type is needed here only to declare PQgetssl() */
#ifdef USE_SSL
@@ -479,8 +480,10 @@
extern int lo_read(PGconn *conn, int fd, char *buf, size_t len);
extern int lo_write(PGconn *conn, int fd, char *buf, size_t len);
extern int lo_lseek(PGconn *conn, int fd, int offset, int whence);
+extern int64 lo_lseek64(PGconn *conn, int fd, int64 offset, int whence);
extern Oid lo_creat(PGconn *conn, int mode);
extern int lo_tell(PGconn *conn, int fd);
+extern int64 lo_tell64(PGconn *conn, int fd);
extern int lo_unlink(PGconn *conn, Oid lobjId);
extern Oid lo_import(PGconn *conn, const char *filename);
extern int lo_export(PGconn *conn, Oid lobjId, const char *filename);
diff -Nur postgresql-8.0.3-orig/src/interfaces/libpq/libpq-int.h
postgresql-8.0.3/src/interfaces/libpq/libpq-int.h
--- postgresql-8.0.3-orig/src/interfaces/libpq/libpq-int.h 2005-01-05
16:59:47.000000000 -0800
+++ postgresql-8.0.3/src/interfaces/libpq/libpq-int.h 2005-09-18
16:05:46.000000000 -0700
@@ -232,6 +232,8 @@
Oid fn_lo_unlink; /* OID of backend function
lo_unlink */
Oid fn_lo_lseek; /* OID of backend function
lo_lseek */
Oid fn_lo_tell; /* OID of backend
function lo_tell */
+ Oid fn_lo_lseek64; /* OID of backend function
lo_lseek64 */
+ Oid fn_lo_tell64; /* OID of backend
function lo_tell64 */
Oid fn_lo_read; /* OID of backend
function LOread */
Oid fn_lo_write; /* OID of backend function
LOwrite */
} PGlobjfuncs;
@@ -457,7 +459,9 @@
extern int pqGetnchar(char *s, size_t len, PGconn *conn);
extern int pqPutnchar(const char *s, size_t len, PGconn *conn);
extern int pqGetInt(int *result, size_t bytes, PGconn *conn);
+extern int pqGetInt64(int64 *result, PGconn *conn);
extern int pqPutInt(int value, size_t bytes, PGconn *conn);
+extern int pqPutInt64(int64 value, PGconn *conn);
extern int pqPutMsgStart(char msg_type, bool force_len, PGconn *conn);
extern int pqPutMsgEnd(PGconn *conn);
extern int pqReadData(PGconn *conn);
diff -Nur postgresql-8.0.3-orig/src/test/examples/Makefile
postgresql-8.0.3/src/test/examples/Makefile
--- postgresql-8.0.3-orig/src/test/examples/Makefile 2005-03-25
10:18:41.000000000 -0800
+++ postgresql-8.0.3/src/test/examples/Makefile 2005-09-17 23:18:47.000000000
-0700
@@ -6,11 +6,11 @@
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
-override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS)
+override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS) -g
override LDLIBS := $(libpq_pgport) $(LDLIBS)
-PROGS = testlibpq testlibpq2 testlibpq3 testlibpq4 testlo
+PROGS = testlibpq testlibpq2 testlibpq3 testlibpq4 testlo testlo64
all: $(PROGS)
diff -Nur postgresql-8.0.3-orig/src/test/examples/testlo64.c
postgresql-8.0.3/src/test/examples/testlo64.c
--- postgresql-8.0.3-orig/src/test/examples/testlo64.c 1969-12-31
16:00:00.000000000 -0800
+++ postgresql-8.0.3/src/test/examples/testlo64.c 2005-09-19
00:52:16.000000000 -0700
@@ -0,0 +1,267 @@
+/*-------------------------------------------------------------------------
+ *
+ * testlo.c
+ * test using large objects with libpq
+ *
+ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $PostgreSQL: pgsql/src/test/examples/testlo.c,v 1.25 2004/12/31
22:03:58 pgsql Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "libpq-fe.h"
+#include "libpq/libpq-fs.h"
+
+#define BUFSIZE 1024
+
+/*
+ * importFile -
+ * import file "in_filename" into database as large object "lobjOid"
+ *
+ */
+static Oid
+importFile(PGconn *conn, char *filename)
+{
+ Oid lobjId;
+ int lobj_fd;
+ char buf[BUFSIZE];
+ int nbytes,
+ tmp;
+ int fd;
+
+ /*
+ * open the file to be read in
+ */
+ fd = open(filename, O_RDONLY, 0666);
+ if (fd < 0)
+ { /* error */
+ fprintf(stderr, "can't open unix file\"%s\"\n", filename);
+ }
+
+ /*
+ * create the large object
+ */
+ lobjId = lo_creat(conn, INV_READ | INV_WRITE);
+ if (lobjId == 0)
+ fprintf(stderr, "can't create large object");
+
+ lobj_fd = lo_open(conn, lobjId, INV_WRITE);
+
+ /*
+ * read in from the Unix file and write to the inversion file
+ */
+ while ((nbytes = read(fd, buf, BUFSIZE)) > 0)
+ {
+ tmp = lo_write(conn, lobj_fd, buf, nbytes);
+ if (tmp < nbytes)
+ fprintf(stderr, "error while reading \"%s\"", filename);
+ }
+
+ close(fd);
+ lo_close(conn, lobj_fd);
+
+ return lobjId;
+}
+
+static void
+pickout(PGconn *conn, Oid lobjId, int64 start, int len)
+{
+ int lobj_fd;
+ char *buf;
+ int nbytes;
+ int nread;
+
+ lobj_fd = lo_open(conn, lobjId, INV_READ);
+ if (lobj_fd < 0)
+ fprintf(stderr, "can't open large object %u", lobjId);
+
+ if (lo_lseek64(conn, lobj_fd, start, SEEK_SET) < 0)
+ {
+ fprintf(stderr, "error lo_lseek64: %s\n", PQerrorMessage(conn));
+ return;
+ }
+
+ buf = malloc(len + 1);
+
+ nread = 0;
+ while (len - nread > 0)
+ {
+ nbytes = lo_read(conn, lobj_fd, buf, len - nread);
+ buf[nbytes] = '\0';
+ fprintf(stderr, ">>> %s", buf);
+ nread += nbytes;
+ if (nbytes <= 0)
+ break; /* no more data? */
+ }
+ free(buf);
+ fprintf(stderr, "\n");
+ lo_close(conn, lobj_fd);
+}
+
+static void
+overwrite(PGconn *conn, Oid lobjId, int64 start, int len)
+{
+ int lobj_fd;
+ char *buf;
+ int nbytes;
+ int nwritten;
+ int i;
+
+ lobj_fd = lo_open(conn, lobjId, INV_READ);
+ if (lobj_fd < 0)
+ fprintf(stderr, "can't open large object %u", lobjId);
+
+ lo_lseek64(conn, lobj_fd, start, SEEK_SET);
+ buf = malloc(len + 1);
+
+ for (i = 0; i < len; i++)
+ buf[i] = 'X';
+ buf[i] = '\0';
+
+ nwritten = 0;
+ while (len - nwritten > 0)
+ {
+ nbytes = lo_write(conn, lobj_fd, buf + nwritten, len -
nwritten);
+ nwritten += nbytes;
+ if (nbytes <= 0)
+ {
+ fprintf(stderr, "\nWRITE FAILED!\n");
+ break;
+ }
+ }
+ free(buf);
+ fprintf(stderr, "\n");
+ lo_close(conn, lobj_fd);
+}
+
+
+/*
+ * exportFile -
+ * export large object "lobjOid" to file "out_filename"
+ *
+ */
+static void
+exportFile(PGconn *conn, Oid lobjId, char *filename)
+{
+ int lobj_fd;
+ char buf[BUFSIZE];
+ int nbytes,
+ tmp;
+ int fd;
+
+ /*
+ * create an inversion "object"
+ */
+ lobj_fd = lo_open(conn, lobjId, INV_READ);
+ if (lobj_fd < 0)
+ fprintf(stderr, "can't 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\"",
+ filename);
+ }
+
+ /*
+ * read in from the Unix file and write to the inversion file
+ */
+ while ((nbytes = lo_read(conn, lobj_fd, buf, BUFSIZE)) > 0)
+ {
+ tmp = write(fd, buf, nbytes);
+ if (tmp < nbytes)
+ {
+ fprintf(stderr, "error while writing \"%s\"",
+ filename);
+ }
+ }
+
+ lo_close(conn, lobj_fd);
+ close(fd);
+
+ return;
+}
+
+static void
+exit_nicely(PGconn *conn)
+{
+ PQfinish(conn);
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ char *in_filename,
+ *out_filename;
+ char *database;
+ Oid lobjOid;
+ PGconn *conn;
+ PGresult *res;
+
+ if (argc != 4)
+ {
+ fprintf(stderr, "Usage: %s database_name in_filename
out_filename\n",
+ argv[0]);
+ exit(1);
+ }
+
+ database = argv[1];
+ in_filename = argv[2];
+ out_filename = argv[3];
+
+ /*
+ * set up the connection
+ */
+ conn = PQsetdb(NULL, NULL, NULL, NULL, database);
+
+ /* check to see that the backend connection was successfully made */
+ if (PQstatus(conn) != CONNECTION_OK)
+ {
+ fprintf(stderr, "Connection to database failed: %s",
+ PQerrorMessage(conn));
+ exit_nicely(conn);
+ }
+
+ res = PQexec(conn, "begin");
+ PQclear(res);
+ printf("importing file \"%s\" ...\n", in_filename);
+/* lobjOid = importFile(conn, in_filename); */
+ lobjOid = lo_import(conn, in_filename);
+ if (lobjOid == 0)
+ fprintf(stderr, "%s\n", PQerrorMessage(conn));
+ else
+ {
+ printf("\tas large object %u.\n", lobjOid);
+
+ printf("picking out bytes 4294967000-4294968000 of the large
object\n");
+ pickout(conn, lobjOid, 4294967000ULL, 1000);
+
+ printf("overwriting bytes 4294967000-4294968000 of the large
object with X's\n");
+ overwrite(conn, lobjOid, 4294967000ULL, 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));
+ }
+
+ res = PQexec(conn, "end");
+ PQclear(res);
+ PQfinish(conn);
+ return 0;
+}
---------------------------(end of broadcast)---------------------------
TIP 2: Don't 'kill -9' the postmaster