On Mon, 18 Aug 2008, Tom Lane wrote:
> What would make more sense is to redesign the large-object stuff to be
> somewhat modern and featureful, and provide stream-access APIs (think
> lo_read, lo_seek, etc) that allow offsets wider than 32 bits.
A few years ago, I was working on such a project for a company I used to
work for. The company changed directions shortly thereafter, and the
project was dropped, but perhaps the patch might still be useful as a
starting point for someone else.
The original patch is
http://archives.postgresql.org/pgsql-hackers/2005-09/msg01026.php, and the
advice I was working on implementing was in
http://archives.postgresql.org/pgsql-hackers/2005-09/msg01063.php
I am attaching the latest version of the patch I found around. As it was
almost 3 years ago, I am a little fuzzy on where I left off, but I do
remember that I was trying to work through the suggestions Tom Lane gave
in that second linked email. I would recommend discarding the libpq
changes, since that seemed to not pass muster.
Note that this patch was against 8.0.3. There only seem to be a few
issues applying it to the current head, but I haven't really dug into them
to see how difficult it would be to update. Luckily, the large object
code is fairly slow-moving, so there aren't too many conflicts. One thing
I did notice is that it looks like someone extracted one of the functions
I wrote in this patch and applied it as a 32-bit version. Good for them.
I'm glad someone got some use out of this project, and perhaps more use
will come of it.
--
At the source of every error which is blamed on the computer you will
find at least two human errors, including the error of blaming it on
the computer.
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-10-03
11:43:36.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_seek(cookies[fd], offset, whence);
+
+ MemoryContextSwitchTo(currentContext);
+
+ PG_RETURN_INT64(status);
+}
+
Datum
lo_creat(PG_FUNCTION_ARGS)
{
@@ -283,6 +311,165 @@
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_length(PG_FUNCTION_ARGS)
+{
+ int32 fd = PG_GETARG_INT32(0);
+ int32 sz = 0;
+ MemoryContext currentContext;
+
+ 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(-1);
+ }
+ Assert(fscxt != NULL);
+ currentContext = MemoryContextSwitchTo(fscxt);
+
+ sz = inv_length(cookies[fd]);
+
+ MemoryContextSwitchTo(currentContext);
+
+ PG_RETURN_INT32(sz);
+}
+
+Datum
+lo_length64(PG_FUNCTION_ARGS)
+{
+ int32 fd = PG_GETARG_INT32(0);
+ int64 sz = 0;
+ MemoryContext currentContext;
+
+ 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);
+
+ sz = inv_length(cookies[fd]);
+
+ MemoryContextSwitchTo(currentContext);
+
+ PG_RETURN_INT64(sz);
+}
+
+Datum
+lo_truncate(PG_FUNCTION_ARGS)
+{
+ int32 fd = PG_GETARG_INT32(0);
+ int32 offset = PG_GETARG_INT32(1);
+ int status = 0;
+ MemoryContext currentContext;
+
+ 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(-1);
+ }
+ Assert(fscxt != NULL);
+ currentContext = MemoryContextSwitchTo(fscxt);
+
+ status = inv_truncate(cookies[fd], offset);
+
+ MemoryContextSwitchTo(currentContext);
+
+ PG_RETURN_INT32(status);
+}
+
+
+Datum
+lo_truncate64(PG_FUNCTION_ARGS)
+{
+ int32 fd = PG_GETARG_INT32(0);
+ int64 offset = PG_GETARG_INT64(1);
+ int status = 0;
+ MemoryContext currentContext;
+
+ 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_truncate(cookies[fd], offset);
+
+ MemoryContextSwitchTo(currentContext);
+
+ PG_RETURN_INT32(status);
+}
+
+Datum
+lo_stat(PG_FUNCTION_ARGS)
+{
+ Oid lobjId = PG_GETARG_OID(0);
+ MemoryContext currentContext;
+ int32 sz = 0;
+
+ if (fscxt == NULL)
+ {
+ CreateFSContext();
+ }
+
+ currentContext = MemoryContextSwitchTo(fscxt);
+ sz = inv_stat (lobjId);
+ MemoryContextSwitchTo(currentContext);
+
+ PG_RETURN_INT32(sz);
+}
+
+Datum
+lo_stat64(PG_FUNCTION_ARGS)
+{
+ Oid lobjId = PG_GETARG_OID(0);
+ MemoryContext currentContext;
+ int64 sz = 0;
+
+ if (fscxt == NULL)
+ {
+ CreateFSContext();
+ }
+
+ currentContext = MemoryContextSwitchTo(fscxt);
+ sz = inv_stat (lobjId);
+ MemoryContextSwitchTo(currentContext);
+
+ PG_RETURN_INT64(sz);
+}
+
Datum
lo_unlink(PG_FUNCTION_ARGS)
{
Binary files
postgresql-8.0.3-orig/src/backend/storage/large_object/.inv_api.c.swp and
postgresql-8.0.3/src/backend/storage/large_object/.inv_api.c.swp differ
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-10-03
12:15:39.000000000 -0700
@@ -255,23 +255,21 @@
* 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)
+static int64
+inv_getsize(Oid lobjId)
{
bool found = false;
- uint32 lastbyte = 0;
+ int64 lastbyte = 0;
ScanKeyData skey[1];
IndexScanDesc sd;
HeapTuple tuple;
- Assert(PointerIsValid(obj_desc));
-
open_lo_relation();
ScanKeyInit(&skey[0],
Anum_pg_largeobject_loid,
BTEqualStrategyNumber, F_OIDEQ,
- ObjectIdGetDatum(obj_desc->id));
+ ObjectIdGetDatum(lobjId));
sd = index_beginscan(lo_heap_r, lo_index_r,
SnapshotNow, 1, skey);
@@ -298,7 +296,7 @@
heap_tuple_untoast_attr((varattrib *)
datafield);
pfreeit = true;
}
- lastbyte = data->pageno * LOBLKSIZE + getbytealen(datafield);
+ lastbyte = (int64) data->pageno * LOBLKSIZE +
getbytealen(datafield);
if (pfreeit)
pfree(datafield);
break;
@@ -309,12 +307,40 @@
if (!found)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("large object %u does not exist",
obj_desc->id)));
+ errmsg("large object %u does not exist",
lobjId)));
return lastbyte;
}
-int
-inv_seek(LargeObjectDesc *obj_desc, int offset, int whence)
+/*
+ * Determine size of a large object
+ *
+ * NOTE: LOs can contain gaps, just like Unix files. We actually return
+ * the offset of the last byte + 1.
+ */
+static int64
+inv_fgetsize(LargeObjectDesc *obj_desc)
+{
+ Assert(PointerIsValid(obj_desc));
+
+ return inv_getsize(obj_desc->id);
+}
+
+int64
+inv_length(LargeObjectDesc * obj_desc)
+{
+ Assert(PointerIsValid(obj_desc));
+
+ return inv_fgetsize(obj_desc);
+}
+
+int64
+inv_stat(Oid lobjId)
+{
+ return inv_getsize(lobjId);
+}
+
+int64
+inv_seek(LargeObjectDesc *obj_desc, int64 offset, int whence)
{
Assert(PointerIsValid(obj_desc));
@@ -322,20 +348,20 @@
{
case SEEK_SET:
if (offset < 0)
- elog(ERROR, "invalid seek offset: %d", offset);
+ elog(ERROR, "invalid seek offset: "
INT64_FORMAT, offset);
obj_desc->offset = offset;
break;
case SEEK_CUR:
- if (offset < 0 && obj_desc->offset < ((uint32)
(-offset)))
- elog(ERROR, "invalid seek offset: %d", offset);
+ if (offset < 0 && obj_desc->offset < ((uint64)
(-offset)))
+ elog(ERROR, "invalid seek offset: "
INT64_FORMAT, offset);
obj_desc->offset += offset;
break;
case SEEK_END:
{
- uint32 size = inv_getsize(obj_desc);
+ int64 size = inv_fgetsize(obj_desc);
- if (offset < 0 && size < ((uint32) (-offset)))
- elog(ERROR, "invalid seek offset: %d",
offset);
+ if (offset < 0 && size < ((uint64) (-offset)))
+ elog(ERROR, "invalid seek offset: "
INT64_FORMAT, offset);
obj_desc->offset = size + offset;
}
break;
@@ -345,7 +371,7 @@
return obj_desc->offset;
}
-int
+int64
inv_tell(LargeObjectDesc *obj_desc)
{
Assert(PointerIsValid(obj_desc));
@@ -357,11 +383,11 @@
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;
+ int64 pageoff;
ScanKeyData skey[2];
IndexScanDesc sd;
HeapTuple tuple;
@@ -400,7 +426,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 = ((int64) data->pageno) * LOBLKSIZE;
if (pageoff > obj_desc->offset)
{
n = pageoff - obj_desc->offset;
@@ -447,6 +473,157 @@
}
int
+inv_truncate(LargeObjectDesc *obj_desc, int64 offset)
+{
+ int64 objsz = inv_fgetsize(obj_desc);
+ if (offset > objsz)
+ {
+ int64 tempoff = obj_desc->offset;
+ obj_desc->offset = offset - 1;
+ char nul = '\0';
+ int ret = inv_write(obj_desc, &nul, 1);
+ obj_desc->offset = tempoff;
+ if (ret < 0)
+ return -1;
+ else
+ return 0;
+ }
+ else
+ {
+ int n;
+ int off;
+ int len;
+ int32 pageno = (int32) (offset / LOBLKSIZE);
+ ScanKeyData skey[2];
+ IndexScanDesc sd;
+ HeapTuple oldtuple;
+ Form_pg_largeobject olddata;
+ bytea *datafield;
+ bool pfreeit;
+ struct
+ {
+ bytea hdr;
+ char data[LOBLKSIZE];
+ } workbuf;
+ char *workb = VARATT_DATA(&workbuf.hdr);
+ HeapTuple newtup;
+ Datum values[Natts_pg_largeobject];
+ char nulls[Natts_pg_largeobject];
+ char replace[Natts_pg_largeobject];
+ CatalogIndexState indstate;
+
+ Assert(PointerIsValid(obj_desc));
+
+ open_lo_relation();
+
+ indstate = CatalogOpenIndexes(lo_heap_r);
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_largeobject_loid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(obj_desc->id));
+
+ ScanKeyInit(&skey[1],
+ Anum_pg_largeobject_pageno,
+ BTGreaterEqualStrategyNumber, F_INT4GE,
+ Int32GetDatum(pageno));
+
+ sd = index_beginscan(lo_heap_r, lo_index_r,
+ SnapshotNow, 2, skey);
+
+ oldtuple = NULL;
+ olddata = NULL;
+
+ while ((oldtuple = index_getnext(sd, ForwardScanDirection)) !=
NULL)
+ {
+ olddata = (Form_pg_largeobject) GETSTRUCT(oldtuple);
+ Assert(olddata->pageno >= pageno);
+
+ /*
+ * If we have a pre-existing page, see if it is the
page we want
+ * to write, or a later one.
+ */
+ if (olddata != NULL && olddata->pageno == pageno)
+ {
+ /*
+ * Update an existing page with fresh data.
+ *
+ * First, load old data into workbuf
+ */
+ datafield = &(olddata->data);
+ pfreeit = false;
+ if (VARATT_IS_EXTENDED(datafield))
+ {
+ datafield = (bytea *)
+
heap_tuple_untoast_attr((varattrib *) datafield);
+ pfreeit = true;
+ }
+ len = getbytealen(datafield);
+ Assert(len <= LOBLKSIZE);
+ memcpy(workb, VARDATA(datafield), len);
+ if (pfreeit)
+ pfree(datafield);
+
+ /*
+ * Fill any hole
+ */
+ off = (int) (offset % LOBLKSIZE);
+
+ /*
+ * Insert appropriate portion of new data
+ */
+ n = LOBLKSIZE - off;
+ MemSet(workb + off, 0, n);
+ /* compute valid length of new page */
+ len = off;
+ VARATT_SIZEP(&workbuf.hdr) = len + VARHDRSZ;
+
+ /*
+ * Form and insert updated tuple
+ */
+ memset(values, 0, sizeof(values));
+ memset(nulls, ' ', sizeof(nulls));
+ memset(replace, ' ', sizeof(replace));
+ values[Anum_pg_largeobject_data - 1] =
PointerGetDatum(&workbuf);
+ replace[Anum_pg_largeobject_data - 1] = 'r';
+ newtup = heap_modifytuple(oldtuple, lo_heap_r,
+
values, nulls, replace);
+ simple_heap_update(lo_heap_r, &newtup->t_self,
newtup);
+ CatalogIndexInsert(indstate, newtup);
+ heap_freetuple(newtup);
+
+ /*
+ * We're done with this old page.
+ */
+ oldtuple = NULL;
+ olddata = NULL;
+ }
+ else
+ {
+ /*
+ * Delete page.
+ *
+ * First, fill any hole
+ */
+ simple_heap_delete(lo_heap_r,
&oldtuple->t_self);
+ }
+ }
+
+ index_endscan(sd);
+
+ CatalogCloseIndexes(indstate);
+
+ /*
+ * Advance command counter so that my tuple updates will be
seen by
+ * later large-object operations in this transaction.
+ */
+ CommandCounterIncrement();
+ }
+
+ return 0;
+}
+
+int
inv_write(LargeObjectDesc *obj_desc, char *buf, int nbytes)
{
int nwritten = 0;
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-10-03
11:56:27.000000000 -0700
@@ -1224,12 +1224,28 @@
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_truncate PGNSP PGUID 12 f f t f v 2
23 "23 23" _null_ lo_truncate - _null_ ));
+DESCR("large object resize");
+DATA(insert OID = 0 ( lo_truncate64 PGNSP PGUID 12 f f t f v 2
23 "23 20" _null_ lo_truncate64 - _null_ ));
+DESCR("large object resize (64bit)");
+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");
+DATA(insert OID = 0 ( lo_stat PGNSP PGUID 12 f f t f v 1
23 "26" _null_ lo_stat - _null_ ));
+DESCR("large object size");
+DATA(insert OID = 0 ( lo_stat64 PGNSP PGUID 12 f f t f v 1
20 "26" _null_ lo_stat64 - _null_ ));
+DESCR("large object size (64bit)");
+DATA(insert OID = 0 ( lo_length PGNSP PGUID 12 f f t f v 1
23 "23" _null_ lo_length - _null_ ));
+DESCR("large object size");
+DATA(insert OID = 0 ( lo_length64 PGNSP PGUID 12 f f t f v 1
20 "23" _null_ lo_length64 - _null_ ));
+DESCR("large object size (64bit)");
DATA(insert OID = 959 ( on_pl PGNSP PGUID 12 f f t f i 2
16 "600 628" _null_ on_pl - _null_ ));
DESCR("point on line?");
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-10-03
11:42:43.000000000 -0700
@@ -32,8 +32,17 @@
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_length(PG_FUNCTION_ARGS);
+extern Datum lo_length64(PG_FUNCTION_ARGS);
+extern Datum lo_stat(PG_FUNCTION_ARGS);
+extern Datum lo_stat64(PG_FUNCTION_ARGS);
+extern Datum lo_truncate(PG_FUNCTION_ARGS);
+extern Datum lo_truncate64(PG_FUNCTION_ARGS);
+
/*
* These are not fmgr-callable, but are available to C code.
* Probably these should have had the underscore-free names,
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-10-03
11:12:42.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: */
@@ -69,9 +69,12 @@
extern LargeObjectDesc *inv_open(Oid lobjId, int flags);
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 int64 inv_seek(LargeObjectDesc *obj_desc, int64 offset, int whence);
+extern int64 inv_tell(LargeObjectDesc *obj_desc);
+extern int64 inv_length(LargeObjectDesc *obj_desc);
+extern int64 inv_stat(Oid lobjId);
extern int inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes);
extern int inv_write(LargeObjectDesc *obj_desc, char *buf, int nbytes);
+extern int inv_truncate(LargeObjectDesc *obj_desc, int64 offset);
#endif /* LARGE_OBJECT_H */
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-20
22:30:48.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-20
22:30:48.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-20
22:30:48.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-20
22:30:48.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-20
22:30:48.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/interfaces/libpq/libpq.rc
postgresql-8.0.3/src/interfaces/libpq/libpq.rc
--- postgresql-8.0.3-orig/src/interfaces/libpq/libpq.rc 2005-05-09
19:16:53.000000000 -0700
+++ postgresql-8.0.3/src/interfaces/libpq/libpq.rc 2005-09-20
23:31:58.000000000 -0700
@@ -1,8 +1,8 @@
#include <winver.h>
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 8,0,3,5129
- PRODUCTVERSION 8,0,3,5129
+ FILEVERSION 8,0,3,5263
+ PRODUCTVERSION 8,0,3,5263
FILEFLAGSMASK 0x3fL
FILEFLAGS 0
FILEOS VOS__WINDOWS32
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-20 22:30:48.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-20
22:30:48.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;
+}
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers