/* * inv_open -- access an existing large object. * * Returns: * large object descriptor, appropriately filled in. */ LargeObjectDesc * 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; 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; }
/* * Open an arbitrary file that will (optionally) disappear when we close it. * This is similar to OpenTemporaryFile, except the exact name specified in * fileName is used. */ File OpenNamedFile(const char *fileName, bool create, bool delOnClose, bool closeAtEOXact) { char tempfilepath[MAXPGPATH]; strncpy(tempfilepath, fileName, sizeof(tempfilepath)); /* * File flags when open the file. Note: we don't use O_EXCL, in case there is an orphaned * temp file that can be reused. */ int fileFlags = O_RDWR | PG_BINARY; if (create) { fileFlags |= O_TRUNC | O_CREAT; } File file = FileNameOpenFile(tempfilepath, fileFlags, 0600); if (file <= 0) { char *dirpath; if (!create) return file; /* * We might need to create the pg_tempfiles subdirectory, if no one * has yet done so. * * Don't check for error from mkdir; it could fail if someone else * just did the same thing. If it doesn't work then we'll bomb out on * the second create attempt, instead. */ dirpath = (char*)palloc(PATH_MAX); snprintf(dirpath, PATH_MAX, "%s/%s", getCurrentTempFilePath, PG_TEMP_FILES_DIR); mkdir(dirpath, S_IRWXU); pfree(dirpath); file = FileNameOpenFile(tempfilepath, fileFlags, 0600); if (file <= 0) elog(ERROR, "could not create temporary file \"%s\": %m", tempfilepath); } /* Mark it for deletion at close */ if(delOnClose) VfdCache[file].fdstate |= FD_TEMPORARY; /* Mark it to be closed at end of transaction. */ if (closeAtEOXact) { VfdCache[file].fdstate |= FD_CLOSE_AT_EOXACT; VfdCache[file].create_subid = GetCurrentSubTransactionId(); } return file; } /* OpenNamedFile */
/* * 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. */ LargeObjectDesc * inv_open(Oid lobjId, int flags, MemoryContext mcxt) { LargeObjectDesc *retval; Snapshot snapshot = NULL; int descflags = 0; if (flags & INV_WRITE) { snapshot = NULL; /* instantaneous MVCC snapshot */ descflags = IFS_WRLOCK | IFS_RDLOCK; } else if (flags & INV_READ) { snapshot = GetActiveSnapshot(); descflags = IFS_RDLOCK; } else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid flags for opening a large object: %d", flags))); /* Can't use LargeObjectExists here because we need to specify snapshot */ if (!myLargeObjectExists(lobjId, snapshot)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("large object %u does not exist", lobjId))); /* * 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 after checking that the LO exists, to avoid * leaking the snapshot if an error is thrown. */ if (snapshot) snapshot = RegisterSnapshotOnOwner(snapshot, TopTransactionResourceOwner); /* All set, create a descriptor */ retval = (LargeObjectDesc *) MemoryContextAlloc(mcxt, sizeof(LargeObjectDesc)); retval->id = lobjId; retval->subid = GetCurrentSubTransactionId(); retval->offset = 0; retval->snapshot = snapshot; retval->flags = descflags; return retval; }
/* * CreatePortal * Returns a new portal given a name. * * allowDup: if true, automatically drop any pre-existing portal of the * same name (if false, an error is raised). * * dupSilent: if true, don't even emit a WARNING. */ Portal CreatePortal(const char *name, bool allowDup, bool dupSilent) { Portal portal; AssertArg(PointerIsValid(name)); portal = GetPortalByName(name); if (PortalIsValid(portal)) { if (!allowDup) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_CURSOR), errmsg("cursor \"%s\" already exists", name))); if (!dupSilent) ereport(WARNING, (errcode(ERRCODE_DUPLICATE_CURSOR), errmsg("closing existing cursor \"%s\"", name))); PortalDrop(portal, false); } /* make new portal structure */ portal = (Portal) MemoryContextAllocZero(PortalMemory, sizeof *portal); /* initialize portal heap context; typically it won't store much */ portal->heap = AllocSetContextCreate(PortalMemory, "PortalHeapMemory", ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MAXSIZE); /* create a resource owner for the portal */ portal->resowner = ResourceOwnerCreate(CurTransactionResourceOwner, "Portal"); /* initialize portal fields that don't start off zero */ portal->cleanup = PortalCleanup; portal->createSubid = GetCurrentSubTransactionId(); portal->strategy = PORTAL_MULTI_QUERY; portal->cursorOptions = CURSOR_OPT_NO_SCROLL; portal->atStart = true; portal->atEnd = true; /* disallow fetches until query is set */ /* put portal in table (sets portal->name) */ PortalHashTableInsert(portal, name); return portal; }
/* * sepgsql_set_client_label * * This routine tries to switch the current security label of the client, and * checks related permissions. The supplied new label shall be added to the * client_label_pending list, then saved at transaction-commit time to ensure * transaction-awareness. */ static void sepgsql_set_client_label(const char *new_label) { const char *tcontext; MemoryContext oldcxt; pending_label *plabel; /* Reset to the initial client label, if NULL */ if (!new_label) tcontext = client_label_peer; else { if (security_check_context_raw((security_context_t) new_label) < 0) ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("SELinux: invalid security label: \"%s\"", new_label))); tcontext = new_label; } /* Check process:{setcurrent} permission. */ sepgsql_avc_check_perms_label(sepgsql_get_client_label(), SEPG_CLASS_PROCESS, SEPG_PROCESS__SETCURRENT, NULL, true); /* Check process:{dyntransition} permission. */ sepgsql_avc_check_perms_label(tcontext, SEPG_CLASS_PROCESS, SEPG_PROCESS__DYNTRANSITION, NULL, true); /* * Append the supplied new_label on the pending list until the current * transaction is committed. */ oldcxt = MemoryContextSwitchTo(CurTransactionContext); plabel = palloc0(sizeof(pending_label)); plabel->subid = GetCurrentSubTransactionId(); if (new_label) plabel->label = pstrdup(new_label); client_label_pending = lappend(client_label_pending, plabel); MemoryContextSwitchTo(oldcxt); }
/* * Routines that want to use <dirent.h> (ie, DIR*) should use AllocateDir * rather than plain opendir(). This lets fd.c deal with freeing FDs if * necessary to open the directory, and with closing it after an elog. * When done, call FreeDir rather than closedir. * * Ideally this should be the *only* direct call of opendir() in the backend. */ DIR * AllocateDir(const char *dirname) { DIR *dir; DO_DB(elog(LOG, "AllocateDir: Allocated %d (%s)", numAllocatedDescs, dirname)); /* * The test against MAX_ALLOCATED_DESCS prevents us from overflowing * allocatedDescs[]; the test against max_safe_fds prevents AllocateDir * from hogging every one of the available FDs, which'd lead to infinite * looping. */ if (numAllocatedDescs >= MAX_ALLOCATED_DESCS || numAllocatedDescs >= max_safe_fds - 1) elog(ERROR, "could not allocate directory: out of file handles"); TryAgain: if ((dir = opendir(dirname)) != NULL) { AllocateDesc *desc = &allocatedDescs[numAllocatedDescs]; desc->kind = AllocateDescDir; desc->desc.dir = dir; desc->create_subid = GetCurrentSubTransactionId(); numAllocatedDescs++; return desc->desc.dir; } if (errno == EMFILE || errno == ENFILE) { int save_errno = errno; ereport(LOG, (errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("out of file handles: %m; release and retry"))); errno = 0; if (ReleaseLruFile()) goto TryAgain; errno = save_errno; } return NULL; }
/* * inv_create -- create a new large object. * * Arguments: * flags * * Returns: * large object descriptor, appropriately filled in. */ LargeObjectDesc * inv_create(int flags) { Oid file_oid; LargeObjectDesc *retval; /* * Allocate an OID to be the LO's identifier. */ file_oid = newoid(); /* Check for duplicate (shouldn't happen) */ if (LargeObjectExists(file_oid)) elog(ERROR, "large object %u already exists", file_oid); /* * Create the LO by writing an empty first page for it in * pg_largeobject */ LargeObjectCreate(file_oid); /* * Advance command counter so that new tuple will be seen by later * large-object operations in this transaction. */ 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; }
/* * Routines that want to use stdio (ie, FILE*) should use AllocateFile * rather than plain fopen(). This lets fd.c deal with freeing FDs if * necessary to open the file. When done, call FreeFile rather than fclose. * * Note that files that will be open for any significant length of time * should NOT be handled this way, since they cannot share kernel file * descriptors with other files; there is grave risk of running out of FDs * if anyone locks down too many FDs. Most callers of this routine are * simply reading a config file that they will read and close immediately. * * fd.c will automatically close all files opened with AllocateFile at * transaction commit or abort; this prevents FD leakage if a routine * that calls AllocateFile is terminated prematurely by ereport(ERROR). * * Ideally this should be the *only* direct call of fopen() in the backend. */ FILE * AllocateFile(const char *name, const char *mode) { FILE *file; DO_DB(elog(LOG, "AllocateFile: Allocated %d (%s)", numAllocatedDescs, name)); /* * The test against MAX_ALLOCATED_DESCS prevents us from overflowing * allocatedFiles[]; the test against max_safe_fds prevents AllocateFile * from hogging every one of the available FDs, which'd lead to infinite * looping. */ if (numAllocatedDescs >= MAX_ALLOCATED_DESCS || numAllocatedDescs >= max_safe_fds - 1) elog(ERROR, "too many private files demanded"); TryAgain: if ((file = fopen(name, mode)) != NULL) { AllocateDesc *desc = &allocatedDescs[numAllocatedDescs]; desc->kind = AllocateDescFile; desc->desc.file = file; desc->create_subid = GetCurrentSubTransactionId(); numAllocatedDescs++; return desc->desc.file; } if (errno == EMFILE || errno == ENFILE) { int save_errno = errno; ereport(LOG, (errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("out of file descriptors: %m; release and retry"))); errno = 0; if (ReleaseLruFile()) goto TryAgain; errno = save_errno; } return NULL; }
/* * 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. */ LargeObjectDesc * inv_open(Oid lobjId, int flags, MemoryContext mcxt) { LargeObjectDesc *retval; retval = (LargeObjectDesc *) MemoryContextAlloc(mcxt, sizeof(LargeObjectDesc)); retval->id = lobjId; retval->subid = GetCurrentSubTransactionId(); retval->offset = 0; if (flags & INV_WRITE) { retval->snapshot = SnapshotNow; retval->flags = IFS_WRLOCK | IFS_RDLOCK; } else if (flags & INV_READ) { /* * 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. */ retval->snapshot = RegisterSnapshotOnOwner(GetActiveSnapshot(), TopTransactionResourceOwner); retval->flags = IFS_RDLOCK; } else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid flags for opening a large object: %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_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. */ LargeObjectDesc * inv_open(Oid lobjId, int flags, MemoryContext mcxt) { LargeObjectDesc *retval; retval = (LargeObjectDesc *) MemoryContextAlloc(mcxt, sizeof(LargeObjectDesc)); retval->id = lobjId; retval->subid = GetCurrentSubTransactionId(); 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 mcxt */ MemoryContext oldContext = MemoryContextSwitchTo(mcxt); retval->snapshot = CopySnapshot(ActiveSnapshot); retval->flags = IFS_RDLOCK; MemoryContextSwitchTo(oldContext); } 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; }
int SPI_connect(void) { int newdepth; /* * When procedure called by Executor _SPI_curid expected to be equal to * _SPI_connected */ if (_SPI_curid != _SPI_connected) return SPI_ERROR_CONNECT; if (_SPI_stack == NULL) { if (_SPI_connected != -1 || _SPI_stack_depth != 0) elog(ERROR, "SPI stack corrupted"); newdepth = 16; _SPI_stack = (_SPI_connection *) MemoryContextAlloc(TopTransactionContext, newdepth * sizeof(_SPI_connection)); _SPI_stack_depth = newdepth; } else { if (_SPI_stack_depth <= 0 || _SPI_stack_depth <= _SPI_connected) elog(ERROR, "SPI stack corrupted"); if (_SPI_stack_depth == _SPI_connected + 1) { newdepth = _SPI_stack_depth * 2; _SPI_stack = (_SPI_connection *) repalloc(_SPI_stack, newdepth * sizeof(_SPI_connection)); _SPI_stack_depth = newdepth; } } /* * We're entering procedure where _SPI_curid == _SPI_connected - 1 */ _SPI_connected++; Assert(_SPI_connected >= 0 && _SPI_connected < _SPI_stack_depth); _SPI_current = &(_SPI_stack[_SPI_connected]); _SPI_current->processed = 0; _SPI_current->lastoid = InvalidOid; _SPI_current->tuptable = NULL; _SPI_current->procCxt = NULL; /* in case we fail to create 'em */ _SPI_current->execCxt = NULL; _SPI_current->connectSubid = GetCurrentSubTransactionId(); /* * Create memory contexts for this procedure * * XXX it would be better to use PortalContext as the parent context, but * we may not be inside a portal (consider deferred-trigger execution). * Perhaps CurTransactionContext would do? For now it doesn't matter * because we clean up explicitly in AtEOSubXact_SPI(). */ _SPI_current->procCxt = AllocSetContextCreate(TopTransactionContext, "SPI Proc", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); _SPI_current->execCxt = AllocSetContextCreate(TopTransactionContext, "SPI Exec", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); /* ... and switch to procedure's context */ _SPI_current->savedcxt = MemoryContextSwitchTo(_SPI_current->procCxt); return SPI_OK_CONNECT; }
Datum plpgsql_inline_handler(PG_FUNCTION_ARGS) { InlineCodeBlock *codeblock = (InlineCodeBlock *) DatumGetPointer(PG_GETARG_DATUM(0)); PLpgSQL_function *func; FunctionCallInfoData fake_fcinfo; FmgrInfo flinfo; EState *simple_eval_estate; Datum retval; int rc; Assert(IsA(codeblock, InlineCodeBlock)); /* * Connect to SPI manager */ if ((rc = SPI_connect()) != SPI_OK_CONNECT) elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc)); /* Compile the anonymous code block */ func = plpgsql_compile_inline(codeblock->source_text); /* Mark the function as busy, just pro forma */ func->use_count++; /* * Set up a fake fcinfo with just enough info to satisfy * plpgsql_exec_function(). In particular note that this sets things up * with no arguments passed. */ MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo)); MemSet(&flinfo, 0, sizeof(flinfo)); fake_fcinfo.flinfo = &flinfo; flinfo.fn_oid = InvalidOid; flinfo.fn_mcxt = CurrentMemoryContext; /* Create a private EState for simple-expression execution */ simple_eval_estate = CreateExecutorState(); /* And run the function */ PG_TRY(); { retval = plpgsql_exec_function(func, &fake_fcinfo, simple_eval_estate); } PG_CATCH(); { /* * We need to clean up what would otherwise be long-lived resources * accumulated by the failed DO block, principally cached plans for * statements (which can be flushed with plpgsql_free_function_memory) * and execution trees for simple expressions, which are in the * private EState. * * Before releasing the private EState, we must clean up any * simple_econtext_stack entries pointing into it, which we can do by * invoking the subxact callback. (It will be called again later if * some outer control level does a subtransaction abort, but no harm * is done.) We cheat a bit knowing that plpgsql_subxact_cb does not * pay attention to its parentSubid argument. */ plpgsql_subxact_cb(SUBXACT_EVENT_ABORT_SUB, GetCurrentSubTransactionId(), 0, NULL); /* Clean up the private EState */ FreeExecutorState(simple_eval_estate); /* Function should now have no remaining use-counts ... */ func->use_count--; Assert(func->use_count == 0); /* ... so we can free subsidiary storage */ plpgsql_free_function_memory(func); /* And propagate the error */ PG_RE_THROW(); } PG_END_TRY(); /* Clean up the private EState */ FreeExecutorState(simple_eval_estate); /* Function should now have no remaining use-counts ... */ func->use_count--; Assert(func->use_count == 0); /* ... so we can free subsidiary storage */ plpgsql_free_function_memory(func); /* * Disconnect from SPI manager */ if ((rc = SPI_finish()) != SPI_OK_FINISH) elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc)); return retval; }
/* * Mark flat database file as needing an update (because pg_database changed) */ void database_file_update_needed(void) { if (database_file_update_subid == InvalidSubTransactionId) database_file_update_subid = GetCurrentSubTransactionId(); }
/* * Mark flat auth time file as needing an update (because * pg_auth_time_constraint changed) */ void auth_time_file_update_needed(void) { if (auth_time_file_update_subid == InvalidSubTransactionId) auth_time_file_update_subid = GetCurrentSubTransactionId(); }