Adjust lo_open() so that specifying INV_READ without INV_WRITE creates
authorTom Lane
Mon, 13 Jun 2005 02:26:53 +0000 (02:26 +0000)
committerTom Lane
Mon, 13 Jun 2005 02:26:53 +0000 (02:26 +0000)
a descriptor that uses the current transaction snapshot, rather than
SnapshotNow as it did before (and still does if INV_WRITE is set).
This means pg_dump will now dump a consistent snapshot of large object
contents, as it never could do before.  Also, add a lo_create() function
that is similar to lo_creat() but allows the desired OID of the large
object to be specified.  This will simplify pg_restore considerably
(but I'll fix that in a separate commit).

doc/src/sgml/lobj.sgml
src/backend/libpq/be-fsstubs.c
src/backend/storage/large_object/inv_api.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.h
src/include/libpq/be-fsstubs.h
src/include/storage/large_object.h
src/interfaces/libpq/exports.txt
src/interfaces/libpq/fe-lobj.c
src/interfaces/libpq/libpq-fe.h
src/interfaces/libpq/libpq-int.h

index 82ca839efb2616394b07f5885c6b32ac7eec8fab..98516082c971bbc18e73c3e79e1c6733f2d225fa 100644 (file)
@@ -1,5 +1,5 @@
 
 
  
@@ -115,26 +115,52 @@ $PostgreSQL: pgsql/doc/src/sgml/lobj.sgml,v 1.36 2005/01/10 00:04:38 tgl Exp $
 Oid lo_creat(PGconn *conn, int mode);
 
      lo_creat
-     creates a new large  object.  
-     mode  is  a  bit mask
-     describing  several  different  attributes  of  the new
-     object.  The symbolic constants used here are defined
-     in the header file libpq/libpq-fs.h.
-     The access type (read, write, or both) is controlled by
-     or'ing together the bits INV_READ  and
-     INV_WRITE.  The low-order sixteen bits of the mask have
-     historically been used at Berkeley to designate the storage  manager  number on which the large object
-     should reside.  These bits should always be zero now.  (The access type
-     does not actually do anything anymore either, but one or both flag bits
-     must be set to avoid an error.)
+     creates a new large object.  
      The return value is the OID that was assigned to the new large object,
      or InvalidOid (zero) on failure.
+
+     mode is unused and
+     ignored as of PostgreSQL 8.1; however, for
+     backwards compatibility with earlier releases it is best to
+     set it to INV_READINV_WRITE,
+     or INV_READ INV_WRITE.
+     (These symbolic constants are defined
+     in the header file libpq/libpq-fs.h.)
     
 
     
      An example:
 
 inv_oid = lo_creat(conn, INV_READ|INV_WRITE);
+
+    
+
+    
+     The function
+
+Oid lo_create(PGconn *conn, Oid lobjId);
+
+     lo_create
+     also creates a new large object.  The OID to be assigned can be
+     specified by lobjId;
+     if so, failure occurs if that OID is already in use for some large
+     object.  If lobjId
+     is InvalidOid (zero) then lo_create assigns an unused
+     OID (this is the same behavior as lo_creat).
+     The return value is the OID that was assigned to the new large object,
+     or InvalidOid (zero) on failure.
+    
+
+    
+     lo_create is new as of PostgreSQL
+     8.1; if this function is run against an older server version, it will
+     fail and return InvalidOid.
+    
+
+    
+     An example:
+
+inv_oid = lo_create(conn, desired_oid);
 
     
    
@@ -186,11 +212,13 @@ int lo_export(PGconn *conn, Oid lobjId, const char *filename);
 int lo_open(PGconn *conn, Oid lobjId, int mode);
 
      lo_open
-     The lobjId argument specifies  the  OID  of  the  large
-     object  to  open.   The  mode  bits control whether the
-     object is opened  for  reading  (INV_READ),  writing (INV_WRITE),  or
-     both.
-     A  large  object cannot be opened before it is created.
+     The lobjId argument specifies the OID of the large
+     object to open.   The mode bits control whether the
+     object is opened for reading (INV_READ), writing
+     (INV_WRITE), or both.
+     (These symbolic constants are defined
+     in the header file libpq/libpq-fs.h.)
+     A large object cannot be opened before it is created.
      lo_open returns a (non-negative) large object
      descriptor for later use in lo_read,
      lo_writelo_lseek,
@@ -198,7 +226,31 @@ int lo_open(PGconn *conn, Oid lobjId, int mode);
      The descriptor is only valid for 
      the duration of the current transaction.
      On failure, -1 is returned.
-
+    
+
+    
+     The server currently does not distinguish between modes
+     INV_WRITE and INV_READ |
+     INV_WRITE: you are allowed to read from the descriptor
+     in either case.  However there is a significant difference between
+     these modes and INV_READ alone: with INV_READ
+     you cannot write on the descriptor, and the data read from it will
+     reflect the contents of the large object at the time of the transaction
+     snapshot that was active when lo_open was executed,
+     regardless of later writes by this or other transactions.  Reading
+     from a descriptor opened with INV_WRITE returns
+     data that reflects all writes of other committed transactions as well
+     as writes of the current transaction.  This is similar to the behavior
+     of SERIALIZABLE versus READ COMMITTED transaction
+     modes for ordinary SQL SELECT commands.
+    
+
+    
+     An example:
+
+inv_fd = lo_open(conn, inv_oid, INV_READ|INV_WRITE);
+
+    
 
 
 
@@ -317,6 +369,7 @@ int lo_unlink(PGconn *conn, Oid lobjId);
    equivalent server-side functions.  The ones that are actually useful
    to call via SQL commands are
    lo_creatlo_creat,
+   lo_createlo_create,
    lo_unlinklo_unlink,
    lo_importlo_import, and
    lo_exportlo_export.
@@ -330,6 +383,8 @@ CREATE TABLE image (
 
 SELECT lo_creat(-1);       -- returns OID of new, empty large object
 
+SELECT lo_create(43213);   -- attempts to create large object with OID 43213
+
 SELECT lo_unlink(173454);  -- deletes large object with OID 173454
 
 INSERT INTO image (name, raster)
index f600e140e785281d89b087ded3dd4d4e0418aea4..016884e425f7f4ba6982be6332a3b60af3e42c87 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/libpq/be-fsstubs.c,v 1.77 2004/12/31 21:59:50 pgsql Exp $
+ *   $PostgreSQL: pgsql/src/backend/libpq/be-fsstubs.c,v 1.78 2005/06/13 02:26:48 tgl Exp $
  *
  * NOTES
  *   This should be moved to a more appropriate place.  It is here
@@ -195,6 +195,12 @@ lo_write(int fd, char *buf, int len)
        return -1;
    }
 
+   if ((cookies[fd]->flags & IFS_WRLOCK) == 0)
+       ereport(ERROR,
+               (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                errmsg("large object descriptor %d was not opened for writing",
+                       fd)));
+
    Assert(fscxt != NULL);
    currentContext = MemoryContextSwitchTo(fscxt);
 
@@ -236,26 +242,33 @@ lo_lseek(PG_FUNCTION_ARGS)
 Datum
 lo_creat(PG_FUNCTION_ARGS)
 {
-   int32       mode = PG_GETARG_INT32(0);
-   LargeObjectDesc *lobjDesc;
-   MemoryContext currentContext;
    Oid         lobjId;
+   MemoryContext currentContext;
 
+   /* do we actually need fscxt for this? */
    CreateFSContext();
 
    currentContext = MemoryContextSwitchTo(fscxt);
 
-   lobjDesc = inv_create(mode);
+   lobjId = inv_create(InvalidOid);
 
-   if (lobjDesc == NULL)
-   {
-       MemoryContextSwitchTo(currentContext);
-       PG_RETURN_OID(InvalidOid);
-   }
+   MemoryContextSwitchTo(currentContext);
+
+   PG_RETURN_OID(lobjId);
+}
+
+Datum
+lo_create(PG_FUNCTION_ARGS)
+{
+   Oid         lobjId = PG_GETARG_OID(0);
+   MemoryContext currentContext;
 
-   lobjId = lobjDesc->id;
+   /* do we actually need fscxt for this? */
+   CreateFSContext();
+
+   currentContext = MemoryContextSwitchTo(fscxt);
 
-   inv_close(lobjDesc);
+   lobjId = inv_create(lobjId);
 
    MemoryContextSwitchTo(currentContext);
 
@@ -403,12 +416,13 @@ lo_import(PG_FUNCTION_ARGS)
    /*
     * create an inversion object
     */
-   lobj = inv_create(INV_READ | INV_WRITE);
-   lobjOid = lobj->id;
+   lobjOid = inv_create(InvalidOid);
 
    /*
-    * read in from the filesystem and write to the inversion file
+    * read in from the filesystem and write to the inversion object
     */
+   lobj = inv_open(lobjOid, INV_WRITE);
+
    while ((nbytes = FileRead(fd, buf, BUFSIZE)) > 0)
    {
        tmp = inv_write(lobj, buf, nbytes);
@@ -421,8 +435,8 @@ lo_import(PG_FUNCTION_ARGS)
                 errmsg("could not read server file \"%s\": %m",
                        fnamebuf)));
 
-   FileClose(fd);
    inv_close(lobj);
+   FileClose(fd);
 
    PG_RETURN_OID(lobjOid);
 }
