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 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 +#include + +#include +#include +#include +#include + +#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; +}