#include <sys/stat.h>
#include <unistd.h>
+#include "access/xact.h"
#include "libpq/be-fsstubs.h"
#include "libpq/libpq-fs.h"
#include "miscadmin.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
+#include "utils/snapmgr.h"
/* define this to enable debug logging */
/* #define FSDB 1 */
static LargeObjectDesc **cookies = NULL;
static int cookies_size = 0;
+static bool lo_cleanup_needed = false;
static MemoryContext fscxt = NULL;
-#define CreateFSContext() \
- do { \
- if (fscxt == NULL) \
- fscxt = AllocSetContextCreate(TopMemoryContext, \
- "Filesystem", \
- ALLOCSET_DEFAULT_SIZES); \
- } while (0)
-
-
-static int newLOfd(LargeObjectDesc *lobjCookie);
-static void deleteLOfd(int fd);
+static int newLOfd(void);
+static void closeLOfd(int fd);
static Oid lo_import_internal(text *filename, Oid lobjOid);
elog(DEBUG4, "lo_open(%u,%d)", lobjId, mode);
#endif
- CreateFSContext();
+ /*
+ * Allocate a large object descriptor first. This will also create
+ * 'fscxt' if this is the first LO opened in this transaction.
+ */
+ fd = newLOfd();
lobjDesc = inv_open(lobjId, mode, fscxt);
+ lobjDesc->subid = GetCurrentSubTransactionId();
+
+ /*
+ * We must register the snapshot in TopTransaction's resowner so that it
+ * stays alive until the LO is closed rather than until the current portal
+ * shuts down.
+ */
+ if (lobjDesc->snapshot)
+ lobjDesc->snapshot = RegisterSnapshotOnOwner(lobjDesc->snapshot,
+ TopTransactionResourceOwner);
- fd = newLOfd(lobjDesc);
+ Assert(cookies[fd] == NULL);
+ cookies[fd] = lobjDesc;
PG_RETURN_INT32(fd);
}
elog(DEBUG4, "lo_close(%d)", fd);
#endif
- inv_close(cookies[fd]);
-
- deleteLOfd(fd);
+ closeLOfd(fd);
PG_RETURN_INT32(0);
}
{
Oid lobjId;
- /*
- * We don't actually need to store into fscxt, but create it anyway to
- * ensure that AtEOXact_LargeObject knows there is state to clean up
- */
- CreateFSContext();
-
+ lo_cleanup_needed = true;
lobjId = inv_create(InvalidOid);
PG_RETURN_OID(lobjId);
{
Oid lobjId = PG_GETARG_OID(0);
- /*
- * We don't actually need to store into fscxt, but create it anyway to
- * ensure that AtEOXact_LargeObject knows there is state to clean up
- */
- CreateFSContext();
-
+ lo_cleanup_needed = true;
lobjId = inv_create(lobjId);
PG_RETURN_OID(lobjId);
for (i = 0; i < cookies_size; i++)
{
if (cookies[i] != NULL && cookies[i]->id == lobjId)
- {
- inv_close(cookies[i]);
- deleteLOfd(i);
- }
+ closeLOfd(i);
}
}
/*
* inv_drop does not create a need for end-of-transaction cleanup and
- * hence we don't need to have created fscxt.
+ * hence we don't need to set lo_cleanup_needed.
*/
PG_RETURN_INT32(inv_drop(lobjId));
}
LargeObjectDesc *lobj;
Oid oid;
- CreateFSContext();
-
/*
* open the file to be read in
*/
/*
* create an inversion object
*/
+ lo_cleanup_needed = true;
oid = inv_create(lobjOid);
/*
* read in from the filesystem and write to the inversion object
*/
- lobj = inv_open(oid, INV_WRITE, fscxt);
+ lobj = inv_open(oid, INV_WRITE, CurrentMemoryContext);
while ((nbytes = read(fd, buf, BUFSIZE)) > 0)
{
LargeObjectDesc *lobj;
mode_t oumask;
- CreateFSContext();
-
/*
* open the inversion object (no need to test for failure)
*/
- lobj = inv_open(lobjId, INV_READ, fscxt);
+ lo_cleanup_needed = true;
+ lobj = inv_open(lobjId, INV_READ, CurrentMemoryContext);
/*
* open the file to be written to
{
int i;
- if (fscxt == NULL)
+ if (!lo_cleanup_needed)
return; /* no LO operations in this xact */
/*
* Close LO fds and clear cookies array so that LO fds are no longer good.
- * On abort we skip the close step.
+ * The memory context and resource owner holding them are going away at
+ * the end-of-transaction anyway, but on commit, we need to close them to
+ * avoid warnings about leaked resources at commit. On abort we can skip
+ * this step.
*/
- for (i = 0; i < cookies_size; i++)
+ if (isCommit)
{
- if (cookies[i] != NULL)
+ for (i = 0; i < cookies_size; i++)
{
- if (isCommit)
- inv_close(cookies[i]);
- deleteLOfd(i);
+ if (cookies[i] != NULL)
+ closeLOfd(i);
}
}
cookies_size = 0;
/* Release the LO memory context to prevent permanent memory leaks. */
- MemoryContextDelete(fscxt);
+ if (fscxt)
+ MemoryContextDelete(fscxt);
fscxt = NULL;
/* Give inv_api.c a chance to clean up, too */
close_lo_relation(isCommit);
+
+ lo_cleanup_needed = false;
}
/*
if (isCommit)
lo->subid = parentSubid;
else
- {
- /*
- * Make sure we do not call inv_close twice if it errors out
- * for some reason. Better a leak than a crash.
- */
- deleteLOfd(i);
- inv_close(lo);
- }
+ closeLOfd(i);
}
}
}
*****************************************************************************/
static int
-newLOfd(LargeObjectDesc *lobjCookie)
+newLOfd(void)
{
int i,
newsize;
+ lo_cleanup_needed = true;
+ if (fscxt == NULL)
+ fscxt = AllocSetContextCreate(TopMemoryContext,
+ "Filesystem",
+ ALLOCSET_DEFAULT_SIZES);
+
/* Try to find a free slot */
for (i = 0; i < cookies_size; i++)
{
if (cookies[i] == NULL)
- {
- cookies[i] = lobjCookie;
return i;
- }
}
/* No free slot, so make the array bigger */
cookies_size = newsize;
}
- Assert(cookies[i] == NULL);
- cookies[i] = lobjCookie;
return i;
}
static void
-deleteLOfd(int fd)
+closeLOfd(int fd)
{
+ LargeObjectDesc *lobj;
+
+ /*
+ * Make sure we do not try to free twice if this errors out for some
+ * reason. Better a leak than a crash.
+ */
+ lobj = cookies[fd];
cookies[fd] = NULL;
+
+ if (lobj->snapshot)
+ UnregisterSnapshotFromOwner(lobj->snapshot,
+ TopTransactionResourceOwner);
+ inv_close(lobj);
}
/*****************************************************************************
int total_read PG_USED_FOR_ASSERTS_ONLY;
bytea *result = NULL;
- /*
- * We don't actually need to store into fscxt, but create it anyway to
- * ensure that AtEOXact_LargeObject knows there is state to clean up
- */
- CreateFSContext();
-
- loDesc = inv_open(loOid, INV_READ, fscxt);
+ lo_cleanup_needed = true;
+ loDesc = inv_open(loOid, INV_READ, CurrentMemoryContext);
/*
* Compute number of bytes we'll actually read, accommodating nbytes == -1
LargeObjectDesc *loDesc;
int written PG_USED_FOR_ASSERTS_ONLY;
- CreateFSContext();
-
+ lo_cleanup_needed = true;
loOid = inv_create(loOid);
- loDesc = inv_open(loOid, INV_WRITE, fscxt);
+ loDesc = inv_open(loOid, INV_WRITE, CurrentMemoryContext);
written = inv_write(loDesc, VARDATA_ANY(str), VARSIZE_ANY_EXHDR(str));
Assert(written == VARSIZE_ANY_EXHDR(str));
inv_close(loDesc);
LargeObjectDesc *loDesc;
int written PG_USED_FOR_ASSERTS_ONLY;
- CreateFSContext();
-
- loDesc = inv_open(loOid, INV_WRITE, fscxt);
+ lo_cleanup_needed = true;
+ loDesc = inv_open(loOid, INV_WRITE, CurrentMemoryContext);
/* Permission check */
if (!lo_compat_privileges &&
/*
* inv_open -- access an existing large object.
*
- * Returns:
- * Large object descriptor, appropriately filled in. The descriptor
- * and subsidiary data are allocated in the specified memory context,
- * which must be suitably long-lived for the caller's purposes.
+ * Returns a large object descriptor, appropriately filled in.
+ * The descriptor and subsidiary data are allocated in the specified
+ * memory context, which must be suitably long-lived for the caller's
+ * purposes. If the returned descriptor has a snapshot associated
+ * with it, the caller must ensure that it also lives long enough,
+ * e.g. by calling RegisterSnapshotOnOwner
*/
LargeObjectDesc *
inv_open(Oid lobjId, int flags, MemoryContext mcxt)
retval = (LargeObjectDesc *) MemoryContextAlloc(mcxt,
sizeof(LargeObjectDesc));
retval->id = lobjId;
- retval->subid = GetCurrentSubTransactionId();
retval->offset = 0;
retval->flags = descflags;
+ /* caller sets if needed, not used by the functions in this file */
+ retval->subid = InvalidSubTransactionId;
+
/*
- * We must register the snapshot in TopTransaction's resowner, because it
- * must stay alive until the LO is closed rather than until the current
- * portal shuts down. Do this last to avoid uselessly leaking the
- * snapshot if an error is thrown above.
+ * The snapshot (if any) is just the currently active snapshot. The
+ * caller will replace it with a longer-lived copy if needed.
*/
- if (snapshot)
- snapshot = RegisterSnapshotOnOwner(snapshot,
- TopTransactionResourceOwner);
retval->snapshot = snapshot;
return retval;
inv_close(LargeObjectDesc *obj_desc)
{
Assert(PointerIsValid(obj_desc));
-
- UnregisterSnapshotFromOwner(obj_desc->snapshot,
- TopTransactionResourceOwner);
-
pfree(obj_desc);
}