index 299e0756d1f92b8909c73652af59a8b80aed7123..38734a5f9c37cae2e6f873d03fcf629571366b57 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.110 2005/04/14 20:03:25 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.111 2005/06/13 02:26:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -114,6 +114,42 @@ close_lo_relation(bool isCommit)
 }
 
 
+/*
+ * Same as pg_largeobject.c's LargeObjectExists(), except snapshot to
+ * read with can be specified.
+ */
+static bool
+myLargeObjectExists(Oid loid, Snapshot snapshot)
+{
+   bool        retval = false;
+   Relation    pg_largeobject;
+   ScanKeyData skey[1];
+   SysScanDesc sd;
+
+   /*
+    * See if we can find any tuples belonging to the specified LO
+    */
+   ScanKeyInit(&skey[0],
+               Anum_pg_largeobject_loid,
+               BTEqualStrategyNumber, F_OIDEQ,
+               ObjectIdGetDatum(loid));
+
+   pg_largeobject = heap_open(LargeObjectRelationId, AccessShareLock);
+
+   sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, true,
+                           snapshot, 1, skey);
+
+   if (systable_getnext(sd) != NULL)
+       retval = true;
+
+   systable_endscan(sd);
+
+   heap_close(pg_largeobject, AccessShareLock);
+
+   return retval;
+}
+
+
 static int32
 getbytealen(bytea *data)
 {
@@ -125,58 +161,44 @@ getbytealen(bytea *data)
 
 
 /*
- * inv_create -- create a new large object.
+ * inv_create -- create a new large object
  *
- *     Arguments:
- *       flags
+ * Arguments:
+ *   lobjId - OID to use for new large object, or InvalidOid to pick one
  *
- *     Returns:
- *       large object descriptor, appropriately filled in.
+ * Returns:
+ *   OID of new object
+ *
+ * If lobjId is not InvalidOid, then an error occurs if the OID is already
+ * in use.
  */
-LargeObjectDesc *
-inv_create(int flags)
+Oid
+inv_create(Oid lobjId)
 {
-   Oid         file_oid;
-   LargeObjectDesc *retval;
-
    /*
-    * Allocate an OID to be the LO's identifier.
+    * Allocate an OID to be the LO's identifier, unless we were told
+    * what to use.  In event of collision with an existing ID, loop
+    * to find a free one.
     */
-   file_oid = newoid();
-
-   /* Check for duplicate (shouldn't happen) */
-   if (LargeObjectExists(file_oid))
-       elog(ERROR, "large object %u already exists", file_oid);
+   if (!OidIsValid(lobjId))
+   {
+       do {
+           lobjId = newoid();
+       } while (LargeObjectExists(lobjId));
+   }
 
    /*
     * Create the LO by writing an empty first page for it in
-    * pg_largeobject
+    * pg_largeobject (will fail if duplicate)
     */
-   LargeObjectCreate(file_oid);
+   LargeObjectCreate(lobjId);
 
    /*
-    * Advance command counter so that new tuple will be seen by later
-    * large-object operations in this transaction.
+    * Advance command counter to make new tuple visible to later operations.
     */
    CommandCounterIncrement();
 
-   /*
-    * Prepare LargeObjectDesc data structure for accessing LO
-    */
-   retval = (LargeObjectDesc *) palloc(sizeof(LargeObjectDesc));
-
-   retval->id = file_oid;
-   retval->subid = GetCurrentSubTransactionId();
-   retval->offset = 0;
-
-   if (flags & INV_WRITE)
-       retval->flags = IFS_WRLOCK | IFS_RDLOCK;
-   else if (flags & INV_READ)
-       retval->flags = IFS_RDLOCK;
-   else
-       elog(ERROR, "invalid flags: %d", flags);
-
-   return retval;
+   return lobjId;
 }
 
 /*
@@ -190,11 +212,6 @@ inv_open(Oid lobjId, int flags)
 {
    LargeObjectDesc *retval;
 
-   if (!LargeObjectExists(lobjId))
-       ereport(ERROR,
-               (errcode(ERRCODE_UNDEFINED_OBJECT),
-                errmsg("large object %u does not exist", lobjId)));
-
    retval = (LargeObjectDesc *) palloc(sizeof(LargeObjectDesc));
 
    retval->id = lobjId;
@@ -202,12 +219,25 @@ inv_open(Oid lobjId, int flags)
    retval->offset = 0;
 
    if (flags & INV_WRITE)
+   {
+       retval->snapshot = SnapshotNow;
        retval->flags = IFS_WRLOCK | IFS_RDLOCK;
+   }
    else if (flags & INV_READ)
+   {
+       /* be sure to copy snap into fscxt */
+       retval->snapshot = CopySnapshot(ActiveSnapshot);
        retval->flags = IFS_RDLOCK;
+   }
    else
        elog(ERROR, "invalid flags: %d", flags);
 
