<!--
-$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">
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>
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>,
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>
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</></>.
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)
*
*
* 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
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);
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);
/*
* 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);
errmsg("could not read server file \"%s\": %m",
fnamebuf)));
- FileClose(fd);
inv_close(lobj);
+ FileClose(fd);
PG_RETURN_OID(lobjOid);
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
}
+/*
+ * 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)
{
/*
- * 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;
}
/*
{
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;
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;
}
inv_close(LargeObjectDesc *obj_desc)
{
Assert(PointerIsValid(obj_desc));
+ if (obj_desc->snapshot != SnapshotNow)
+ FreeSnapshot(obj_desc->snapshot);
pfree(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
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)
{
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;
Int32GetDatum(pageno));
sd = index_beginscan(lo_heap_r, lo_index_r,
- SnapshotNow, 2, skey);
+ obj_desc->snapshot, 2, skey);
oldtuple = NULL;
olddata = NULL;
* 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 $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200506071
+#define CATALOG_VERSION_NO 200506121
#endif
* 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
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");
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
* 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
*
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 */
/* flag bits: */
#define IFS_RDLOCK (1 << 0)
#define IFS_WRLOCK (1 << 1)
+
} 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);
-# $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
PQgetCancel 120
PQfreeCancel 121
PQcancel 122
+lo_create 123
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/*
* 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)
{
}
}
+/*
+ * 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
/*
* 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 "
"'lo_open', "
"'lo_close', "
"'lo_creat', "
+ "'lo_create', "
"'lo_unlink', "
"'lo_lseek', "
"'lo_tell', "
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"))
/*
* 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)
{
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
* 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 $
*
*-------------------------------------------------------------------------
*/
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 */