Adjust lo_open() so that specifying INV_READ without INV_WRITE creates

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).
This commit is contained in:
Tom Lane 2005-06-13 02:26:53 +00:00
parent f52a34229b
commit a2fb7b8a1f
11 changed files with 265 additions and 96 deletions

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/lobj.sgml,v 1.36 2005/01/10 00:04:38 tgl Exp $
$PostgreSQL: pgsql/doc/src/sgml/lobj.sgml,v 1.37 2005/06/13 02:26:46 tgl Exp $
-->
<chapter id="largeObjects">
@ -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);
</synopsis>
<indexterm><primary>lo_creat</></>
creates a new large object.
<replaceable class="parameter">mode</replaceable> is a bit mask
describing several different attributes of the new
object. The symbolic constants used here are defined
in the header file <filename>libpq/libpq-fs.h</filename>.
The access type (read, write, or both) is controlled by
or'ing together the bits <symbol>INV_READ</symbol> and
<symbol>INV_WRITE</symbol>. 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.
<replaceable class="parameter">mode</replaceable> is unused and
ignored as of <productname>PostgreSQL</productname> 8.1; however, for
backwards compatibility with earlier releases it is best to
set it to <symbol>INV_READ</symbol>, <symbol>INV_WRITE</symbol>,
or <symbol>INV_READ</symbol> <literal>|</> <symbol>INV_WRITE</symbol>.
(These symbolic constants are defined
in the header file <filename>libpq/libpq-fs.h</filename>.)
</para>
<para>
An example:
<programlisting>
inv_oid = lo_creat(conn, INV_READ|INV_WRITE);
</programlisting>
</para>
<para>
The function
<synopsis>
Oid lo_create(PGconn *conn, Oid lobjId);
</synopsis>
<indexterm><primary>lo_create</></>
also creates a new large object. The OID to be assigned can be
specified by <replaceable class="parameter">lobjId</replaceable>;
if so, failure occurs if that OID is already in use for some large
object. If <replaceable class="parameter">lobjId</replaceable>
is InvalidOid (zero) then <function>lo_create</> assigns an unused
OID (this is the same behavior as <function>lo_creat</>).
The return value is the OID that was assigned to the new large object,
or InvalidOid (zero) on failure.
</para>
<para>
<function>lo_create</> is new as of <productname>PostgreSQL</productname>
8.1; if this function is run against an older server version, it will
fail and return InvalidOid.
</para>
<para>
An example:
<programlisting>
inv_oid = lo_create(conn, desired_oid);
</programlisting>
</para>
</sect2>
@ -186,11 +212,13 @@ int lo_export(PGconn *conn, Oid lobjId, const char *filename);
int lo_open(PGconn *conn, Oid lobjId, int mode);
</synopsis>
<indexterm><primary>lo_open</></>
The <parameter>lobjId</parameter> argument specifies the OID of the large
object to open. The <parameter>mode</parameter> bits control whether the
object is opened for reading (<symbol>INV_READ</>), writing (<symbol>INV_WRITE</symbol>), or
both.
A large object cannot be opened before it is created.
The <parameter>lobjId</parameter> argument specifies the OID of the large
object to open. The <parameter>mode</parameter> bits control whether the
object is opened for reading (<symbol>INV_READ</>), writing
(<symbol>INV_WRITE</symbol>), or both.
(These symbolic constants are defined
in the header file <filename>libpq/libpq-fs.h</filename>.)
A large object cannot be opened before it is created.
<function>lo_open</function> returns a (non-negative) large object
descriptor for later use in <function>lo_read</function>,
<function>lo_write</function>, <function>lo_lseek</function>,
@ -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.
</para>
</para>
<para>
The server currently does not distinguish between modes
<symbol>INV_WRITE</symbol> and <symbol>INV_READ</> <literal>|</>
<symbol>INV_WRITE</symbol>: you are allowed to read from the descriptor
in either case. However there is a significant difference between
these modes and <symbol>INV_READ</> alone: with <symbol>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 <function>lo_open</> was executed,
regardless of later writes by this or other transactions. Reading
from a descriptor opened with <symbol>INV_WRITE</symbol> 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 <literal>SERIALIZABLE</> versus <literal>READ COMMITTED</> transaction
modes for ordinary SQL <command>SELECT</> commands.
</para>
<para>
An example:
<programlisting>
inv_fd = lo_open(conn, inv_oid, INV_READ|INV_WRITE);
</programlisting>
</para>
</sect2>
<sect2>
@ -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
<function>lo_creat</function><indexterm><primary>lo_creat</></>,
<function>lo_create</function><indexterm><primary>lo_create</></>,
<function>lo_unlink</function><indexterm><primary>lo_unlink</></>,
<function>lo_import</function><indexterm><primary>lo_import</></>, and
<function>lo_export</function><indexterm><primary>lo_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)

View 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);
lobjId = lobjDesc->id;
PG_RETURN_OID(lobjId);
}
inv_close(lobjDesc);
Datum
lo_create(PG_FUNCTION_ARGS)
{
Oid lobjId = PG_GETARG_OID(0);
MemoryContext currentContext;
/* do we actually need fscxt for this? */
CreateFSContext();
currentContext = MemoryContextSwitchTo(fscxt);
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);
}

View 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;

View 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

View 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");

View 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);

View 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);

View 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

View 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)
{

View 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);

View 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 */