+   /* Can't use LargeObjectExists here because it always uses SnapshotNow */
+   if (!myLargeObjectExists(lobjId, retval->snapshot))
+       ereport(ERROR,
+               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                errmsg("large object %u does not exist", lobjId)));
+
    return retval;
 }
 
@@ -218,6 +248,8 @@ void
 inv_close(LargeObjectDesc *obj_desc)
 {
    Assert(PointerIsValid(obj_desc));
+   if (obj_desc->snapshot != SnapshotNow)
+       FreeSnapshot(obj_desc->snapshot);
    pfree(obj_desc);
 }
 
@@ -268,7 +300,7 @@ inv_getsize(LargeObjectDesc *obj_desc)
                ObjectIdGetDatum(obj_desc->id));
 
    sd = index_beginscan(lo_heap_r, lo_index_r,
-                        SnapshotNow, 1, skey);
+                        obj_desc->snapshot, 1, skey);
 
    /*
     * Because the pg_largeobject index is on both loid and pageno, but we
@@ -379,7 +411,7 @@ inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
                Int32GetDatum(pageno));
 
    sd = index_beginscan(lo_heap_r, lo_index_r,
-                        SnapshotNow, 2, skey);
+                        obj_desc->snapshot, 2, skey);
 
    while ((tuple = index_getnext(sd, ForwardScanDirection)) != NULL)
    {
@@ -470,6 +502,13 @@ inv_write(LargeObjectDesc *obj_desc, char *buf, int nbytes)
    Assert(PointerIsValid(obj_desc));
    Assert(buf != NULL);
 
+   /* enforce writability because snapshot is probably wrong otherwise */
+   if ((obj_desc->flags & IFS_WRLOCK) == 0)
+       ereport(ERROR,
+               (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                errmsg("large object %u was not opened for writing",
+                       obj_desc->id)));
+
    if (nbytes <= 0)
        return 0;
 
