/* * Check that the code is being called in right context. */ static void mirroring_sanity_check(int flags, const char *func) { if ((flags & MASTER_ONLY) == MASTER_ONLY) { /* TODO: Add new check */ } if ((flags & UTILITY_MODE) == UTILITY_MODE) { if (Gp_role != GP_ROLE_UTILITY) elog(ERROR, "%s must be run in utility mode", func); } if ((flags & SINGLE_USER_MODE) == SINGLE_USER_MODE) { if (IsUnderPostmaster) elog(ERROR, "%s must be run in single-user mode", func); } if ((flags & SUPERUSER) == SUPERUSER) { if (!superuser()) elog(ERROR, "%s can only be run by a superuser", func); } if ((flags & READ_ONLY) == READ_ONLY) { if (gp_set_read_only != true) elog(ERROR, "%s can only be run if the system is in read only mode", func); } if ((flags & SEGMENT_ONLY) == SEGMENT_ONLY) { /* TODO: Add new check */ } if ((flags & STANDBY_ONLY) == STANDBY_ONLY) { /* TODO: Add new check */ } }
/* * pg_start_backup: set up for taking an on-line backup dump * * Essentially what this does is to create a backup label file in $PGDATA, * where it will be archived as part of the backup dump. The label file * contains the user-supplied label string (typically this would be used * to tell where the backup dump will be stored) and the starting time and * starting WAL location for the dump. */ Datum pg_start_backup(PG_FUNCTION_ARGS) { text *backupid = PG_GETARG_TEXT_P(0); bool fast = PG_GETARG_BOOL(1); char *backupidstr; XLogRecPtr startpoint; backupidstr = text_to_cstring(backupid); if (!superuser() && !has_rolreplication(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser or replication role to run a backup"))); startpoint = do_pg_start_backup(backupidstr, fast, NULL, NULL); PG_RETURN_LSN(startpoint); }
Datum wait_pid(PG_FUNCTION_ARGS) { int pid = PG_GETARG_INT32(0); if (!superuser()) elog(ERROR, "must be superuser to check PID liveness"); while (kill(pid, 0) == 0) { CHECK_FOR_INTERRUPTS(); pg_usleep(50000); } if (errno != ESRCH) elog(ERROR, "could not check PID %d liveness: %m", pid); PG_RETURN_VOID(); }
/* * Rotate log file * * This function is kept to support adminpack 1.0. */ Datum pg_rotate_logfile(PG_FUNCTION_ARGS) { if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to rotate log files with adminpack 1.0"), errhint("Consider using pg_logfile_rotate(), which is part of core, instead.")))); if (!Logging_collector) { ereport(WARNING, (errmsg("rotation not possible because log collection not active"))); PG_RETURN_BOOL(false); } SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE); PG_RETURN_BOOL(true); }
/* * gp_update_global_sequence_entry(tid, bigint) => bool * * Updates the given global sequence to the specified value: * - Only allows increasing the sequence value * - Only lets you set the tids '(0,1)' through '(0,4)' * * these are the only tids currently used by the system * (see cdb/cdbglobalsequence.h) */ Datum gp_update_global_sequence_entry(PG_FUNCTION_ARGS) { ItemPointer tid; int8 sequenceVal; GpGlobalSequence sequence; /* Must be super user */ if (!superuser()) elog(ERROR, "permission denied"); /* Check input arguments */ if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) elog(ERROR, "null input parameter"); tid = (ItemPointer) PG_GETARG_POINTER(0); sequenceVal = PG_GETARG_INT64(1); /* Check tid */ if (ItemPointerGetBlockNumber(tid) != 0) elog(ERROR, "unexpected block number in tid"); sequence = (GpGlobalSequence) ItemPointerGetOffsetNumber(tid); switch (sequence) { case GpGlobalSequence_PersistentRelation: case GpGlobalSequence_PersistentDatabase: case GpGlobalSequence_PersistentTablespace: case GpGlobalSequence_PersistentFilespace: break; default: elog(ERROR, "unexpected offset number in tid"); } /* Check sequence value */ if (sequenceVal < GlobalSequence_Current(sequence)) elog(ERROR, "sequence number too low"); /* Everything looks good, update the value */ GlobalSequence_Set(sequence, sequenceVal); PG_RETURN_BOOL(true); }
/* * Rename language */ void RenameLanguage(const char *oldname, const char *newname) { HeapTuple tup; Relation rel; /* Translate both names for consistency with CREATE */ oldname = case_translate_language_name(oldname); newname = case_translate_language_name(newname); rel = heap_open(LanguageRelationId, RowExclusiveLock); tup = SearchSysCacheCopy(LANGNAME, CStringGetDatum(oldname), 0, 0, 0); if (!HeapTupleIsValid(tup)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("language \"%s\" does not exist", oldname))); /* make sure the new name doesn't exist */ if (SearchSysCacheExists(LANGNAME, CStringGetDatum(newname), 0, 0, 0)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("language \"%s\" already exists", newname))); /* must be superuser, since we do not have owners for PLs */ if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to rename procedural language"))); /* rename */ namestrcpy(&(((Form_pg_language) GETSTRUCT(tup))->lanname), newname); simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); heap_close(rel, NoLock); heap_freetuple(tup); }
Datum pg_stat_get_backend_start(PG_FUNCTION_ARGS) { int32 beid = PG_GETARG_INT32(0); TimestampTz result; PgBackendStatus *beentry; if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL) PG_RETURN_NULL(); if (!superuser() && beentry->st_userid != GetUserId()) PG_RETURN_NULL(); result = beentry->st_proc_start_timestamp; if (result == 0) /* probably can't happen? */ PG_RETURN_NULL(); PG_RETURN_TIMESTAMPTZ(result); }
/* * pgespresso_start_backup: set up for taking an on-line backup dump * * Essentially what this does is to return a backup label file that the * user is responsible for placing in the $PGDATA of the backup AFTER * the backup has been taken. The label file must not be written to the * data directory of the server from which the backup is taken because * this type of backup presumes and allows that more than one backup * may be in progress at any one time. The label file * contains the user-supplied label string (typically this would be used * to tell where the backup dump will be stored) and the starting time and * starting WAL location for the dump. */ Datum pgespresso_start_backup(PG_FUNCTION_ARGS) { text *backupid = PG_GETARG_TEXT_P(0); bool fast = PG_GETARG_BOOL(1); char *backupidstr; char *labelfile; backupidstr = text_to_cstring(backupid); if (!superuser() && !has_rolreplication(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser or replication role to run a backup"))); /* * ThisTimeLineID is always 0 in a normal backend during recovery. * We get latest redo apply position timeline and we update it globally * to make do_pg_start_backup use the correct value when generating * the backup label text */ if (RecoveryInProgress()) { TimeLineID replayTLI; GetXLogReplayRecPtr(&replayTLI); ThisTimeLineID = replayTLI; elog(DEBUG1, "updated ThisTimeLineID = %u", ThisTimeLineID); } /* * Starting from 9.3 the do_pg_start_backup returns the timeline ID * in *starttli_p additional argument */ #if PG_VERSION_NUM >= 90300 do_pg_start_backup(backupidstr, fast, NULL, &labelfile); #else do_pg_start_backup(backupidstr, fast, &labelfile); #endif PG_RETURN_TEXT_P(cstring_to_text(labelfile)); }
/* * pg_create_restore_point: a named point for restore */ Datum pg_create_restore_point(PG_FUNCTION_ARGS) { text *restore_name = PG_GETARG_TEXT_P(0); char *restore_name_str; XLogRecPtr restorepoint; char location[MAXFNAMELEN]; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to create a restore point")))); if (RecoveryInProgress()) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), (errmsg("recovery is in progress"), errhint("WAL control functions cannot be executed during recovery.")))); if (!XLogIsNeeded()) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("WAL level not sufficient for creating a restore point"), errhint("wal_level must be set to \"archive\" or \"hot_standby\" at server start."))); restore_name_str = text_to_cstring(restore_name); if (strlen(restore_name_str) >= MAXFNAMELEN) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("value too long for restore point (maximum %d characters)", MAXFNAMELEN - 1))); restorepoint = XLogRestorePoint(restore_name_str); /* * As a convenience, return the WAL location of the restore point record */ snprintf(location, sizeof(location), "%X/%X", restorepoint.xlogid, restorepoint.xrecoff); PG_RETURN_TEXT_P(cstring_to_text(location)); }
/* * For non-superusers, insist that the connstr specify a password. This * prevents a password from being picked up from .pgpass, a service file, * the environment, etc. We don't want the postgres user's passwords * to be accessible to non-superusers. (See also dblink_connstr_check in * contrib/dblink.) */ static void check_conn_params(const char **keywords, const char **values) { int i; /* no check required if superuser */ if (superuser()) return; /* ok if params contain a non-empty password */ for (i = 0; keywords[i] != NULL; i++) { if (strcmp(keywords[i], "password") == 0 && values[i][0] != '\0') return; } ereport(ERROR, (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED), errmsg("password is required"), errdetail("Non-superusers must provide a password in the user mapping."))); }
Datum pg_stat_get_backend_waiting_reason(PG_FUNCTION_ARGS) { int32 beid = PG_GETARG_INT32(0); PgBackendStatus *beentry; char *result; if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL) PG_RETURN_NULL(); if (!superuser() && beentry->st_userid != GetUserId()) PG_RETURN_NULL(); result = pgstat_waiting_string(beentry->st_waiting); /* waiting for nothing */ if (result == NULL) PG_RETURN_NULL(); PG_RETURN_DATUM(CStringGetTextDatum(result)); }
static void dummy_object_relabel(const ObjectAddress *object, const char *seclabel) { if (seclabel == NULL || strcmp(seclabel, "unclassified") == 0 || strcmp(seclabel, "classified") == 0) return; if (strcmp(seclabel, "secret") == 0 || strcmp(seclabel, "top secret") == 0) { if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("only superuser can set '%s' label", seclabel))); return; } ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("'%s' is not a valid security label", seclabel))); }
Datum brin_page_type(PG_FUNCTION_ARGS) { bytea *raw_page = PG_GETARG_BYTEA_P(0); Page page = VARDATA(raw_page); int raw_page_size; char *type; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use raw page functions")))); raw_page_size = VARSIZE(raw_page) - VARHDRSZ; if (raw_page_size != BLCKSZ) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("input page too small"), errdetail("Expected size %d, got %d", BLCKSZ, raw_page_size))); switch (BrinPageType(page)) { case BRIN_PAGETYPE_META: type = "meta"; break; case BRIN_PAGETYPE_REVMAP: type = "revmap"; break; case BRIN_PAGETYPE_REGULAR: type = "regular"; break; default: type = psprintf("unknown (%02x)", BrinPageType(page)); break; } PG_RETURN_TEXT_P(cstring_to_text(type)); }
Datum pg_relpagesbyid(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); int64 relpages; Relation rel; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use pgstattuple functions")))); rel = relation_open(relid, AccessShareLock); /* note: this will work OK on non-local temp tables */ relpages = RelationGetNumberOfBlocks(rel); relation_close(rel, AccessShareLock); PG_RETURN_INT64(relpages); }
/* --------------------------------------------------------------------- * DROP PROCEDURAL LANGUAGE * --------------------------------------------------------------------- */ void DropProceduralLanguage(DropPLangStmt *stmt) { char *languageName; HeapTuple langTup; ObjectAddress object; /* * Check permission */ if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to drop procedural language"))); /* * Translate the language name, check that the language exists */ languageName = case_translate_language_name(stmt->plname); langTup = SearchSysCache(LANGNAME, CStringGetDatum(languageName), 0, 0, 0); if (!HeapTupleIsValid(langTup)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("language \"%s\" does not exist", languageName))); object.classId = LanguageRelationId; object.objectId = HeapTupleGetOid(langTup); object.objectSubId = 0; ReleaseSysCache(langTup); /* * Do the deletion */ performDeletion(&object, stmt->behavior); }
/** * Get weight associated with queue. See queue.c. * * Attention is paid in order to avoid catalog lookups when not allowed. The * superuser() function performs catalog lookups in certain cases. Also the * GetResqueueCapabilityEntry will always do a catalog lookup. In such cases * use the default weight. */ static int ResourceQueueGetPriorityWeight(Oid queueId) { List *capabilitiesList = NULL; List *entry = NULL; ListCell *le = NULL; int weight = BackoffDefaultWeight(); if (!IsTransactionState()) return weight; if (superuser()) return BackoffSuperuserStatementWeight(); if (queueId == InvalidOid) return weight; capabilitiesList = GetResqueueCapabilityEntry(queueId); /* This is a list of * lists */ if (!capabilitiesList) return weight; foreach(le, capabilitiesList) { Value *key = NULL; entry = (List *) lfirst(le); Assert(entry); key = (Value *) linitial(entry); Assert(key->type == T_Integer); /* This is resource type id */ if (intVal(key) == PG_RESRCTYPE_PRIORITY) { Value *val = lsecond(entry); Assert(val->type == T_String); weight = BackoffPriorityValueToInt(strVal(val)); } }
/* * Read a section of a file, returning it as bytea */ Datum pg_read_binary_file(PG_FUNCTION_ARGS) { text *filename_t = PG_GETARG_TEXT_P(0); int64 seek_offset = PG_GETARG_INT64(1); int64 bytes_to_read = PG_GETARG_INT64(2); char *filename; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to read files")))); filename = convert_and_check_filename(filename_t); if (bytes_to_read < 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("requested length cannot be negative"))); PG_RETURN_BYTEA_P(read_binary_file(filename, seek_offset, bytes_to_read)); }
/* * Read a section of a file, returning it as text * * This function is kept to support adminpack 1.0. */ Datum pg_read_file(PG_FUNCTION_ARGS) { text *filename_t = PG_GETARG_TEXT_PP(0); int64 seek_offset = 0; int64 bytes_to_read = -1; bool missing_ok = false; char *filename; text *result; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to read files with adminpack 1.0"), errhint("Consider using pg_file_read(), which is part of core, instead.")))); /* handle optional arguments */ if (PG_NARGS() >= 3) { seek_offset = PG_GETARG_INT64(1); bytes_to_read = PG_GETARG_INT64(2); if (bytes_to_read < 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("requested length cannot be negative"))); } if (PG_NARGS() >= 4) missing_ok = PG_GETARG_BOOL(3); filename = convert_and_check_filename(filename_t); result = read_text_file(filename, seek_offset, bytes_to_read, missing_ok); if (result) PG_RETURN_TEXT_P(result); else PG_RETURN_NULL(); }
/* --------------------------------------------------- * hash_page_type() * * Usage: SELECT hash_page_type(get_raw_page('con_hash_index', 1)); * --------------------------------------------------- */ Datum hash_page_type(PG_FUNCTION_ARGS) { bytea *raw_page = PG_GETARG_BYTEA_P(0); Page page; HashPageOpaque opaque; int pagetype; const char *type; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use raw page functions")))); page = verify_hash_page(raw_page, 0); if (PageIsNew(page)) type = "unused"; else { opaque = (HashPageOpaque) PageGetSpecialPointer(page); /* page type (flags) */ pagetype = opaque->hasho_flag & LH_PAGE_TYPE; if (pagetype == LH_META_PAGE) type = "metapage"; else if (pagetype == LH_OVERFLOW_PAGE) type = "overflow"; else if (pagetype == LH_BUCKET_PAGE) type = "bucket"; else if (pagetype == LH_BITMAP_PAGE) type = "bitmap"; else type = "unused"; } PG_RETURN_TEXT_P(cstring_to_text(type)); }
Datum pg_stat_get_backend_activity(PG_FUNCTION_ARGS) { PgStat_StatBeEntry *beentry; int32 beid; int len; text *result; beid = PG_GETARG_INT32(0); if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL) PG_RETURN_NULL(); if (!superuser() && beentry->userid != GetUserId()) PG_RETURN_NULL(); len = strlen(beentry->activity); result = palloc(VARHDRSZ + len); VARATT_SIZEP(result) = VARHDRSZ + len; memcpy(VARDATA(result), beentry->activity, len); PG_RETURN_TEXT_P(result); }
/* -------------------------------------------------------- * pg_relpages() * * Get a number of pages of the table/index. * * Usage: SELECT pg_relpages('t1'); * SELECT pg_relpages('t1_pkey'); * -------------------------------------------------------- */ Datum pg_relpages(PG_FUNCTION_ARGS) { text *relname = PG_GETARG_TEXT_P(0); Relation rel; RangeVar *relrv; int4 relpages; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use pgstattuple functions")))); relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); rel = relation_openrv(relrv, AccessShareLock); relpages = RelationGetNumberOfBlocks(rel); relation_close(rel, AccessShareLock); PG_RETURN_INT32(relpages); }
/* * pg_switch_xlog: switch to next xlog file */ Datum pg_switch_xlog(PG_FUNCTION_ARGS) { XLogRecPtr switchpoint; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to switch transaction log files")))); if (RecoveryInProgress()) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("recovery is in progress"), errhint("WAL control functions cannot be executed during recovery."))); switchpoint = RequestXLogSwitch(); /* * As a convenience, return the WAL location of the switch record */ PG_RETURN_LSN(switchpoint); }
/* * Guts of access method deletion. */ void RemoveAccessMethodById(Oid amOid) { Relation relation; HeapTuple tup; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to drop access methods"))); relation = heap_open(AccessMethodRelationId, RowExclusiveLock); tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid)); if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for access method %u", amOid); simple_heap_delete(relation, &tup->t_self); ReleaseSysCache(tup); heap_close(relation, RowExclusiveLock); }
/* * Free the IRQ lines. */ int syscall_IRQ_free(void *dummy, int irqid) { irq_t irq, tofree; if(!superuser()) return -EPERM; irq = &pc_irq[irqid]; spin_lock(&irq->lock); /* If it is first entry, just make the handler NULL */ if(irqid < MAX_IRQ) { irq->handler = NULL; irq->flag = 0; irq->owner = NULL; spin_unlock(&irq->lock); return 0; } tofree = find_by_irqid(irqid); if(tofree == NULL) { spin_unlock(&irq->lock); return -EINVAL; } list_del(&irq->list, tofree, list, irq_t); irqhash_remove(tofree); kmem_cache_free(IRQ_cache, tofree); spin_unlock(&irq->lock); return 0; }
Datum pg_stat_get_backend_activity_start(PG_FUNCTION_ARGS) { int32 beid = PG_GETARG_INT32(0); TimestampTz result; PgBackendStatus *beentry; if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL) PG_RETURN_NULL(); if (!superuser() && beentry->st_userid != GetUserId()) PG_RETURN_NULL(); result = beentry->st_activity_start_timestamp; /* * No time recorded for start of current query -- this is the case if the * user hasn't enabled query-level stats collection. */ if (result == 0) PG_RETURN_NULL(); PG_RETURN_TIMESTAMPTZ(result); }
/* * Extract all item values from a BRIN index page * * Usage: SELECT * FROM brin_page_items(get_raw_page('idx', 1), 'idx'::regclass); */ Datum brin_page_items(PG_FUNCTION_ARGS) { bytea *raw_page = PG_GETARG_BYTEA_P(0); Oid indexRelid = PG_GETARG_OID(1); ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; TupleDesc tupdesc; MemoryContext oldcontext; Tuplestorestate *tupstore; Relation indexRel; brin_column_state **columns; BrinDesc *bdesc; BrinMemTuple *dtup; Page page; OffsetNumber offset; AttrNumber attno; bool unusedItem; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use raw page functions")))); /* check to see if caller supports us returning a tuplestore */ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); if (!(rsinfo->allowedModes & SFRM_Materialize) || rsinfo->expectedDesc == NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialize mode required, but it is not allowed in this context"))); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); /* Build tuplestore to hold the result rows */ oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory); tupstore = tuplestore_begin_heap(true, false, work_mem); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; MemoryContextSwitchTo(oldcontext); indexRel = index_open(indexRelid, AccessShareLock); bdesc = brin_build_desc(indexRel); /* minimally verify the page we got */ page = verify_brin_page(raw_page, BRIN_PAGETYPE_REGULAR, "regular"); /* * Initialize output functions for all indexed datatypes; simplifies * calling them later. */ columns = palloc(sizeof(brin_column_state *) * RelationGetDescr(indexRel)->natts); for (attno = 1; attno <= bdesc->bd_tupdesc->natts; attno++) { Oid output; bool isVarlena; BrinOpcInfo *opcinfo; int i; brin_column_state *column; opcinfo = bdesc->bd_info[attno - 1]; column = palloc(offsetof(brin_column_state, outputFn) + sizeof(FmgrInfo) * opcinfo->oi_nstored); column->nstored = opcinfo->oi_nstored; for (i = 0; i < opcinfo->oi_nstored; i++) { getTypeOutputInfo(opcinfo->oi_typcache[i]->type_id, &output, &isVarlena); fmgr_info(output, &column->outputFn[i]); } columns[attno - 1] = column; } offset = FirstOffsetNumber; unusedItem = false; dtup = NULL; for (;;) { Datum values[7]; bool nulls[7]; /* * This loop is called once for every attribute of every tuple in the * page. At the start of a tuple, we get a NULL dtup; that's our * signal for obtaining and decoding the next one. If that's not the * case, we output the next attribute. */ if (dtup == NULL) { ItemId itemId; /* verify item status: if there's no data, we can't decode */ itemId = PageGetItemId(page, offset); if (ItemIdIsUsed(itemId)) { dtup = brin_deform_tuple(bdesc, (BrinTuple *) PageGetItem(page, itemId)); attno = 1; unusedItem = false; } else unusedItem = true; } else attno++; MemSet(nulls, 0, sizeof(nulls)); if (unusedItem) { values[0] = UInt16GetDatum(offset); nulls[1] = true; nulls[2] = true; nulls[3] = true; nulls[4] = true; nulls[5] = true; nulls[6] = true; } else { int att = attno - 1; values[0] = UInt16GetDatum(offset); values[1] = UInt32GetDatum(dtup->bt_blkno); values[2] = UInt16GetDatum(attno); values[3] = BoolGetDatum(dtup->bt_columns[att].bv_allnulls); values[4] = BoolGetDatum(dtup->bt_columns[att].bv_hasnulls); values[5] = BoolGetDatum(dtup->bt_placeholder); if (!dtup->bt_columns[att].bv_allnulls) { BrinValues *bvalues = &dtup->bt_columns[att]; StringInfoData s; bool first; int i; initStringInfo(&s); appendStringInfoChar(&s, '{'); first = true; for (i = 0; i < columns[att]->nstored; i++) { char *val; if (!first) appendStringInfoString(&s, " .. "); first = false; val = OutputFunctionCall(&columns[att]->outputFn[i], bvalues->bv_values[i]); appendStringInfoString(&s, val); pfree(val); } appendStringInfoChar(&s, '}'); values[6] = CStringGetTextDatum(s.data); pfree(s.data); } else { nulls[6] = true; } } tuplestore_putvalues(tupstore, tupdesc, values, nulls); /* * If the item was unused, jump straight to the next one; otherwise, * the only cleanup needed here is to set our signal to go to the next * tuple in the following iteration, by freeing the current one. */ if (unusedItem) offset = OffsetNumberNext(offset); else if (attno >= bdesc->bd_tupdesc->natts) { pfree(dtup); dtup = NULL; offset = OffsetNumberNext(offset); } /* * If we're beyond the end of the page, we're done. */ if (offset > PageGetMaxOffsetNumber(page)) break; } /* clean up and return the tuplestore */ brin_free_desc(bdesc); tuplestore_donestoring(tupstore); index_close(indexRel, AccessShareLock); return (Datum) 0; }
Datum pg_logdir_ls(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; struct dirent *de; directory_fctx *fctx; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("only superuser can list the log directory")))); if (strcmp(Log_filename, "postgresql-%Y-%m-%d_%H%M%S.log") != 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errmsg("the log_filename parameter must equal 'postgresql-%%Y-%%m-%%d_%%H%%M%%S.log'")))); if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; TupleDesc tupdesc; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); fctx = palloc(sizeof(directory_fctx)); tupdesc = CreateTemplateTupleDesc(2, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starttime", TIMESTAMPOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "filename", TEXTOID, -1, 0); funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); fctx->location = pstrdup(Log_directory); fctx->dirdesc = AllocateDir(fctx->location); if (!fctx->dirdesc) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open directory \"%s\": %m", fctx->location))); funcctx->user_fctx = fctx; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); fctx = (directory_fctx *) funcctx->user_fctx; while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL) { char *values[2]; HeapTuple tuple; char timestampbuf[32]; char *field[MAXDATEFIELDS]; char lowstr[MAXDATELEN + 1]; int dtype; int nf, ftype[MAXDATEFIELDS]; fsec_t fsec; int tz = 0; struct pg_tm date; /* * Default format: postgresql-YYYY-MM-DD_HHMMSS.log */ if (strlen(de->d_name) != 32 || strncmp(de->d_name, "postgresql-", 11) != 0 || de->d_name[21] != '_' || strcmp(de->d_name + 28, ".log") != 0) continue; /* extract timestamp portion of filename */ strcpy(timestampbuf, de->d_name + 11); timestampbuf[17] = '\0'; /* parse and decode expected timestamp to verify it's OK format */ if (ParseDateTime(timestampbuf, lowstr, MAXDATELEN, field, ftype, MAXDATEFIELDS, &nf)) continue; if (DecodeDateTime(field, ftype, nf, &dtype, &date, &fsec, &tz)) continue; /* Seems the timestamp is OK; prepare and return tuple */ values[0] = timestampbuf; values[1] = psprintf("%s/%s", fctx->location, de->d_name); tuple = BuildTupleFromCStrings(funcctx->attinmeta, values); SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); } FreeDir(fctx->dirdesc); SRF_RETURN_DONE(funcctx); }
/* --------------------------------------------------------------------- * CREATE PROCEDURAL LANGUAGE * --------------------------------------------------------------------- */ void CreateProceduralLanguage(CreatePLangStmt *stmt) { char *languageName; PLTemplate *pltemplate; Oid handlerOid, valOid; Oid funcrettype; Oid funcargtypes[1]; /* * Check permission */ if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to create procedural language"))); /* * Translate the language name and check that this language doesn't * already exist */ languageName = case_translate_language_name(stmt->plname); if (SearchSysCacheExists(LANGNAME, PointerGetDatum(languageName), 0, 0, 0)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("language \"%s\" already exists", languageName))); /* * If we have template information for the language, ignore the supplied * parameters (if any) and use the template information. */ if ((pltemplate = find_language_template(languageName)) != NULL) { List *funcname; /* * Give a notice if we are ignoring supplied parameters. */ if (stmt->plhandler) ereport(NOTICE, (errmsg("using pg_pltemplate information instead of CREATE LANGUAGE parameters"))); /* * Find or create the handler function, which we force to be in the * pg_catalog schema. If already present, it must have the correct * return type. */ funcname = SystemFuncName(pltemplate->tmplhandler); handlerOid = LookupFuncName(funcname, 0, funcargtypes, true); if (OidIsValid(handlerOid)) { funcrettype = get_func_rettype(handlerOid); if (funcrettype != LANGUAGE_HANDLEROID) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("function %s must return type \"language_handler\"", NameListToString(funcname)))); } else { handlerOid = ProcedureCreate(pltemplate->tmplhandler, PG_CATALOG_NAMESPACE, false, /* replace */ false, /* returnsSet */ LANGUAGE_HANDLEROID, ClanguageId, F_FMGR_C_VALIDATOR, pltemplate->tmplhandler, pltemplate->tmpllibrary, false, /* isAgg */ false, /* security_definer */ false, /* isStrict */ PROVOLATILE_VOLATILE, buildoidvector(funcargtypes, 0), PointerGetDatum(NULL), PointerGetDatum(NULL), PointerGetDatum(NULL)); } /* * Likewise for the validator, if required; but we don't care about * its return type. */ if (pltemplate->tmplvalidator) { funcname = SystemFuncName(pltemplate->tmplvalidator); funcargtypes[0] = OIDOID; valOid = LookupFuncName(funcname, 1, funcargtypes, true); if (!OidIsValid(valOid)) { valOid = ProcedureCreate(pltemplate->tmplvalidator, PG_CATALOG_NAMESPACE, false, /* replace */ false, /* returnsSet */ VOIDOID, ClanguageId, F_FMGR_C_VALIDATOR, pltemplate->tmplvalidator, pltemplate->tmpllibrary, false, /* isAgg */ false, /* security_definer */ false, /* isStrict */ PROVOLATILE_VOLATILE, buildoidvector(funcargtypes, 1), PointerGetDatum(NULL), PointerGetDatum(NULL), PointerGetDatum(NULL)); } } else valOid = InvalidOid; /* ok, create it */ create_proc_lang(languageName, handlerOid, valOid, pltemplate->tmpltrusted); } else { /* * No template, so use the provided information. If there's no * handler clause, the user is trying to rely on a template that we * don't have, so complain accordingly. */ if (!stmt->plhandler) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("unsupported language \"%s\"", languageName), errhint("The supported languages are listed in the pg_pltemplate system catalog."))); /* * Lookup the PL handler function and check that it is of the expected * return type */ handlerOid = LookupFuncName(stmt->plhandler, 0, funcargtypes, false); funcrettype = get_func_rettype(handlerOid); if (funcrettype != LANGUAGE_HANDLEROID) { /* * We allow OPAQUE just so we can load old dump files. When we * see a handler function declared OPAQUE, change it to * LANGUAGE_HANDLER. (This is probably obsolete and removable?) */ if (funcrettype == OPAQUEOID) { ereport(WARNING, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("changing return type of function %s from \"opaque\" to \"language_handler\"", NameListToString(stmt->plhandler)))); SetFunctionReturnType(handlerOid, LANGUAGE_HANDLEROID); } else ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("function %s must return type \"language_handler\"", NameListToString(stmt->plhandler)))); } /* validate the validator function */ if (stmt->plvalidator) { funcargtypes[0] = OIDOID; valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false); /* return value is ignored, so we don't check the type */ } else valOid = InvalidOid; /* ok, create it */ create_proc_lang(languageName, handlerOid, valOid, stmt->pltrusted); } }
/* * Validate the generic options given to a FOREIGN DATA WRAPPER, SERVER, * USER MAPPING or FOREIGN TABLE that uses file_fdw. * * Raise an ERROR if the option or its value is considered invalid. */ Datum file_fdw_validator(PG_FUNCTION_ARGS) { List *options_list = untransformRelOptions(PG_GETARG_DATUM(0)); Oid catalog = PG_GETARG_OID(1); char *filename = NULL; DefElem *force_not_null = NULL; List *other_options = NIL; ListCell *cell; /* * Only superusers are allowed to set options of a file_fdw foreign table. * This is because the filename is one of those options, and we don't want * non-superusers to be able to determine which file gets read. * * Putting this sort of permissions check in a validator is a bit of a * crock, but there doesn't seem to be any other place that can enforce * the check more cleanly. * * Note that the valid_options[] array disallows setting filename at any * options level other than foreign table --- otherwise there'd still be a * security hole. */ if (catalog == ForeignTableRelationId && !superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("only superuser can change options of a file_fdw foreign table"))); /* * Check that only options supported by file_fdw, and allowed for the * current object type, are given. */ foreach(cell, options_list) { DefElem *def = (DefElem *) lfirst(cell); if (!is_valid_option(def->defname, catalog)) { struct FileFdwOption *opt; StringInfoData buf; /* * Unknown option specified, complain about it. Provide a hint * with list of valid options for the object. */ initStringInfo(&buf); for (opt = valid_options; opt->optname; opt++) { if (catalog == opt->optcontext) appendStringInfo(&buf, "%s%s", (buf.len > 0) ? ", " : "", opt->optname); } ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_OPTION_NAME), errmsg("invalid option \"%s\"", def->defname), errhint("Valid options in this context are: %s", buf.data))); } /* * Separate out filename and force_not_null, since ProcessCopyOptions * won't accept them. (force_not_null only comes in a boolean * per-column flavor here.) */ if (strcmp(def->defname, "filename") == 0) { if (filename) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); filename = defGetString(def); } else if (strcmp(def->defname, "force_not_null") == 0) { if (force_not_null) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); force_not_null = def; /* Don't care what the value is, as long as it's a legal boolean */ (void) defGetBoolean(def); } else other_options = lappend(other_options, def); }
/* -------------------------------- * InitPostgres * Initialize POSTGRES. * * The database can be specified by name, using the in_dbname parameter, or by * OID, using the dboid parameter. In the latter case, the actual database * name can be returned to the caller in out_dbname. If out_dbname isn't * NULL, it must point to a buffer of size NAMEDATALEN. * * In bootstrap mode no parameters are used. The autovacuum launcher process * doesn't use any parameters either, because it only goes far enough to be * able to read pg_database; it doesn't connect to any particular database. * In walsender mode only username is used. * * As of PostgreSQL 8.2, we expect InitProcess() was already called, so we * already have a PGPROC struct ... but it's not completely filled in yet. * * Note: * Be very careful with the order of calls in the InitPostgres function. * -------------------------------- */ void InitPostgres(const char *in_dbname, Oid dboid, const char *username, char *out_dbname) { bool bootstrap = IsBootstrapProcessingMode(); bool am_superuser; char *fullpath; char dbname[NAMEDATALEN]; elog(DEBUG3, "InitPostgres"); /* * Add my PGPROC struct to the ProcArray. * * Once I have done this, I am visible to other backends! */ InitProcessPhase2(); /* * Initialize my entry in the shared-invalidation manager's array of * per-backend data. * * Sets up MyBackendId, a unique backend identifier. */ MyBackendId = InvalidBackendId; SharedInvalBackendInit(false); if (MyBackendId > MaxBackends || MyBackendId <= 0) elog(FATAL, "bad backend ID: %d", MyBackendId); /* Now that we have a BackendId, we can participate in ProcSignal */ ProcSignalInit(MyBackendId); /* * Also set up timeout handlers needed for backend operation. We need * these in every case except bootstrap. */ if (!bootstrap) { RegisterTimeout(DEADLOCK_TIMEOUT, CheckDeadLock); RegisterTimeout(STATEMENT_TIMEOUT, StatementTimeoutHandler); RegisterTimeout(LOCK_TIMEOUT, LockTimeoutHandler); } /* * bufmgr needs another initialization call too */ InitBufferPoolBackend(); /* * Initialize local process's access to XLOG. */ if (IsUnderPostmaster) { /* * The postmaster already started the XLOG machinery, but we need to * call InitXLOGAccess(), if the system isn't in hot-standby mode. * This is handled by calling RecoveryInProgress and ignoring the * result. */ (void) RecoveryInProgress(); } else { /* * We are either a bootstrap process or a standalone backend. Either * way, start up the XLOG machinery, and register to have it closed * down at exit. */ StartupXLOG(); on_shmem_exit(ShutdownXLOG, 0); } /* * Initialize the relation cache and the system catalog caches. Note that * no catalog access happens here; we only set up the hashtable structure. * We must do this before starting a transaction because transaction abort * would try to touch these hashtables. */ RelationCacheInitialize(); InitCatalogCache(); InitPlanCache(); /* Initialize portal manager */ EnablePortalManager(); /* Initialize stats collection --- must happen before first xact */ if (!bootstrap) pgstat_initialize(); /* * Load relcache entries for the shared system catalogs. This must create * at least entries for pg_database and catalogs used for authentication. */ RelationCacheInitializePhase2(); /* * Set up process-exit callback to do pre-shutdown cleanup. This is the * first before_shmem_exit callback we register; thus, this will be the * last thing we do before low-level modules like the buffer manager begin * to close down. We need to have this in place before we begin our first * transaction --- if we fail during the initialization transaction, as is * entirely possible, we need the AbortTransaction call to clean up. */ before_shmem_exit(ShutdownPostgres, 0); /* The autovacuum launcher is done here */ if (IsAutoVacuumLauncherProcess()) return; /* * Start a new transaction here before first access to db, and get a * snapshot. We don't have a use for the snapshot itself, but we're * interested in the secondary effect that it sets RecentGlobalXmin. (This * is critical for anything that reads heap pages, because HOT may decide * to prune them even if the process doesn't attempt to modify any * tuples.) */ if (!bootstrap) { /* statement_timestamp must be set for timeouts to work correctly */ SetCurrentStatementStartTimestamp(); StartTransactionCommand(); /* * transaction_isolation will have been set to the default by the * above. If the default is "serializable", and we are in hot * standby, we will fail if we don't change it to something lower. * Fortunately, "read committed" is plenty good enough. */ XactIsoLevel = XACT_READ_COMMITTED; (void) GetTransactionSnapshot(); } /* * Perform client authentication if necessary, then figure out our * postgres user ID, and see if we are a superuser. * * In standalone mode and in autovacuum worker processes, we use a fixed * ID, otherwise we figure it out from the authenticated user name. */ if (bootstrap || IsAutoVacuumWorkerProcess()) { InitializeSessionUserIdStandalone(); am_superuser = true; } else if (!IsUnderPostmaster) { InitializeSessionUserIdStandalone(); am_superuser = true; if (!ThereIsAtLeastOneRole()) ereport(WARNING, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("no roles are defined in this database system"), errhint("You should immediately run CREATE USER \"%s\" SUPERUSER;.", username))); } else if (IsBackgroundWorker) { if (username == NULL) { InitializeSessionUserIdStandalone(); am_superuser = true; } else { InitializeSessionUserId(username); am_superuser = superuser(); } } else { /* normal multiuser case */ Assert(MyProcPort != NULL); PerformAuthentication(MyProcPort); InitializeSessionUserId(username); am_superuser = superuser(); } /* * If we're trying to shut down, only superusers can connect, and new * replication connections are not allowed. */ if ((!am_superuser || am_walsender) && MyProcPort != NULL && MyProcPort->canAcceptConnections == CAC_WAITBACKUP) { if (am_walsender) ereport(FATAL, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("new replication connections are not allowed during database shutdown"))); else ereport(FATAL, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to connect during database shutdown"))); } /* * Binary upgrades only allowed super-user connections */ if (IsBinaryUpgrade && !am_superuser) { ereport(FATAL, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to connect in binary upgrade mode"))); } /* * The last few connections slots are reserved for superusers. Although * replication connections currently require superuser privileges, we * don't allow them to consume the reserved slots, which are intended for * interactive use. */ if ((!am_superuser || am_walsender) && ReservedBackends > 0 && !HaveNFreeProcs(ReservedBackends)) ereport(FATAL, (errcode(ERRCODE_TOO_MANY_CONNECTIONS), errmsg("remaining connection slots are reserved for non-replication superuser connections"))); /* Check replication permissions needed for walsender processes. */ if (am_walsender) { Assert(!bootstrap); if (!superuser() && !has_rolreplication(GetUserId())) ereport(FATAL, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser or replication role to start walsender"))); } /* * If this is a plain walsender only supporting physical replication, we * don't want to connect to any particular database. Just finish the * backend startup by processing any options from the startup packet, and * we're done. */ if (am_walsender && !am_db_walsender) { /* process any options passed in the startup packet */ if (MyProcPort != NULL) process_startup_options(MyProcPort, am_superuser); /* Apply PostAuthDelay as soon as we've read all options */ if (PostAuthDelay > 0) pg_usleep(PostAuthDelay * 1000000L); /* initialize client encoding */ InitializeClientEncoding(); /* report this backend in the PgBackendStatus array */ pgstat_bestart(); /* close the transaction we started above */ CommitTransactionCommand(); return; } /* * Set up the global variables holding database id and default tablespace. * But note we won't actually try to touch the database just yet. * * We take a shortcut in the bootstrap case, otherwise we have to look up * the db's entry in pg_database. */ if (bootstrap) { MyDatabaseId = TemplateDbOid; MyDatabaseTableSpace = DEFAULTTABLESPACE_OID; } else if (in_dbname != NULL) { HeapTuple tuple; Form_pg_database dbform; tuple = GetDatabaseTuple(in_dbname); if (!HeapTupleIsValid(tuple)) ereport(FATAL, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", in_dbname))); dbform = (Form_pg_database) GETSTRUCT(tuple); MyDatabaseId = HeapTupleGetOid(tuple); MyDatabaseTableSpace = dbform->dattablespace; /* take database name from the caller, just for paranoia */ strlcpy(dbname, in_dbname, sizeof(dbname)); } else if (OidIsValid(dboid)) { /* caller specified database by OID */ HeapTuple tuple; Form_pg_database dbform; tuple = GetDatabaseTupleByOid(dboid); if (!HeapTupleIsValid(tuple)) ereport(FATAL, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database %u does not exist", dboid))); dbform = (Form_pg_database) GETSTRUCT(tuple); MyDatabaseId = HeapTupleGetOid(tuple); MyDatabaseTableSpace = dbform->dattablespace; Assert(MyDatabaseId == dboid); strlcpy(dbname, NameStr(dbform->datname), sizeof(dbname)); /* pass the database name back to the caller */ if (out_dbname) strcpy(out_dbname, dbname); } else { /* * If this is a background worker not bound to any particular * database, we're done now. Everything that follows only makes * sense if we are bound to a specific database. We do need to * close the transaction we started before returning. */ if (!bootstrap) CommitTransactionCommand(); return; } /* * Now, take a writer's lock on the database we are trying to connect to. * If there is a concurrently running DROP DATABASE on that database, this * will block us until it finishes (and has committed its update of * pg_database). * * Note that the lock is not held long, only until the end of this startup * transaction. This is OK since we will advertise our use of the * database in the ProcArray before dropping the lock (in fact, that's the * next thing to do). Anyone trying a DROP DATABASE after this point will * see us in the array once they have the lock. Ordering is important for * this because we don't want to advertise ourselves as being in this * database until we have the lock; otherwise we create what amounts to a * deadlock with CountOtherDBBackends(). * * Note: use of RowExclusiveLock here is reasonable because we envision * our session as being a concurrent writer of the database. If we had a * way of declaring a session as being guaranteed-read-only, we could use * AccessShareLock for such sessions and thereby not conflict against * CREATE DATABASE. */ if (!bootstrap) LockSharedObject(DatabaseRelationId, MyDatabaseId, 0, RowExclusiveLock); /* * Now we can mark our PGPROC entry with the database ID. * * We assume this is an atomic store so no lock is needed; though actually * things would work fine even if it weren't atomic. Anyone searching the * ProcArray for this database's ID should hold the database lock, so they * would not be executing concurrently with this store. A process looking * for another database's ID could in theory see a chance match if it read * a partially-updated databaseId value; but as long as all such searches * wait and retry, as in CountOtherDBBackends(), they will certainly see * the correct value on their next try. */ MyProc->databaseId = MyDatabaseId; /* * We established a catalog snapshot while reading pg_authid and/or * pg_database; but until we have set up MyDatabaseId, we won't react to * incoming sinval messages for unshared catalogs, so we won't realize it * if the snapshot has been invalidated. Assume it's no good anymore. */ InvalidateCatalogSnapshot(); /* * Recheck pg_database to make sure the target database hasn't gone away. * If there was a concurrent DROP DATABASE, this ensures we will die * cleanly without creating a mess. */ if (!bootstrap) { HeapTuple tuple; tuple = GetDatabaseTuple(dbname); if (!HeapTupleIsValid(tuple) || MyDatabaseId != HeapTupleGetOid(tuple) || MyDatabaseTableSpace != ((Form_pg_database) GETSTRUCT(tuple))->dattablespace) ereport(FATAL, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", dbname), errdetail("It seems to have just been dropped or renamed."))); } /* * Now we should be able to access the database directory safely. Verify * it's there and looks reasonable. */ fullpath = GetDatabasePath(MyDatabaseId, MyDatabaseTableSpace); if (!bootstrap) { if (access(fullpath, F_OK) == -1) { if (errno == ENOENT) ereport(FATAL, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", dbname), errdetail("The database subdirectory \"%s\" is missing.", fullpath))); else ereport(FATAL, (errcode_for_file_access(), errmsg("could not access directory \"%s\": %m", fullpath))); } ValidatePgVersion(fullpath); } SetDatabasePath(fullpath); /* * It's now possible to do real access to the system catalogs. * * Load relcache entries for the system catalogs. This must create at * least the minimum set of "nailed-in" cache entries. */ RelationCacheInitializePhase3(); /* set up ACL framework (so CheckMyDatabase can check permissions) */ initialize_acl(); /* * Re-read the pg_database row for our database, check permissions and set * up database-specific GUC settings. We can't do this until all the * database-access infrastructure is up. (Also, it wants to know if the * user is a superuser, so the above stuff has to happen first.) */ if (!bootstrap) CheckMyDatabase(dbname, am_superuser); /* * Now process any command-line switches and any additional GUC variable * settings passed in the startup packet. We couldn't do this before * because we didn't know if client is a superuser. */ if (MyProcPort != NULL) process_startup_options(MyProcPort, am_superuser); /* Process pg_db_role_setting options */ process_settings(MyDatabaseId, GetSessionUserId()); /* Apply PostAuthDelay as soon as we've read all options */ if (PostAuthDelay > 0) pg_usleep(PostAuthDelay * 1000000L); /* * Initialize various default states that can't be set up until we've * selected the active user and gotten the right GUC settings. */ /* set default namespace search path */ InitializeSearchPath(); /* initialize client encoding */ InitializeClientEncoding(); /* report this backend in the PgBackendStatus array */ if (!bootstrap) pgstat_bestart(); /* close the transaction we started above */ if (!bootstrap) CommitTransactionCommand(); }