Header And Logo

PostgreSQL
| The world's most advanced open source database.

be-fsstubs.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * be-fsstubs.c
00004  *    Builtin functions for open/close/read/write operations on large objects
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  * Portions Copyright (c) 1994, Regents of the University of California
00008  *
00009  *
00010  * IDENTIFICATION
00011  *    src/backend/libpq/be-fsstubs.c
00012  *
00013  * NOTES
00014  *    This should be moved to a more appropriate place.  It is here
00015  *    for lack of a better place.
00016  *
00017  *    These functions store LargeObjectDesc structs in a private MemoryContext,
00018  *    which means that large object descriptors hang around until we destroy
00019  *    the context at transaction end.  It'd be possible to prolong the lifetime
00020  *    of the context so that LO FDs are good across transactions (for example,
00021  *    we could release the context only if we see that no FDs remain open).
00022  *    But we'd need additional state in order to do the right thing at the
00023  *    end of an aborted transaction.  FDs opened during an aborted xact would
00024  *    still need to be closed, since they might not be pointing at valid
00025  *    relations at all.  Locking semantics are also an interesting problem
00026  *    if LOs stay open across transactions.  For now, we'll stick with the
00027  *    existing documented semantics of LO FDs: they're only good within a
00028  *    transaction.
00029  *
00030  *    As of PostgreSQL 8.0, much of the angst expressed above is no longer
00031  *    relevant, and in fact it'd be pretty easy to allow LO FDs to stay
00032  *    open across transactions.  (Snapshot relevancy would still be an issue.)
00033  *    However backwards compatibility suggests that we should stick to the
00034  *    status quo.
00035  *
00036  *-------------------------------------------------------------------------
00037  */
00038 
00039 #include "postgres.h"
00040 
00041 #include <fcntl.h>
00042 #include <sys/stat.h>
00043 #include <unistd.h>
00044 
00045 #include "libpq/be-fsstubs.h"
00046 #include "libpq/libpq-fs.h"
00047 #include "miscadmin.h"
00048 #include "storage/fd.h"
00049 #include "storage/large_object.h"
00050 #include "utils/acl.h"
00051 #include "utils/builtins.h"
00052 #include "utils/memutils.h"
00053 
00054 /*
00055  * compatibility flag for permission checks
00056  */
00057 bool        lo_compat_privileges;
00058 
00059 /* define this to enable debug logging */
00060 /* #define FSDB 1 */
00061 /* chunk size for lo_import/lo_export transfers */
00062 #define BUFSIZE         8192
00063 
00064 /*
00065  * LO "FD"s are indexes into the cookies array.
00066  *
00067  * A non-null entry is a pointer to a LargeObjectDesc allocated in the
00068  * LO private memory context "fscxt".  The cookies array itself is also
00069  * dynamically allocated in that context.  Its current allocated size is
00070  * cookies_len entries, of which any unused entries will be NULL.
00071  */
00072 static LargeObjectDesc **cookies = NULL;
00073 static int  cookies_size = 0;
00074 
00075 static MemoryContext fscxt = NULL;
00076 
00077 #define CreateFSContext() \
00078     do { \
00079         if (fscxt == NULL) \
00080             fscxt = AllocSetContextCreate(TopMemoryContext, \
00081                                           "Filesystem", \
00082                                           ALLOCSET_DEFAULT_MINSIZE, \
00083                                           ALLOCSET_DEFAULT_INITSIZE, \
00084                                           ALLOCSET_DEFAULT_MAXSIZE); \
00085     } while (0)
00086 
00087 
00088 static int  newLOfd(LargeObjectDesc *lobjCookie);
00089 static void deleteLOfd(int fd);
00090 static Oid  lo_import_internal(text *filename, Oid lobjOid);
00091 
00092 
00093 /*****************************************************************************
00094  *  File Interfaces for Large Objects
00095  *****************************************************************************/
00096 
00097 Datum
00098 lo_open(PG_FUNCTION_ARGS)
00099 {
00100     Oid         lobjId = PG_GETARG_OID(0);
00101     int32       mode = PG_GETARG_INT32(1);
00102     LargeObjectDesc *lobjDesc;
00103     int         fd;
00104 
00105 #if FSDB
00106     elog(DEBUG4, "lo_open(%u,%d)", lobjId, mode);
00107 #endif
00108 
00109     CreateFSContext();
00110 
00111     lobjDesc = inv_open(lobjId, mode, fscxt);
00112 
00113     if (lobjDesc == NULL)
00114     {                           /* lookup failed */
00115 #if FSDB
00116         elog(DEBUG4, "could not open large object %u", lobjId);
00117 #endif
00118         PG_RETURN_INT32(-1);
00119     }
00120 
00121     fd = newLOfd(lobjDesc);
00122 
00123     PG_RETURN_INT32(fd);
00124 }
00125 
00126 Datum
00127 lo_close(PG_FUNCTION_ARGS)
00128 {
00129     int32       fd = PG_GETARG_INT32(0);
00130 
00131     if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
00132         ereport(ERROR,
00133                 (errcode(ERRCODE_UNDEFINED_OBJECT),
00134                  errmsg("invalid large-object descriptor: %d", fd)));
00135 
00136 #if FSDB
00137     elog(DEBUG4, "lo_close(%d)", fd);
00138 #endif
00139 
00140     inv_close(cookies[fd]);
00141 
00142     deleteLOfd(fd);
00143 
00144     PG_RETURN_INT32(0);
00145 }
00146 
00147 
00148 /*****************************************************************************
00149  *  Bare Read/Write operations --- these are not fmgr-callable!
00150  *
00151  *  We assume the large object supports byte oriented reads and seeks so
00152  *  that our work is easier.
00153  *
00154  *****************************************************************************/
00155 
00156 int
00157 lo_read(int fd, char *buf, int len)
00158 {
00159     int         status;
00160     LargeObjectDesc *lobj;
00161 
00162     if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
00163         ereport(ERROR,
00164                 (errcode(ERRCODE_UNDEFINED_OBJECT),
00165                  errmsg("invalid large-object descriptor: %d", fd)));
00166     lobj = cookies[fd];
00167 
00168     /* We don't bother to check IFS_RDLOCK, since it's always set */
00169 
00170     /* Permission checks --- first time through only */
00171     if ((lobj->flags & IFS_RD_PERM_OK) == 0)
00172     {
00173         if (!lo_compat_privileges &&
00174             pg_largeobject_aclcheck_snapshot(lobj->id,
00175                                              GetUserId(),
00176                                              ACL_SELECT,
00177                                              lobj->snapshot) != ACLCHECK_OK)
00178             ereport(ERROR,
00179                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00180                      errmsg("permission denied for large object %u",
00181                             lobj->id)));
00182         lobj->flags |= IFS_RD_PERM_OK;
00183     }
00184 
00185     status = inv_read(lobj, buf, len);
00186 
00187     return status;
00188 }
00189 
00190 int
00191 lo_write(int fd, const char *buf, int len)
00192 {
00193     int         status;
00194     LargeObjectDesc *lobj;
00195 
00196     if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
00197         ereport(ERROR,
00198                 (errcode(ERRCODE_UNDEFINED_OBJECT),
00199                  errmsg("invalid large-object descriptor: %d", fd)));
00200     lobj = cookies[fd];
00201 
00202     if ((lobj->flags & IFS_WRLOCK) == 0)
00203         ereport(ERROR,
00204                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
00205               errmsg("large object descriptor %d was not opened for writing",
00206                      fd)));
00207 
00208     /* Permission checks --- first time through only */
00209     if ((lobj->flags & IFS_WR_PERM_OK) == 0)
00210     {
00211         if (!lo_compat_privileges &&
00212             pg_largeobject_aclcheck_snapshot(lobj->id,
00213                                              GetUserId(),
00214                                              ACL_UPDATE,
00215                                              lobj->snapshot) != ACLCHECK_OK)
00216             ereport(ERROR,
00217                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00218                      errmsg("permission denied for large object %u",
00219                             lobj->id)));
00220         lobj->flags |= IFS_WR_PERM_OK;
00221     }
00222 
00223     status = inv_write(lobj, buf, len);
00224 
00225     return status;
00226 }
00227 
00228 Datum
00229 lo_lseek(PG_FUNCTION_ARGS)
00230 {
00231     int32       fd = PG_GETARG_INT32(0);
00232     int32       offset = PG_GETARG_INT32(1);
00233     int32       whence = PG_GETARG_INT32(2);
00234     int64       status;
00235 
00236     if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
00237         ereport(ERROR,
00238                 (errcode(ERRCODE_UNDEFINED_OBJECT),
00239                  errmsg("invalid large-object descriptor: %d", fd)));
00240 
00241     status = inv_seek(cookies[fd], offset, whence);
00242 
00243     /* guard against result overflow */
00244     if (status != (int32) status)
00245         ereport(ERROR,
00246                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
00247         errmsg("lo_lseek result out of range for large-object descriptor %d",
00248                fd)));
00249 
00250     PG_RETURN_INT32((int32) status);
00251 }
00252 
00253 Datum
00254 lo_lseek64(PG_FUNCTION_ARGS)
00255 {
00256     int32       fd = PG_GETARG_INT32(0);
00257     int64       offset = PG_GETARG_INT64(1);
00258     int32       whence = PG_GETARG_INT32(2);
00259     int64       status;
00260 
00261     if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
00262         ereport(ERROR,
00263                 (errcode(ERRCODE_UNDEFINED_OBJECT),
00264                  errmsg("invalid large-object descriptor: %d", fd)));
00265 
00266     status = inv_seek(cookies[fd], offset, whence);
00267 
00268     PG_RETURN_INT64(status);
00269 }
00270 
00271 Datum
00272 lo_creat(PG_FUNCTION_ARGS)
00273 {
00274     Oid         lobjId;
00275 
00276     /*
00277      * We don't actually need to store into fscxt, but create it anyway to
00278      * ensure that AtEOXact_LargeObject knows there is state to clean up
00279      */
00280     CreateFSContext();
00281 
00282     lobjId = inv_create(InvalidOid);
00283 
00284     PG_RETURN_OID(lobjId);
00285 }
00286 
00287 Datum
00288 lo_create(PG_FUNCTION_ARGS)
00289 {
00290     Oid         lobjId = PG_GETARG_OID(0);
00291 
00292     /*
00293      * We don't actually need to store into fscxt, but create it anyway to
00294      * ensure that AtEOXact_LargeObject knows there is state to clean up
00295      */
00296     CreateFSContext();
00297 
00298     lobjId = inv_create(lobjId);
00299 
00300     PG_RETURN_OID(lobjId);
00301 }
00302 
00303 Datum
00304 lo_tell(PG_FUNCTION_ARGS)
00305 {
00306     int32       fd = PG_GETARG_INT32(0);
00307     int64       offset;
00308 
00309     if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
00310         ereport(ERROR,
00311                 (errcode(ERRCODE_UNDEFINED_OBJECT),
00312                  errmsg("invalid large-object descriptor: %d", fd)));
00313 
00314     offset = inv_tell(cookies[fd]);
00315 
00316     /* guard against result overflow */
00317     if (offset != (int32) offset)
00318         ereport(ERROR,
00319                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
00320          errmsg("lo_tell result out of range for large-object descriptor %d",
00321                 fd)));
00322 
00323     PG_RETURN_INT32((int32) offset);
00324 }
00325 
00326 Datum
00327 lo_tell64(PG_FUNCTION_ARGS)
00328 {
00329     int32       fd = PG_GETARG_INT32(0);
00330     int64       offset;
00331 
00332     if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
00333         ereport(ERROR,
00334                 (errcode(ERRCODE_UNDEFINED_OBJECT),
00335                  errmsg("invalid large-object descriptor: %d", fd)));
00336 
00337     offset = inv_tell(cookies[fd]);
00338 
00339     PG_RETURN_INT64(offset);
00340 }
00341 
00342 Datum
00343 lo_unlink(PG_FUNCTION_ARGS)
00344 {
00345     Oid         lobjId = PG_GETARG_OID(0);
00346 
00347     /* Must be owner of the largeobject */
00348     if (!lo_compat_privileges &&
00349         !pg_largeobject_ownercheck(lobjId, GetUserId()))
00350         ereport(ERROR,
00351                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00352                  errmsg("must be owner of large object %u", lobjId)));
00353 
00354     /*
00355      * If there are any open LO FDs referencing that ID, close 'em.
00356      */
00357     if (fscxt != NULL)
00358     {
00359         int         i;
00360 
00361         for (i = 0; i < cookies_size; i++)
00362         {
00363             if (cookies[i] != NULL && cookies[i]->id == lobjId)
00364             {
00365                 inv_close(cookies[i]);
00366                 deleteLOfd(i);
00367             }
00368         }
00369     }
00370 
00371     /*
00372      * inv_drop does not create a need for end-of-transaction cleanup and
00373      * hence we don't need to have created fscxt.
00374      */
00375     PG_RETURN_INT32(inv_drop(lobjId));
00376 }
00377 
00378 /*****************************************************************************
00379  *  Read/Write using bytea
00380  *****************************************************************************/
00381 
00382 Datum
00383 loread(PG_FUNCTION_ARGS)
00384 {
00385     int32       fd = PG_GETARG_INT32(0);
00386     int32       len = PG_GETARG_INT32(1);
00387     bytea      *retval;
00388     int         totalread;
00389 
00390     if (len < 0)
00391         len = 0;
00392 
00393     retval = (bytea *) palloc(VARHDRSZ + len);
00394     totalread = lo_read(fd, VARDATA(retval), len);
00395     SET_VARSIZE(retval, totalread + VARHDRSZ);
00396 
00397     PG_RETURN_BYTEA_P(retval);
00398 }
00399 
00400 Datum
00401 lowrite(PG_FUNCTION_ARGS)
00402 {
00403     int32       fd = PG_GETARG_INT32(0);
00404     bytea      *wbuf = PG_GETARG_BYTEA_P(1);
00405     int         bytestowrite;
00406     int         totalwritten;
00407 
00408     bytestowrite = VARSIZE(wbuf) - VARHDRSZ;
00409     totalwritten = lo_write(fd, VARDATA(wbuf), bytestowrite);
00410     PG_RETURN_INT32(totalwritten);
00411 }
00412 
00413 /*****************************************************************************
00414  *   Import/Export of Large Object
00415  *****************************************************************************/
00416 
00417 /*
00418  * lo_import -
00419  *    imports a file as an (inversion) large object.
00420  */
00421 Datum
00422 lo_import(PG_FUNCTION_ARGS)
00423 {
00424     text       *filename = PG_GETARG_TEXT_PP(0);
00425 
00426     PG_RETURN_OID(lo_import_internal(filename, InvalidOid));
00427 }
00428 
00429 /*
00430  * lo_import_with_oid -
00431  *    imports a file as an (inversion) large object specifying oid.
00432  */
00433 Datum
00434 lo_import_with_oid(PG_FUNCTION_ARGS)
00435 {
00436     text       *filename = PG_GETARG_TEXT_PP(0);
00437     Oid         oid = PG_GETARG_OID(1);
00438 
00439     PG_RETURN_OID(lo_import_internal(filename, oid));
00440 }
00441 
00442 static Oid
00443 lo_import_internal(text *filename, Oid lobjOid)
00444 {
00445     int         fd;
00446     int         nbytes,
00447                 tmp PG_USED_FOR_ASSERTS_ONLY;
00448     char        buf[BUFSIZE];
00449     char        fnamebuf[MAXPGPATH];
00450     LargeObjectDesc *lobj;
00451     Oid         oid;
00452 
00453 #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS
00454     if (!superuser())
00455         ereport(ERROR,
00456                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00457                  errmsg("must be superuser to use server-side lo_import()"),
00458                  errhint("Anyone can use the client-side lo_import() provided by libpq.")));
00459 #endif
00460 
00461     CreateFSContext();
00462 
00463     /*
00464      * open the file to be read in
00465      */
00466     text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
00467     fd = OpenTransientFile(fnamebuf, O_RDONLY | PG_BINARY, S_IRWXU);
00468     if (fd < 0)
00469         ereport(ERROR,
00470                 (errcode_for_file_access(),
00471                  errmsg("could not open server file \"%s\": %m",
00472                         fnamebuf)));
00473 
00474     /*
00475      * create an inversion object
00476      */
00477     oid = inv_create(lobjOid);
00478 
00479     /*
00480      * read in from the filesystem and write to the inversion object
00481      */
00482     lobj = inv_open(oid, INV_WRITE, fscxt);
00483 
00484     while ((nbytes = read(fd, buf, BUFSIZE)) > 0)
00485     {
00486         tmp = inv_write(lobj, buf, nbytes);
00487         Assert(tmp == nbytes);
00488     }
00489 
00490     if (nbytes < 0)
00491         ereport(ERROR,
00492                 (errcode_for_file_access(),
00493                  errmsg("could not read server file \"%s\": %m",
00494                         fnamebuf)));
00495 
00496     inv_close(lobj);
00497     CloseTransientFile(fd);
00498 
00499     return oid;
00500 }
00501 
00502 /*
00503  * lo_export -
00504  *    exports an (inversion) large object.
00505  */
00506 Datum
00507 lo_export(PG_FUNCTION_ARGS)
00508 {
00509     Oid         lobjId = PG_GETARG_OID(0);
00510     text       *filename = PG_GETARG_TEXT_PP(1);
00511     int         fd;
00512     int         nbytes,
00513                 tmp;
00514     char        buf[BUFSIZE];
00515     char        fnamebuf[MAXPGPATH];
00516     LargeObjectDesc *lobj;
00517     mode_t      oumask;
00518 
00519 #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS
00520     if (!superuser())
00521         ereport(ERROR,
00522                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00523                  errmsg("must be superuser to use server-side lo_export()"),
00524                  errhint("Anyone can use the client-side lo_export() provided by libpq.")));
00525 #endif
00526 
00527     CreateFSContext();
00528 
00529     /*
00530      * open the inversion object (no need to test for failure)
00531      */
00532     lobj = inv_open(lobjId, INV_READ, fscxt);
00533 
00534     /*
00535      * open the file to be written to
00536      *
00537      * Note: we reduce backend's normal 077 umask to the slightly friendlier
00538      * 022. This code used to drop it all the way to 0, but creating
00539      * world-writable export files doesn't seem wise.
00540      */
00541     text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
00542     oumask = umask(S_IWGRP | S_IWOTH);
00543     fd = OpenTransientFile(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY,
00544                            S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
00545     umask(oumask);
00546     if (fd < 0)
00547         ereport(ERROR,
00548                 (errcode_for_file_access(),
00549                  errmsg("could not create server file \"%s\": %m",
00550                         fnamebuf)));
00551 
00552     /*
00553      * read in from the inversion file and write to the filesystem
00554      */
00555     while ((nbytes = inv_read(lobj, buf, BUFSIZE)) > 0)
00556     {
00557         tmp = write(fd, buf, nbytes);
00558         if (tmp != nbytes)
00559             ereport(ERROR,
00560                     (errcode_for_file_access(),
00561                      errmsg("could not write server file \"%s\": %m",
00562                             fnamebuf)));
00563     }
00564 
00565     CloseTransientFile(fd);
00566     inv_close(lobj);
00567 
00568     PG_RETURN_INT32(1);
00569 }
00570 
00571 /*
00572  * lo_truncate -
00573  *    truncate a large object to a specified length
00574  */
00575 static void
00576 lo_truncate_internal(int32 fd, int64 len)
00577 {
00578     LargeObjectDesc *lobj;
00579 
00580     if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
00581         ereport(ERROR,
00582                 (errcode(ERRCODE_UNDEFINED_OBJECT),
00583                  errmsg("invalid large-object descriptor: %d", fd)));
00584     lobj = cookies[fd];
00585 
00586     if ((lobj->flags & IFS_WRLOCK) == 0)
00587         ereport(ERROR,
00588                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
00589               errmsg("large object descriptor %d was not opened for writing",
00590                      fd)));
00591 
00592     /* Permission checks --- first time through only */
00593     if ((lobj->flags & IFS_WR_PERM_OK) == 0)
00594     {
00595         if (!lo_compat_privileges &&
00596             pg_largeobject_aclcheck_snapshot(lobj->id,
00597                                              GetUserId(),
00598                                              ACL_UPDATE,
00599                                              lobj->snapshot) != ACLCHECK_OK)
00600             ereport(ERROR,
00601                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00602                      errmsg("permission denied for large object %u",
00603                             lobj->id)));
00604         lobj->flags |= IFS_WR_PERM_OK;
00605     }
00606 
00607     inv_truncate(lobj, len);
00608 }
00609 
00610 Datum
00611 lo_truncate(PG_FUNCTION_ARGS)
00612 {
00613     int32       fd = PG_GETARG_INT32(0);
00614     int32       len = PG_GETARG_INT32(1);
00615 
00616     lo_truncate_internal(fd, len);
00617     PG_RETURN_INT32(0);
00618 }
00619 
00620 Datum
00621 lo_truncate64(PG_FUNCTION_ARGS)
00622 {
00623     int32       fd = PG_GETARG_INT32(0);
00624     int64       len = PG_GETARG_INT64(1);
00625 
00626     lo_truncate_internal(fd, len);
00627     PG_RETURN_INT32(0);
00628 }
00629 
00630 /*
00631  * AtEOXact_LargeObject -
00632  *       prepares large objects for transaction commit
00633  */
00634 void
00635 AtEOXact_LargeObject(bool isCommit)
00636 {
00637     int         i;
00638 
00639     if (fscxt == NULL)
00640         return;                 /* no LO operations in this xact */
00641 
00642     /*
00643      * Close LO fds and clear cookies array so that LO fds are no longer good.
00644      * On abort we skip the close step.
00645      */
00646     for (i = 0; i < cookies_size; i++)
00647     {
00648         if (cookies[i] != NULL)
00649         {
00650             if (isCommit)
00651                 inv_close(cookies[i]);
00652             deleteLOfd(i);
00653         }
00654     }
00655 
00656     /* Needn't actually pfree since we're about to zap context */
00657     cookies = NULL;
00658     cookies_size = 0;
00659 
00660     /* Release the LO memory context to prevent permanent memory leaks. */
00661     MemoryContextDelete(fscxt);
00662     fscxt = NULL;
00663 
00664     /* Give inv_api.c a chance to clean up, too */
00665     close_lo_relation(isCommit);
00666 }
00667 
00668 /*
00669  * AtEOSubXact_LargeObject
00670  *      Take care of large objects at subtransaction commit/abort
00671  *
00672  * Reassign LOs created/opened during a committing subtransaction
00673  * to the parent subtransaction.  On abort, just close them.
00674  */
00675 void
00676 AtEOSubXact_LargeObject(bool isCommit, SubTransactionId mySubid,
00677                         SubTransactionId parentSubid)
00678 {
00679     int         i;
00680 
00681     if (fscxt == NULL)          /* no LO operations in this xact */
00682         return;
00683 
00684     for (i = 0; i < cookies_size; i++)
00685     {
00686         LargeObjectDesc *lo = cookies[i];
00687 
00688         if (lo != NULL && lo->subid == mySubid)
00689         {
00690             if (isCommit)
00691                 lo->subid = parentSubid;
00692             else
00693             {
00694                 /*
00695                  * Make sure we do not call inv_close twice if it errors out
00696                  * for some reason.  Better a leak than a crash.
00697                  */
00698                 deleteLOfd(i);
00699                 inv_close(lo);
00700             }
00701         }
00702     }
00703 }
00704 
00705 /*****************************************************************************
00706  *  Support routines for this file
00707  *****************************************************************************/
00708 
00709 static int
00710 newLOfd(LargeObjectDesc *lobjCookie)
00711 {
00712     int         i,
00713                 newsize;
00714 
00715     /* Try to find a free slot */
00716     for (i = 0; i < cookies_size; i++)
00717     {
00718         if (cookies[i] == NULL)
00719         {
00720             cookies[i] = lobjCookie;
00721             return i;
00722         }
00723     }
00724 
00725     /* No free slot, so make the array bigger */
00726     if (cookies_size <= 0)
00727     {
00728         /* First time through, arbitrarily make 64-element array */
00729         i = 0;
00730         newsize = 64;
00731         cookies = (LargeObjectDesc **)
00732             MemoryContextAllocZero(fscxt, newsize * sizeof(LargeObjectDesc *));
00733         cookies_size = newsize;
00734     }
00735     else
00736     {
00737         /* Double size of array */
00738         i = cookies_size;
00739         newsize = cookies_size * 2;
00740         cookies = (LargeObjectDesc **)
00741             repalloc(cookies, newsize * sizeof(LargeObjectDesc *));
00742         MemSet(cookies + cookies_size, 0,
00743                (newsize - cookies_size) * sizeof(LargeObjectDesc *));
00744         cookies_size = newsize;
00745     }
00746 
00747     Assert(cookies[i] == NULL);
00748     cookies[i] = lobjCookie;
00749     return i;
00750 }
00751 
00752 static void
00753 deleteLOfd(int fd)
00754 {
00755     cookies[fd] = NULL;
00756 }