@@ -488,7 +527,7 @@ inv_write(LargeObjectDesc *obj_desc, char *buf, int nbytes)
                Int32GetDatum(pageno));
 
    sd = index_beginscan(lo_heap_r, lo_index_r,
-                        SnapshotNow, 2, skey);
+                        obj_desc->snapshot, 2, skey);
 
    oldtuple = NULL;
    olddata = NULL;
index a58ec7b5a57c0a64119d266b2a09156cae253351..ee8b3aee0a28787982dd8c31a6a55c097b83c90a 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.273 2005/06/07 07:08:34 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.274 2005/06/13 02:26:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 200506071
+#define CATALOG_VERSION_NO 200506121
 
 #endif
index 5bf7753dcea661f1f0b0d1da31a9856963f0d269..bc5d8afefaf266c349a2468e695daf14056f47c9 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.365 2005/06/09 16:35:09 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.366 2005/06/13 02:26:50 tgl Exp $
  *
  * NOTES
  *   The script catalog/genbki.sh reads this file and generates .bki
@@ -1248,6 +1248,8 @@ DATA(insert OID = 956 (  lo_lseek        PGNSP PGUID 12 f f t f v 3 23 "23 23 23" _
 DESCR("large object seek");
 DATA(insert OID = 957 (  lo_creat         PGNSP PGUID 12 f f t f v 1 26 "23" _null_ _null_ _null_  lo_creat - _null_ ));
 DESCR("large object create");
+DATA(insert OID = 715 (  lo_create        PGNSP PGUID 12 f f t f v 1 26 "26" _null_ _null_ _null_  lo_create - _null_ ));
+DESCR("large object create");
 DATA(insert OID = 958 (  lo_tell          PGNSP PGUID 12 f f t f v 1 23 "23" _null_ _null_ _null_  lo_tell - _null_ ));
 DESCR("large object position");
 
index 0e40374e7d3ab8e5b2b6b57373923aad829f0a9b..1307293b2570a048368a8ae000fb5c7a98f904b5 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/libpq/be-fsstubs.h,v 1.23 2004/12/31 22:03:32 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/libpq/be-fsstubs.h,v 1.24 2005/06/13 02:26:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,6 +23,7 @@ extern Datum lo_import(PG_FUNCTION_ARGS);
 extern Datum lo_export(PG_FUNCTION_ARGS);
 
 extern Datum lo_creat(PG_FUNCTION_ARGS);
+extern Datum lo_create(PG_FUNCTION_ARGS);
 
 extern Datum lo_open(PG_FUNCTION_ARGS);
 extern Datum lo_close(PG_FUNCTION_ARGS);
index 758fb6f039834e91cb2789139fd8fe14c9829eab..c9795d2f7a31f77d105e53990fba4762a30819a1 100644 (file)
@@ -8,19 +8,22 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/storage/large_object.h,v 1.31 2004/12/31 22:03:42 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/storage/large_object.h,v 1.32 2005/06/13 02:26:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef LARGE_OBJECT_H
 #define LARGE_OBJECT_H
 
+#include "utils/tqual.h"
+
 
 /*----------
  * Data about a currently-open large object.
  *
  * id is the logical OID of the large object
- * subid is the subtransaction that opened the LO (or currently owns it)
+ * snapshot is the snapshot to use for read/write operations
+ * subid is the subtransaction that opened the desc (or currently owns it)
  * offset is the current seek offset within the LO
  * flags contains some flag bits
  *
@@ -32,6 +35,7 @@
 typedef struct LargeObjectDesc
 {
    Oid         id;             /* LO's identifier */
+   Snapshot    snapshot;       /* snapshot to use */
    SubTransactionId subid;     /* owning subtransaction ID */
    uint32      offset;         /* current seek pointer */
    int         flags;          /* locking info, etc */
@@ -39,6 +43,7 @@ typedef struct LargeObjectDesc
 /* flag bits: */
 #define IFS_RDLOCK     (1 << 0)
 #define IFS_WRLOCK     (1 << 1)
+
 } LargeObjectDesc;
 
 
@@ -65,7 +70,7 @@ typedef struct LargeObjectDesc
 
 /* inversion stuff in inv_api.c */
 extern void close_lo_relation(bool isCommit);
-extern LargeObjectDesc *inv_create(int flags);
+extern Oid inv_create(Oid lobjId);
 extern LargeObjectDesc *inv_open(Oid lobjId, int flags);
 extern void inv_close(LargeObjectDesc *obj_desc);
 extern int inv_drop(Oid lobjId);
index 363764fffe9cc7452c9879649663e85004eec96f..8d0c4b9743ca0af4d98d5ddd282639468f01f0b6 100644 (file)
@@ -1,4 +1,4 @@
-# $PostgreSQL: pgsql/src/interfaces/libpq/exports.txt,v 1.3 2004/10/30 23:11:26 tgl Exp $
+# $PostgreSQL: pgsql/src/interfaces/libpq/exports.txt,v 1.4 2005/06/13 02:26:53 tgl Exp $
 # Functions to be exported by libpq DLLs
 PQconnectdb               1
 PQsetdbLogin              2
@@ -122,3 +122,4 @@ PQsendPrepare             119
 PQgetCancel               120
 PQfreeCancel              121
 PQcancel                  122
+lo_create                 123
index 30c77e98a539265d354cda0c806176d31eeb2205..665efe90bc9f52b68576ad8c7f98a2baaa6cbc37 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/interfaces/libpq/fe-lobj.c,v 1.52 2004/12/31 22:03:50 pgsql Exp $
+ *   $PostgreSQL: pgsql/src/interfaces/libpq/fe-lobj.c,v 1.53 2005/06/13 02:26:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -266,12 +266,11 @@ lo_lseek(PGconn *conn, int fd, int offset, int whence)
 /*
  * lo_creat
  *   create a new large object
- * the mode is a bitmask describing different attributes of the new object
+ * the mode is ignored (once upon a time it had a use)
  *
  * returns the oid of the large object created or
  * InvalidOid upon failure
  */
-
 Oid
 lo_creat(PGconn *conn, int mode)
 {
@@ -303,6 +302,53 @@ lo_creat(PGconn *conn, int mode)
    }
 }
 
+/*
+ * lo_create
+ *   create a new large object
+ * if lobjId isn't InvalidOid, it specifies the OID to (attempt to) create
+ *
+ * returns the oid of the large object created or
+ * InvalidOid upon failure
+ */
+Oid
+lo_create(PGconn *conn, Oid lobjId)
+{
+   PQArgBlock  argv[1];
+   PGresult   *res;
+   int         retval;
+   int         result_len;
+
+   if (conn->lobjfuncs == NULL)
+   {
+       if (lo_initialize(conn) < 0)
+           return InvalidOid;
+   }
+
+   /* Must check this on-the-fly because it's not there pre-8.1 */
+   if (conn->lobjfuncs->fn_lo_create == 0)
+   {
+       printfPQExpBuffer(&conn->errorMessage,
+          libpq_gettext("cannot determine OID of function lo_create\n"));
+       return InvalidOid;
+   }
+
+   argv[0].isint = 1;
+   argv[0].len = 4;
+   argv[0].u.integer = lobjId;
+   res = PQfn(conn, conn->lobjfuncs->fn_lo_create,
+              &retval, &result_len, 1, argv, 1);
+   if (PQresultStatus(res) == PGRES_COMMAND_OK)
+   {
+       PQclear(res);
+       return (Oid) retval;
+   }
+   else
+   {
+       PQclear(res);
+       return InvalidOid;
+   }
+}
+
 
 /*
  * lo_tell
@@ -560,7 +606,8 @@ lo_initialize(PGconn *conn)
 
    /*
     * Execute the query to get all the functions at once.  In 7.3 and
-    * later we need to be schema-safe.
+    * later we need to be schema-safe.  lo_create only exists in 8.1
+    * and up.
     */
    if (conn->sversion >= 70300)
        query = "select proname, oid from pg_catalog.pg_proc "
@@ -568,6 +615,7 @@ lo_initialize(PGconn *conn)
            "'lo_open', "
            "'lo_close', "
            "'lo_creat', "
+           "'lo_create', "
            "'lo_unlink', "
            "'lo_lseek', "
            "'lo_tell', "
@@ -615,6 +663,8 @@ lo_initialize(PGconn *conn)
            lobjfuncs->fn_lo_close = foid;
        else if (!strcmp(fname, "lo_creat"))
            lobjfuncs->fn_lo_creat = foid;
+       else if (!strcmp(fname, "lo_create"))
+           lobjfuncs->fn_lo_create = foid;
        else if (!strcmp(fname, "lo_unlink"))
            lobjfuncs->fn_lo_unlink = foid;
        else if (!strcmp(fname, "lo_lseek"))
@@ -631,7 +681,7 @@ lo_initialize(PGconn *conn)
 
    /*
     * Finally check that we really got all large object interface
-    * functions.
+    * functions --- except lo_create, which may not exist.
     */
    if (lobjfuncs->fn_lo_open == 0)
    {
index 8976d45e2583a11385946928cbbbf397d48b1bad..56fba44f8bb4a45ff27f6aac33a0aaa8697ddea6 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.117 2005/06/09 20:01:16 tgl Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.118 2005/06/13 02:26:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -480,6 +480,7 @@ 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 Oid lo_creat(PGconn *conn, int mode);
+extern Oid lo_create(PGconn *conn, Oid lobjId);
 extern int lo_tell(PGconn *conn, int fd);
 extern int lo_unlink(PGconn *conn, Oid lobjId);
 extern Oid lo_import(PGconn *conn, const char *filename);
index 6e14fa8df25ca3399efb33ea33813d91eef2f755..2274efbfb546ee766ee4d8598972b44839c9a5fb 100644 (file)
@@ -12,7 +12,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.102 2005/06/12 00:00:21 neilc Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.103 2005/06/13 02:26:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -229,6 +229,7 @@ typedef struct pgLobjfuncs
    Oid         fn_lo_open;     /* OID of backend function lo_open      */
    Oid         fn_lo_close;    /* OID of backend function lo_close     */
    Oid         fn_lo_creat;    /* OID of backend function lo_creat     */
+   Oid         fn_lo_create;   /* OID of backend function lo_create    */
    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      */