/* * find_inheritance_children * * Returns a list containing the OIDs of all relations which * inherit *directly* from the relation with OID 'parentrelId'. * * The specified lock type is acquired on each child relation (but not on the * given rel; caller should already have locked it). If lockmode is NoLock * then no locks are acquired, but caller must beware of race conditions * against possible DROPs of child relations. */ List * find_inheritance_children(Oid parentrelId, LOCKMODE lockmode) { List *list = NIL; Relation relation; SysScanDesc scan; ScanKeyData key[1]; HeapTuple inheritsTuple; Oid inhrelid; Oid *oidarr; int maxoids, numoids, i; /* * Can skip the scan if pg_class shows the relation has never had a * subclass. */ if (!has_subclass(parentrelId)) return NIL; /* * Scan pg_inherits and build a working array of subclass OIDs. */ maxoids = 32; oidarr = (Oid *) palloc(maxoids * sizeof(Oid)); numoids = 0; relation = heap_open(InheritsRelationId, AccessShareLock); ScanKeyInit(&key[0], Anum_pg_inherits_inhparent, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(parentrelId)); scan = systable_beginscan(relation, InheritsParentIndexId, true, NULL, 1, key); while ((inheritsTuple = systable_getnext(scan)) != NULL) { inhrelid = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhrelid; if (numoids >= maxoids) { maxoids *= 2; oidarr = (Oid *) repalloc(oidarr, maxoids * sizeof(Oid)); } oidarr[numoids++] = inhrelid; } systable_endscan(scan); heap_close(relation, AccessShareLock); /* * If we found more than one child, sort them by OID. This ensures * reasonably consistent behavior regardless of the vagaries of an * indexscan. This is important since we need to be sure all backends * lock children in the same order to avoid needless deadlocks. */ if (numoids > 1) qsort(oidarr, numoids, sizeof(Oid), oid_cmp); /* * Acquire locks and build the result list. */ for (i = 0; i < numoids; i++) { inhrelid = oidarr[i]; if (lockmode != NoLock) { /* Get the lock to synchronize against concurrent drop */ LockRelationOid(inhrelid, lockmode); /* * Now that we have the lock, double-check to see if the relation * really exists or not. If not, assume it was dropped while we * waited to acquire lock, and ignore it. */ if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(inhrelid))) { /* Release useless lock */ UnlockRelationOid(inhrelid, lockmode); /* And ignore this relation */ continue; } } list = lappend_oid(list, inhrelid); } pfree(oidarr); return list; }
/* * regprocin - converts "proname" to proc OID * * We also accept a numeric OID, for symmetry with the output routine. * * '-' signifies unknown (OID 0). In all other cases, the input must * match an existing pg_proc entry. */ Datum regprocin(PG_FUNCTION_ARGS) { char *pro_name_or_oid = PG_GETARG_CSTRING(0); RegProcedure result = InvalidOid; List *names; FuncCandidateList clist; /* '-' ? */ if (strcmp(pro_name_or_oid, "-") == 0) PG_RETURN_OID(InvalidOid); /* Numeric OID? */ if (pro_name_or_oid[0] >= '0' && pro_name_or_oid[0] <= '9' && strspn(pro_name_or_oid, "0123456789") == strlen(pro_name_or_oid)) { result = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(pro_name_or_oid))); PG_RETURN_OID(result); } /* Else it's a name, possibly schema-qualified */ /* * In bootstrap mode we assume the given name is not schema-qualified, and * just search pg_proc for a unique match. This is needed for * initializing other system catalogs (pg_namespace may not exist yet, and * certainly there are no schemas other than pg_catalog). */ if (IsBootstrapProcessingMode()) { int matches = 0; Relation hdesc; ScanKeyData skey[1]; SysScanDesc sysscan; HeapTuple tuple; ScanKeyInit(&skey[0], Anum_pg_proc_proname, BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(pro_name_or_oid)); hdesc = heap_open(ProcedureRelationId, AccessShareLock); sysscan = systable_beginscan(hdesc, ProcedureNameArgsNspIndexId, true, NULL, 1, skey); while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) { result = (RegProcedure) HeapTupleGetOid(tuple); if (++matches > 1) break; } systable_endscan(sysscan); heap_close(hdesc, AccessShareLock); if (matches == 0) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function \"%s\" does not exist", pro_name_or_oid))); else if (matches > 1) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_FUNCTION), errmsg("more than one function named \"%s\"", pro_name_or_oid))); PG_RETURN_OID(result); } /* * Normal case: parse the name into components and see if it matches any * pg_proc entries in the current search path. */ names = stringToQualifiedNameList(pro_name_or_oid); clist = FuncnameGetCandidates(names, -1, NIL, false, false, false); if (clist == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function \"%s\" does not exist", pro_name_or_oid))); else if (clist->next != NULL) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_FUNCTION), errmsg("more than one function named \"%s\"", pro_name_or_oid))); result = clist->oid; PG_RETURN_OID(result); }
static int64 PersistentBuild_BuildDb( Oid dbOid, bool mirrored) { int64 count = 0; Relation gp_global_sequence; Relation pg_database; HeapTuple tuple; HeapScanDesc scandesc; Form_pg_database form_pg_database; DatabaseInfo *info; Oid defaultTablespace; int t; bool collectGpRelationNodeInfo, collectAppendOnlyCatalogSegmentInfo; /* * Turn this on so we don't try to fetch persistence information from * gp_releation_node for gp_relation_node and its index until we've done the * assignment with PersistentRelation_AddCreated. */ gp_before_persistence_work = true; /* * If the gp_global_sequence table hasn't been populated yet then we need * to populate it before we can procede with building the rest of the * persistent tables. */ gp_global_sequence = heap_open(GpGlobalSequenceRelationId, RowExclusiveLock); scandesc = heap_beginscan(gp_global_sequence, SnapshotAny, 0, NULL); tuple = heap_getnext(scandesc, ForwardScanDirection); if (!HeapTupleIsValid(tuple)) { TupleDesc tupDesc; Datum values[Natts_gp_global_sequence]; bool nulls[Natts_gp_global_sequence]; /* Insert N frozen tuples of value 0 */ tupDesc = RelationGetDescr(gp_global_sequence); MemSet(nulls, false, sizeof(nulls)); values[Anum_gp_global_sequence_sequence_num-1] = Int64GetDatum(0); tuple = heap_form_tuple(tupDesc, values, nulls); if (!HeapTupleIsValid(tuple)) elog(ERROR, "failed to build global sequence tuple"); for (t = 0; t < GpGlobalSequence_MaxSequenceTid; t++) frozen_heap_insert(gp_global_sequence, tuple); } heap_endscan(scandesc); heap_close(gp_global_sequence, RowExclusiveLock); /* Lookup the information for the current database */ pg_database = heap_open(DatabaseRelationId, AccessShareLock); /* Fetch a copy of the tuple to scribble on */ tuple = SearchSysCacheCopy(DATABASEOID, ObjectIdGetDatum(dbOid), 0, 0, 0); if (!HeapTupleIsValid(tuple)) elog(ERROR, "could not find tuple for database %u", dbOid); form_pg_database = (Form_pg_database) GETSTRUCT(tuple); defaultTablespace = form_pg_database->dattablespace; if (Debug_persistent_print) elog(Persistent_DebugPrintLevel(), "PersistentBuild_BuildDb: dbOid %u, '%s'", dbOid, form_pg_database->datname.data); /* * Special call here to scan the persistent meta-data structures so we are open for * business and then we can add information. */ PersistentFileSysObj_BuildInitScan(); if (gp_upgrade_mode && (Gp_role == GP_ROLE_DISPATCH || Gp_role == GP_ROLE_UTILITY)){ collectGpRelationNodeInfo = false; collectAppendOnlyCatalogSegmentInfo = false; }else{ collectGpRelationNodeInfo = true; collectAppendOnlyCatalogSegmentInfo = true; } info = DatabaseInfo_Collect( dbOid, defaultTablespace, collectGpRelationNodeInfo, collectAppendOnlyCatalogSegmentInfo, /* scanFileSystem */ true); for (t = 0; t < info->tablespacesCount; t++) { Oid tablespace = info->tablespaces[t]; DbDirNode dbDirNode; ItemPointerData persistentTid; if (tablespace == GLOBALTABLESPACE_OID) continue; dbDirNode.tablespace = tablespace; dbDirNode.database = dbOid; PersistentDatabase_AddCreated( &dbDirNode, &persistentTid, /* flushToXLog */ false); } PersistentBuild_PopulateGpRelationNode( info, defaultTablespace, &count); heap_close(pg_database, AccessShareLock); gp_before_persistence_work = false; /* * Since we have written XLOG records with <persistentTid, * persistentSerialNum> of zeroes because of the gp_before_persistence_work * GUC, lets do a checkpoint to force out all buffer pool pages so we never * try to redo those XLOG records in Crash Recovery. */ CreateCheckPoint(false, true); return count; }
/* * scram_utils_verifier * * Generate a verifier for SCRAM-SHA-256 authentication and update the * related user's pg_authid entry as per RFC 7677. */ Datum scram_utils_verifier(PG_FUNCTION_ARGS) { pg_saslprep_rc rc; char *username = text_to_cstring(PG_GETARG_TEXT_PP(0)); const char *password = text_to_cstring(PG_GETARG_TEXT_PP(1)); int iterations = PG_GETARG_INT32(2); int saltlen = PG_GETARG_INT32(3); char *prep_password = NULL; char *saltbuf; char *verifier; HeapTuple oldtuple, newtuple; TupleDesc dsc; Relation rel; Datum repl_val[Natts_pg_authid]; bool repl_null[Natts_pg_authid]; bool repl_repl[Natts_pg_authid]; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to update one's SCRAM verifier")))); /* Control iteration number and salt length */ if (iterations <= 0) { ereport(WARNING, (errmsg("Incorrect iteration number, defaulting to %d", SCRAM_DEFAULT_ITERATIONS))); iterations = SCRAM_DEFAULT_ITERATIONS; } if (saltlen <= 0) { ereport(WARNING, (errmsg("Incorrect salt length number, defaulting to %d", SCRAM_DEFAULT_SALT_LEN))); saltlen = SCRAM_DEFAULT_SALT_LEN; } /* * Normalize the password with SASLprep. If that doesn't work, because * the password isn't valid UTF-8 or contains prohibited characters, just * proceed with the original password. (See comments at top of file.) */ rc = pg_saslprep(password, &prep_password); if (rc == SASLPREP_OOM) elog(ERROR, "out of memory"); if (rc == SASLPREP_SUCCESS) password = (const char *) prep_password; /* Generate a random salt */ saltbuf = palloc(sizeof(char) * saltlen); if (!pg_strong_random(saltbuf, saltlen)) elog(ERROR, "Failed to generate random salt"); /* Build verifier */ verifier = scram_build_verifier(saltbuf, saltlen, iterations, password); if (prep_password) pfree(prep_password); /* Verifier is built, so update pg_authid with it */ rel = heap_open(AuthIdRelationId, RowExclusiveLock); oldtuple = SearchSysCache1(AUTHNAME, CStringGetDatum(username)); if (!HeapTupleIsValid(oldtuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("role \"%s\" does not exist", username))); /* OK, construct the modified tuple with new password */ memset(repl_repl, false, sizeof(repl_repl)); memset(repl_null, false, sizeof(repl_null)); repl_repl[Anum_pg_authid_rolpassword - 1] = true; repl_val[Anum_pg_authid_rolpassword - 1] = CStringGetTextDatum(verifier); repl_null[Anum_pg_authid_rolpassword - 1] = false; dsc = RelationGetDescr(rel); newtuple = heap_modify_tuple(oldtuple, dsc, repl_val, repl_null, repl_repl); CatalogTupleUpdate(rel, &oldtuple->t_self, newtuple); ReleaseSysCache(oldtuple); /* * Close pg_authid, but keep lock till commit. */ heap_close(rel, NoLock); PG_RETURN_NULL(); }
/* * Look to see if we have template information for the given language name. */ static PLTemplate * find_language_template(const char *languageName) { PLTemplate *result; Relation rel; SysScanDesc scan; ScanKeyData key; HeapTuple tup; rel = heap_open(PLTemplateRelationId, AccessShareLock); ScanKeyInit(&key, Anum_pg_pltemplate_tmplname, BTEqualStrategyNumber, F_NAMEEQ, NameGetDatum(languageName)); scan = systable_beginscan(rel, PLTemplateNameIndexId, true, SnapshotNow, 1, &key); tup = systable_getnext(scan); if (HeapTupleIsValid(tup)) { Form_pg_pltemplate tmpl = (Form_pg_pltemplate) GETSTRUCT(tup); Datum datum; bool isnull; result = (PLTemplate *) palloc0(sizeof(PLTemplate)); result->tmpltrusted = tmpl->tmpltrusted; result->tmpldbacreate = tmpl->tmpldbacreate; /* Remaining fields are variable-width so we need heap_getattr */ datum = heap_getattr(tup, Anum_pg_pltemplate_tmplhandler, RelationGetDescr(rel), &isnull); if (!isnull) result->tmplhandler = TextDatumGetCString(datum); datum = heap_getattr(tup, Anum_pg_pltemplate_tmplinline, RelationGetDescr(rel), &isnull); if (!isnull) result->tmplinline = TextDatumGetCString(datum); datum = heap_getattr(tup, Anum_pg_pltemplate_tmplvalidator, RelationGetDescr(rel), &isnull); if (!isnull) result->tmplvalidator = TextDatumGetCString(datum); datum = heap_getattr(tup, Anum_pg_pltemplate_tmpllibrary, RelationGetDescr(rel), &isnull); if (!isnull) result->tmpllibrary = TextDatumGetCString(datum); /* Ignore template if handler or library info is missing */ if (!result->tmplhandler || !result->tmpllibrary) result = NULL; } else result = NULL; systable_endscan(scan); heap_close(rel, AccessShareLock); return result; }
/* * sepgsql_attribute_post_create * * This routine assigns a default security label on a newly defined * column, using ALTER TABLE ... ADD COLUMN. * Note that this routine is not invoked in the case of CREATE TABLE, * although it also defines columns in addition to table. */ void sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum) { Relation rel; ScanKeyData skey[2]; SysScanDesc sscan; HeapTuple tuple; char *scontext; char *tcontext; char *ncontext; ObjectAddress object; Form_pg_attribute attForm; StringInfoData audit_name; /* * Only attributes within regular relation have individual security * labels. */ if (get_rel_relkind(relOid) != RELKIND_RELATION) return; /* * Compute a default security label of the new column underlying the * specified relation, and check permission to create it. */ rel = heap_open(AttributeRelationId, AccessShareLock); ScanKeyInit(&skey[0], Anum_pg_attribute_attrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relOid)); ScanKeyInit(&skey[1], Anum_pg_attribute_attnum, BTEqualStrategyNumber, F_INT2EQ, Int16GetDatum(attnum)); sscan = systable_beginscan(rel, AttributeRelidNumIndexId, true, SnapshotSelf, 2, &skey[0]); tuple = systable_getnext(sscan); if (!HeapTupleIsValid(tuple)) elog(ERROR, "catalog lookup failed for column %d of relation %u", attnum, relOid); attForm = (Form_pg_attribute) GETSTRUCT(tuple); scontext = sepgsql_get_client_label(); tcontext = sepgsql_get_label(RelationRelationId, relOid, 0); ncontext = sepgsql_compute_create(scontext, tcontext, SEPG_CLASS_DB_COLUMN, NameStr(attForm->attname)); /* * check db_column:{create} permission */ object.classId = RelationRelationId; object.objectId = relOid; object.objectSubId = 0; initStringInfo(&audit_name); appendStringInfo(&audit_name, "%s.%s", getObjectIdentity(&object), quote_identifier(NameStr(attForm->attname))); sepgsql_avc_check_perms_label(ncontext, SEPG_CLASS_DB_COLUMN, SEPG_DB_COLUMN__CREATE, audit_name.data, true); /* * Assign the default security label on a new procedure */ object.classId = RelationRelationId; object.objectId = relOid; object.objectSubId = attnum; SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext); systable_endscan(sscan); heap_close(rel, AccessShareLock); pfree(tcontext); pfree(ncontext); }
/* * This is a copy of swap_relation_files in cluster.c, but it also swaps * relfrozenxid. */ static void swap_heap_or_index_files(Oid r1, Oid r2) { Relation relRelation; HeapTuple reltup1, reltup2; Form_pg_class relform1, relform2; Oid swaptemp; CatalogIndexState indstate; /* We need writable copies of both pg_class tuples. */ relRelation = heap_open(RelationRelationId, RowExclusiveLock); reltup1 = SearchSysCacheCopy(RELOID, ObjectIdGetDatum(r1), 0, 0, 0); if (!HeapTupleIsValid(reltup1)) elog(ERROR, "cache lookup failed for relation %u", r1); relform1 = (Form_pg_class) GETSTRUCT(reltup1); reltup2 = SearchSysCacheCopy(RELOID, ObjectIdGetDatum(r2), 0, 0, 0); if (!HeapTupleIsValid(reltup2)) elog(ERROR, "cache lookup failed for relation %u", r2); relform2 = (Form_pg_class) GETSTRUCT(reltup2); Assert(relform1->relkind == relform2->relkind); /* * Actually swap the fields in the two tuples */ swaptemp = relform1->relfilenode; relform1->relfilenode = relform2->relfilenode; relform2->relfilenode = swaptemp; swaptemp = relform1->reltablespace; relform1->reltablespace = relform2->reltablespace; relform2->reltablespace = swaptemp; swaptemp = relform1->reltoastrelid; relform1->reltoastrelid = relform2->reltoastrelid; relform2->reltoastrelid = swaptemp; /* set rel1's frozen Xid to larger one */ if (TransactionIdIsNormal(relform1->relfrozenxid)) { if (TransactionIdFollows(relform1->relfrozenxid, relform2->relfrozenxid)) relform1->relfrozenxid = relform2->relfrozenxid; else relform2->relfrozenxid = relform1->relfrozenxid; } /* swap size statistics too, since new rel has freshly-updated stats */ { #if PG_VERSION_NUM >= 90300 int32 swap_pages; #else int4 swap_pages; #endif float4 swap_tuples; swap_pages = relform1->relpages; relform1->relpages = relform2->relpages; relform2->relpages = swap_pages; swap_tuples = relform1->reltuples; relform1->reltuples = relform2->reltuples; relform2->reltuples = swap_tuples; } /* Update the tuples in pg_class */ simple_heap_update(relRelation, &reltup1->t_self, reltup1); simple_heap_update(relRelation, &reltup2->t_self, reltup2); /* Keep system catalogs current */ indstate = CatalogOpenIndexes(relRelation); CatalogIndexInsert(indstate, reltup1); CatalogIndexInsert(indstate, reltup2); CatalogCloseIndexes(indstate); /* * If we have toast tables associated with the relations being swapped, * change their dependency links to re-associate them with their new * owning relations. Otherwise the wrong one will get dropped ... * * NOTE: it is possible that only one table has a toast table; this can * happen in CLUSTER if there were dropped columns in the old table, and * in ALTER TABLE when adding or changing type of columns. * * NOTE: at present, a TOAST table's only dependency is the one on its * owning table. If more are ever created, we'd need to use something * more selective than deleteDependencyRecordsFor() to get rid of only the * link we want. */ if (relform1->reltoastrelid || relform2->reltoastrelid) { ObjectAddress baseobject, toastobject; long count; /* Delete old dependencies */ if (relform1->reltoastrelid) { count = deleteDependencyRecordsFor(RelationRelationId, relform1->reltoastrelid, false); if (count != 1) elog(ERROR, "expected one dependency record for TOAST table, found %ld", count); } if (relform2->reltoastrelid) { count = deleteDependencyRecordsFor(RelationRelationId, relform2->reltoastrelid, false); if (count != 1) elog(ERROR, "expected one dependency record for TOAST table, found %ld", count); } /* Register new dependencies */ baseobject.classId = RelationRelationId; baseobject.objectSubId = 0; toastobject.classId = RelationRelationId; toastobject.objectSubId = 0; if (relform1->reltoastrelid) { baseobject.objectId = r1; toastobject.objectId = relform1->reltoastrelid; recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL); } if (relform2->reltoastrelid) { baseobject.objectId = r2; toastobject.objectId = relform2->reltoastrelid; recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL); } } /* * Blow away the old relcache entries now. We need this kluge because * relcache.c keeps a link to the smgr relation for the physical file, and * that will be out of date as soon as we do CommandCounterIncrement. * Whichever of the rels is the second to be cleared during cache * invalidation will have a dangling reference to an already-deleted smgr * relation. Rather than trying to avoid this by ordering operations just * so, it's easiest to not have the relcache entries there at all. * (Fortunately, since one of the entries is local in our transaction, * it's sufficient to clear out our own relcache this way; the problem * cannot arise for other backends when they see our update on the * non-local relation.) */ RelationForgetRelation(r1); RelationForgetRelation(r2); /* Clean up. */ heap_freetuple(reltup1); heap_freetuple(reltup2); heap_close(relRelation, RowExclusiveLock); }
/* * CloseErrorTable * * Done using the error table. Close it. */ void CloseErrorTable(CdbSreh *cdbsreh) { /* close the error relation */ if (cdbsreh->errtbl) heap_close(cdbsreh->errtbl, NoLock); }
/* ---------------------------------------------------------------- * TypeCreate * * This does all the necessary work needed to define a new type. * * Returns the OID assigned to the new type. If newTypeOid is * zero (the normal case), a new OID is created; otherwise we * use exactly that OID. * ---------------------------------------------------------------- */ Oid TypeCreateWithOptions(Oid newTypeOid, const char *typeName, Oid typeNamespace, Oid relationOid, /* only for relation rowtypes */ char relationKind, /* ditto */ Oid ownerId, int16 internalSize, char typeType, char typDelim, Oid inputProcedure, Oid outputProcedure, Oid receiveProcedure, Oid sendProcedure, Oid typmodinProcedure, Oid typmodoutProcedure, Oid analyzeProcedure, Oid elementType, bool isImplicitArray, Oid arrayType, Oid baseType, const char *defaultTypeValue, /* human readable rep */ char *defaultTypeBin, /* cooked rep */ bool passedByValue, char alignment, char storage, int32 typeMod, int32 typNDims, /* Array dimensions for baseType */ bool typeNotNull, Datum typoptions) { Relation pg_type_desc; Oid typeObjectId; bool rebuildDeps = false; HeapTuple tup; bool nulls[Natts_pg_type]; bool replaces[Natts_pg_type]; Datum values[Natts_pg_type]; NameData name; int i; /* * We assume that the caller validated the arguments individually, but did * not check for bad combinations. * * Validate size specifications: either positive (fixed-length) or -1 * (varlena) or -2 (cstring). Pass-by-value types must have a fixed * length not more than sizeof(Datum). */ if (!(internalSize > 0 || internalSize == -1 || internalSize == -2)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("invalid type internal size %d", internalSize))); if (passedByValue && (internalSize <= 0 || internalSize > (int16) sizeof(Datum))) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("internal size %d is invalid for passed-by-value type", internalSize))); /* Only varlena types can be toasted */ if (storage != 'p' && internalSize != -1) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("fixed-size types must have storage PLAIN"))); /* * initialize arrays needed for heap_form_tuple or heap_modify_tuple */ for (i = 0; i < Natts_pg_type; ++i) { nulls[i] = false; replaces[i] = true; values[i] = (Datum) 0; } /* * initialize the *values information */ i = 0; namestrcpy(&name, typeName); values[i++] = NameGetDatum(&name); /* typname */ values[i++] = ObjectIdGetDatum(typeNamespace); /* typnamespace */ values[i++] = ObjectIdGetDatum(ownerId); /* typowner */ values[i++] = Int16GetDatum(internalSize); /* typlen */ values[i++] = BoolGetDatum(passedByValue); /* typbyval */ values[i++] = CharGetDatum(typeType); /* typtype */ values[i++] = BoolGetDatum(true); /* typisdefined */ values[i++] = CharGetDatum(typDelim); /* typdelim */ values[i++] = ObjectIdGetDatum(relationOid); /* typrelid */ values[i++] = ObjectIdGetDatum(elementType); /* typelem */ values[i++] = ObjectIdGetDatum(arrayType); /* typarray */ values[i++] = ObjectIdGetDatum(inputProcedure); /* typinput */ values[i++] = ObjectIdGetDatum(outputProcedure); /* typoutput */ values[i++] = ObjectIdGetDatum(receiveProcedure); /* typreceive */ values[i++] = ObjectIdGetDatum(sendProcedure); /* typsend */ values[i++] = ObjectIdGetDatum(typmodinProcedure); /* typmodin */ values[i++] = ObjectIdGetDatum(typmodoutProcedure); /* typmodout */ values[i++] = ObjectIdGetDatum(analyzeProcedure); /* typanalyze */ values[i++] = CharGetDatum(alignment); /* typalign */ values[i++] = CharGetDatum(storage); /* typstorage */ values[i++] = BoolGetDatum(typeNotNull); /* typnotnull */ values[i++] = ObjectIdGetDatum(baseType); /* typbasetype */ values[i++] = Int32GetDatum(typeMod); /* typtypmod */ values[i++] = Int32GetDatum(typNDims); /* typndims */ /* * initialize the default binary value for this type. Check for nulls of * course. */ if (defaultTypeBin) values[i] = CStringGetTextDatum(defaultTypeBin); else nulls[i] = true; i++; /* typdefaultbin */ /* * initialize the default value for this type. */ if (defaultTypeValue) values[i] = CStringGetTextDatum(defaultTypeValue); else nulls[i] = true; i++; /* typdefault */ /* * open pg_type and prepare to insert or update a row. * * NOTE: updating will not work correctly in bootstrap mode; but we don't * expect to be overwriting any shell types in bootstrap mode. */ pg_type_desc = heap_open(TypeRelationId, RowExclusiveLock); tup = SearchSysCacheCopy(TYPENAMENSP, CStringGetDatum(typeName), ObjectIdGetDatum(typeNamespace), 0, 0); if (HeapTupleIsValid(tup)) { /* * check that the type is not already defined. It may exist as a * shell type, however. */ if (((Form_pg_type) GETSTRUCT(tup))->typisdefined) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("type \"%s\" already exists", typeName))); /* * shell type must have been created by same owner */ if (((Form_pg_type) GETSTRUCT(tup))->typowner != ownerId) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, typeName); /* trouble if caller wanted to force the OID */ if (OidIsValid(newTypeOid) && newTypeOid != HeapTupleHeaderGetOid((tup)->t_data)) elog(ERROR, "cannot assign new OID to existing shell type %u", HeapTupleHeaderGetOid((tup)->t_data)); /* * Okay to update existing shell type tuple */ tup = heap_modify_tuple(tup, RelationGetDescr(pg_type_desc), values, nulls, replaces); simple_heap_update(pg_type_desc, &tup->t_self, tup); typeObjectId = HeapTupleGetOid(tup); rebuildDeps = true; /* get rid of shell type's dependencies */ } else { tup = heap_form_tuple(RelationGetDescr(pg_type_desc), values, nulls); /* Force the OID if requested by caller, else heap_insert does it */ if (OidIsValid(newTypeOid)) HeapTupleSetOid(tup, newTypeOid); else if (Gp_role == GP_ROLE_EXECUTE) elog(ERROR," newtypeOid NULL"); typeObjectId = simple_heap_insert(pg_type_desc, tup); } /* Update indexes */ CatalogUpdateIndexes(pg_type_desc, tup); /* * Create dependencies. We can/must skip this in bootstrap mode. */ if (!IsBootstrapProcessingMode()) GenerateTypeDependencies(typeNamespace, typeObjectId, relationOid, relationKind, ownerId, inputProcedure, outputProcedure, receiveProcedure, sendProcedure, typmodinProcedure, typmodoutProcedure, analyzeProcedure, elementType, isImplicitArray, baseType, (defaultTypeBin ? stringToNode(defaultTypeBin) : NULL), rebuildDeps); /* * finish up with pg_type */ heap_close(pg_type_desc, RowExclusiveLock); /* now pg_type_encoding */ if (DatumGetPointer(typoptions) != NULL) add_type_encoding(typeObjectId, typoptions); return typeObjectId; }
/* * CreateConstraintEntry * Create a constraint table entry. * * Subsidiary records (such as triggers or indexes to implement the * constraint) are *not* created here. But we do make dependency links * from the constraint to the things it depends on. */ Oid CreateConstraintEntry(const char *constraintName, Oid constraintNamespace, char constraintType, bool isDeferrable, bool isDeferred, Oid relId, const int16 *constraintKey, int constraintNKeys, Oid domainId, Oid foreignRelId, const int16 *foreignKey, const Oid *pfEqOp, const Oid *ppEqOp, const Oid *ffEqOp, int foreignNKeys, char foreignUpdateType, char foreignDeleteType, char foreignMatchType, Oid indexRelId, Node *conExpr, const char *conBin, const char *conSrc) { Relation conDesc; Oid conOid; HeapTuple tup; bool nulls[Natts_pg_constraint]; Datum values[Natts_pg_constraint]; ArrayType *conkeyArray; ArrayType *confkeyArray; ArrayType *conpfeqopArray; ArrayType *conppeqopArray; ArrayType *conffeqopArray; NameData cname; int i; ObjectAddress conobject; conDesc = heap_open(ConstraintRelationId, RowExclusiveLock); Assert(constraintName); namestrcpy(&cname, constraintName); /* * Convert C arrays into Postgres arrays. */ if (constraintNKeys > 0) { Datum *conkey; conkey = (Datum *) palloc(constraintNKeys * sizeof(Datum)); for (i = 0; i < constraintNKeys; i++) conkey[i] = Int16GetDatum(constraintKey[i]); conkeyArray = construct_array(conkey, constraintNKeys, INT2OID, 2, true, 's'); } else conkeyArray = NULL; if (foreignNKeys > 0) { Datum *fkdatums; fkdatums = (Datum *) palloc(foreignNKeys * sizeof(Datum)); for (i = 0; i < foreignNKeys; i++) fkdatums[i] = Int16GetDatum(foreignKey[i]); confkeyArray = construct_array(fkdatums, foreignNKeys, INT2OID, 2, true, 's'); for (i = 0; i < foreignNKeys; i++) fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]); conpfeqopArray = construct_array(fkdatums, foreignNKeys, OIDOID, sizeof(Oid), true, 'i'); for (i = 0; i < foreignNKeys; i++) fkdatums[i] = ObjectIdGetDatum(ppEqOp[i]); conppeqopArray = construct_array(fkdatums, foreignNKeys, OIDOID, sizeof(Oid), true, 'i'); for (i = 0; i < foreignNKeys; i++) fkdatums[i] = ObjectIdGetDatum(ffEqOp[i]); conffeqopArray = construct_array(fkdatums, foreignNKeys, OIDOID, sizeof(Oid), true, 'i'); } else { confkeyArray = NULL; conpfeqopArray = NULL; conppeqopArray = NULL; conffeqopArray = NULL; } /* initialize nulls and values */ for (i = 0; i < Natts_pg_constraint; i++) { nulls[i] = false; values[i] = (Datum) 0; } values[Anum_pg_constraint_conname - 1] = NameGetDatum(&cname); values[Anum_pg_constraint_connamespace - 1] = ObjectIdGetDatum(constraintNamespace); values[Anum_pg_constraint_contype - 1] = CharGetDatum(constraintType); values[Anum_pg_constraint_condeferrable - 1] = BoolGetDatum(isDeferrable); values[Anum_pg_constraint_condeferred - 1] = BoolGetDatum(isDeferred); values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(relId); values[Anum_pg_constraint_contypid - 1] = ObjectIdGetDatum(domainId); values[Anum_pg_constraint_confrelid - 1] = ObjectIdGetDatum(foreignRelId); values[Anum_pg_constraint_confupdtype - 1] = CharGetDatum(foreignUpdateType); values[Anum_pg_constraint_confdeltype - 1] = CharGetDatum(foreignDeleteType); values[Anum_pg_constraint_confmatchtype - 1] = CharGetDatum(foreignMatchType); if (conkeyArray) values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray); else nulls[Anum_pg_constraint_conkey - 1] = true; if (confkeyArray) values[Anum_pg_constraint_confkey - 1] = PointerGetDatum(confkeyArray); else nulls[Anum_pg_constraint_confkey - 1] = true; if (conpfeqopArray) values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray); else nulls[Anum_pg_constraint_conpfeqop - 1] = 'n'; if (conppeqopArray) values[Anum_pg_constraint_conppeqop - 1] = PointerGetDatum(conppeqopArray); else nulls[Anum_pg_constraint_conppeqop - 1] = 'n'; if (conffeqopArray) values[Anum_pg_constraint_conffeqop - 1] = PointerGetDatum(conffeqopArray); else nulls[Anum_pg_constraint_conffeqop - 1] = 'n'; /* * initialize the binary form of the check constraint. */ if (conBin) values[Anum_pg_constraint_conbin - 1] = DirectFunctionCall1(textin, CStringGetDatum(conBin)); else nulls[Anum_pg_constraint_conbin - 1] = true; /* * initialize the text form of the check constraint */ if (conSrc) values[Anum_pg_constraint_consrc - 1] = DirectFunctionCall1(textin, CStringGetDatum(conSrc)); else nulls[Anum_pg_constraint_consrc - 1] = true; tup = heap_form_tuple(RelationGetDescr(conDesc), values, nulls); conOid = simple_heap_insert(conDesc, tup); /* update catalog indexes */ CatalogUpdateIndexes(conDesc, tup); conobject.classId = ConstraintRelationId; conobject.objectId = conOid; conobject.objectSubId = 0; heap_close(conDesc, RowExclusiveLock); if (OidIsValid(relId)) { /* * Register auto dependency from constraint to owning relation, or to * specific column(s) if any are mentioned. */ ObjectAddress relobject; relobject.classId = RelationRelationId; relobject.objectId = relId; if (constraintNKeys > 0) { for (i = 0; i < constraintNKeys; i++) { relobject.objectSubId = constraintKey[i]; recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO); } } else { relobject.objectSubId = 0; recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO); } } if (OidIsValid(domainId)) { /* * Register auto dependency from constraint to owning domain */ ObjectAddress domobject; domobject.classId = TypeRelationId; domobject.objectId = domainId; domobject.objectSubId = 0; recordDependencyOn(&conobject, &domobject, DEPENDENCY_AUTO); } if (OidIsValid(foreignRelId)) { /* * Register normal dependency from constraint to foreign relation, or * to specific column(s) if any are mentioned. */ ObjectAddress relobject; relobject.classId = RelationRelationId; relobject.objectId = foreignRelId; if (foreignNKeys > 0) { for (i = 0; i < foreignNKeys; i++) { relobject.objectSubId = foreignKey[i]; recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL); } } else { relobject.objectSubId = 0; recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL); } } if (OidIsValid(indexRelId)) { /* * Register normal dependency on the unique index that supports a * foreign-key constraint. */ ObjectAddress relobject; relobject.classId = RelationRelationId; relobject.objectId = indexRelId; relobject.objectSubId = 0; recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL); } if (foreignNKeys > 0) { /* * Register normal dependencies on the equality operators that support * a foreign-key constraint. If the PK and FK types are the same then * all three operators for a column are the same; otherwise they are * different. */ ObjectAddress oprobject; oprobject.classId = OperatorRelationId; oprobject.objectSubId = 0; for (i = 0; i < foreignNKeys; i++) { oprobject.objectId = pfEqOp[i]; recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL); if (ppEqOp[i] != pfEqOp[i]) { oprobject.objectId = ppEqOp[i]; recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL); } if (ffEqOp[i] != pfEqOp[i]) { oprobject.objectId = ffEqOp[i]; recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL); } } } if (conExpr != NULL) { /* * Register dependencies from constraint to objects mentioned in CHECK * expression. */ recordDependencyOnSingleRelExpr(&conobject, conExpr, relId, DEPENDENCY_NORMAL, DEPENDENCY_NORMAL); } return conOid; }
/* * Map a relation's (tablespace, filenode) to a relation's oid and cache the * result. * * Returns InvalidOid if no relation matching the criteria could be found. */ Oid RelidByRelfilenode(Oid reltablespace, Oid relfilenode) { RelfilenodeMapKey key; RelfilenodeMapEntry *entry; bool found; SysScanDesc scandesc; Relation relation; HeapTuple ntp; ScanKeyData skey[2]; Oid relid; if (RelfilenodeMapHash == NULL) InitializeRelfilenodeMap(); /* pg_class will show 0 when the value is actually MyDatabaseTableSpace */ if (reltablespace == MyDatabaseTableSpace) reltablespace = 0; MemSet(&key, 0, sizeof(key)); key.reltablespace = reltablespace; key.relfilenode = relfilenode; /* * Check cache and return entry if one is found. Even if no target * relation can be found later on we store the negative match and return a * InvalidOid from cache. That's not really necessary for performance * since querying invalid values isn't supposed to be a frequent thing, * but it's basically free. */ entry = hash_search(RelfilenodeMapHash, (void *) &key, HASH_FIND, &found); if (found) return entry->relid; /* ok, no previous cache entry, do it the hard way */ /* initialize empty/negative cache entry before doing the actual lookups */ relid = InvalidOid; if (reltablespace == GLOBALTABLESPACE_OID) { /* * Ok, shared table, check relmapper. */ relid = RelationMapFilenodeToOid(relfilenode, true); } else { /* * Not a shared table, could either be a plain relation or a * non-shared, nailed one, like e.g. pg_class. */ /* check for plain relations by looking in pg_class */ relation = heap_open(RelationRelationId, AccessShareLock); /* copy scankey to local copy, it will be modified during the scan */ memcpy(skey, relfilenode_skey, sizeof(skey)); /* set scan arguments */ skey[0].sk_argument = ObjectIdGetDatum(reltablespace); skey[1].sk_argument = ObjectIdGetDatum(relfilenode); scandesc = systable_beginscan(relation, ClassTblspcRelfilenodeIndexId, true, NULL, 2, skey); found = false; while (HeapTupleIsValid(ntp = systable_getnext(scandesc))) { if (found) elog(ERROR, "unexpected duplicate for tablespace %u, relfilenode %u", reltablespace, relfilenode); found = true; #ifdef USE_ASSERT_CHECKING { bool isnull; Oid check; check = fastgetattr(ntp, Anum_pg_class_reltablespace, RelationGetDescr(relation), &isnull); Assert(!isnull && check == reltablespace); check = fastgetattr(ntp, Anum_pg_class_relfilenode, RelationGetDescr(relation), &isnull); Assert(!isnull && check == relfilenode); } #endif relid = HeapTupleGetOid(ntp); } systable_endscan(scandesc); heap_close(relation, AccessShareLock); /* check for tables that are mapped but not shared */ if (!found) relid = RelationMapFilenodeToOid(relfilenode, false); } /* * Only enter entry into cache now, our opening of pg_class could have * caused cache invalidations to be executed which would have deleted a * new entry if we had entered it above. */ entry = hash_search(RelfilenodeMapHash, (void *) &key, HASH_ENTER, &found); if (found) elog(ERROR, "corrupted hashtable"); entry->relid = relid; return relid; }
static void CatalogCacheInitializeCache(CatCache *cache) { Relation relation; MemoryContext oldcxt; TupleDesc tupdesc; int i; CatalogCacheInitializeCache_DEBUG1; relation = heap_open(cache->cc_reloid, AccessShareLock); /* * switch to the cache context so our allocations do not vanish at the end * of a transaction */ Assert(CacheMemoryContext != NULL); oldcxt = MemoryContextSwitchTo(CacheMemoryContext); /* * copy the relcache's tuple descriptor to permanent cache storage */ tupdesc = CreateTupleDescCopyConstr(RelationGetDescr(relation)); /* * save the relation's name and relisshared flag, too (cc_relname is used * only for debugging purposes) */ cache->cc_relname = pstrdup(RelationGetRelationName(relation)); cache->cc_relisshared = RelationGetForm(relation)->relisshared; /* * return to the caller's memory context and close the rel */ MemoryContextSwitchTo(oldcxt); heap_close(relation, AccessShareLock); CACHE3_elog(DEBUG2, "CatalogCacheInitializeCache: %s, %d keys", cache->cc_relname, cache->cc_nkeys); /* * initialize cache's key information */ for (i = 0; i < cache->cc_nkeys; ++i) { Oid keytype; RegProcedure eqfunc; CatalogCacheInitializeCache_DEBUG2; if (cache->cc_key[i] > 0) keytype = tupdesc->attrs[cache->cc_key[i] - 1]->atttypid; else { if (cache->cc_key[i] != ObjectIdAttributeNumber) elog(FATAL, "only sys attr supported in caches is OID"); keytype = OIDOID; } GetCCHashEqFuncs(keytype, &cache->cc_hashfunc[i], &eqfunc); cache->cc_isname[i] = (keytype == NAMEOID); /* * Do equality-function lookup (we assume this won't need a catalog * lookup for any supported type) */ fmgr_info_cxt(eqfunc, &cache->cc_skey[i].sk_func, CacheMemoryContext); /* Initialize sk_attno suitably for HeapKeyTest() and heap scans */ cache->cc_skey[i].sk_attno = cache->cc_key[i]; /* Fill in sk_strategy as well --- always standard equality */ cache->cc_skey[i].sk_strategy = BTEqualStrategyNumber; cache->cc_skey[i].sk_subtype = InvalidOid; CACHE4_elog(DEBUG2, "CatalogCacheInitializeCache %s %d %p", cache->cc_relname, i, cache); } /* * mark this cache fully initialized */ cache->cc_tupdesc = tupdesc; }
/* * SearchCatCacheList * * Generate a list of all tuples matching a partial key (that is, * a key specifying just the first K of the cache's N key columns). * * The caller must not modify the list object or the pointed-to tuples, * and must call ReleaseCatCacheList() when done with the list. */ CatCList * SearchCatCacheList(CatCache *cache, int nkeys, Datum v1, Datum v2, Datum v3, Datum v4) { ScanKeyData cur_skey[CATCACHE_MAXKEYS]; uint32 lHashValue; Dlelem *elt; CatCList *cl; CatCTup *ct; List *volatile ctlist; ListCell *ctlist_item; int nmembers; bool ordered; HeapTuple ntp; MemoryContext oldcxt; int i; /* * one-time startup overhead for each cache */ if (cache->cc_tupdesc == NULL) CatalogCacheInitializeCache(cache); Assert(nkeys > 0 && nkeys < cache->cc_nkeys); #ifdef CATCACHE_STATS cache->cc_lsearches++; #endif /* * initialize the search key information */ memcpy(cur_skey, cache->cc_skey, sizeof(cur_skey)); cur_skey[0].sk_argument = v1; cur_skey[1].sk_argument = v2; cur_skey[2].sk_argument = v3; cur_skey[3].sk_argument = v4; /* * compute a hash value of the given keys for faster search. We don't * presently divide the CatCList items into buckets, but this still lets * us skip non-matching items quickly most of the time. */ lHashValue = CatalogCacheComputeHashValue(cache, nkeys, cur_skey); /* * scan the items until we find a match or exhaust our list */ for (elt = DLGetHead(&cache->cc_lists); elt; elt = DLGetSucc(elt)) { bool res; cl = (CatCList *) DLE_VAL(elt); if (cl->dead) continue; /* ignore dead entries */ if (cl->hash_value != lHashValue) continue; /* quickly skip entry if wrong hash val */ /* * see if the cached list matches our key. */ if (cl->nkeys != nkeys) continue; HeapKeyTest(&cl->tuple, cache->cc_tupdesc, nkeys, cur_skey, &res); if (!res) continue; /* * We found a matching list. Move the list to the front of the * cache's list-of-lists, to speed subsequent searches. (We do not * move the members to the fronts of their hashbucket lists, however, * since there's no point in that unless they are searched for * individually.) */ DLMoveToFront(&cl->cache_elem); /* Bump the list's refcount and return it */ ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); cl->refcount++; ResourceOwnerRememberCatCacheListRef(CurrentResourceOwner, cl); CACHE2_elog(DEBUG2, "SearchCatCacheList(%s): found list", cache->cc_relname); #ifdef CATCACHE_STATS cache->cc_lhits++; #endif return cl; } /* * List was not found in cache, so we have to build it by reading the * relation. For each matching tuple found in the relation, use an * existing cache entry if possible, else build a new one. * * We have to bump the member refcounts temporarily to ensure they won't * get dropped from the cache while loading other members. We use a PG_TRY * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); ctlist = NIL; PG_TRY(); { Relation relation; SysScanDesc scandesc; relation = heap_open(cache->cc_reloid, AccessShareLock); scandesc = systable_beginscan(relation, cache->cc_indexoid, IndexScanOK(cache, cur_skey), SnapshotNow, nkeys, cur_skey); /* The list will be ordered iff we are doing an index scan */ ordered = (scandesc->irel != NULL); while (HeapTupleIsValid(ntp = systable_getnext(scandesc))) { uint32 hashValue; Index hashIndex; /* * See if there's an entry for this tuple already. */ ct = NULL; hashValue = CatalogCacheComputeTupleHashValue(cache, ntp); hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets); for (elt = DLGetHead(&cache->cc_bucket[hashIndex]); elt; elt = DLGetSucc(elt)) { ct = (CatCTup *) DLE_VAL(elt); if (ct->dead || ct->negative) continue; /* ignore dead and negative entries */ if (ct->hash_value != hashValue) continue; /* quickly skip entry if wrong hash val */ if (!ItemPointerEquals(&(ct->tuple.t_self), &(ntp->t_self))) continue; /* not same tuple */ /* * Found a match, but can't use it if it belongs to another * list already */ if (ct->c_list) continue; break; /* A-OK */ } if (elt == NULL) { /* We didn't find a usable entry, so make a new one */ ct = CatalogCacheCreateEntry(cache, ntp, hashValue, hashIndex, false); } /* Careful here: add entry to ctlist, then bump its refcount */ /* This way leaves state correct if lappend runs out of memory */ ctlist = lappend(ctlist, ct); ct->refcount++; } systable_endscan(scandesc); heap_close(relation, AccessShareLock); /* * Now we can build the CatCList entry. First we need a dummy tuple * containing the key values... */ ntp = build_dummy_tuple(cache, nkeys, cur_skey); oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(sizeof(CatCList) + nmembers * sizeof(CatCTup *)); heap_copytuple_with_tuple(ntp, &cl->tuple); MemoryContextSwitchTo(oldcxt); heap_freetuple(ntp); /* * We are now past the last thing that could trigger an elog before we * have finished building the CatCList and remembering it in the * resource owner. So it's OK to fall out of the PG_TRY, and indeed * we'd better do so before we start marking the members as belonging * to the list. */ } PG_CATCH(); { foreach(ctlist_item, ctlist) { ct = (CatCTup *) lfirst(ctlist_item); Assert(ct->c_list == NULL); Assert(ct->refcount > 0); ct->refcount--; if ( #ifndef CATCACHE_FORCE_RELEASE ct->dead && #endif ct->refcount == 0 && (ct->c_list == NULL || ct->c_list->refcount == 0)) CatCacheRemoveCTup(cache, ct); } PG_RE_THROW(); }
/* * SearchCatCache * * This call searches a system cache for a tuple, opening the relation * if necessary (on the first access to a particular cache). * * The result is NULL if not found, or a pointer to a HeapTuple in * the cache. The caller must not modify the tuple, and must call * ReleaseCatCache() when done with it. * * The search key values should be expressed as Datums of the key columns' * datatype(s). (Pass zeroes for any unused parameters.) As a special * exception, the passed-in key for a NAME column can be just a C string; * the caller need not go to the trouble of converting it to a fully * null-padded NAME. */ HeapTuple SearchCatCache(CatCache *cache, Datum v1, Datum v2, Datum v3, Datum v4) { ScanKeyData cur_skey[CATCACHE_MAXKEYS]; uint32 hashValue; Index hashIndex; Dlelem *elt; CatCTup *ct; Relation relation; SysScanDesc scandesc; HeapTuple ntp; /* * one-time startup overhead for each cache */ if (cache->cc_tupdesc == NULL) CatalogCacheInitializeCache(cache); #ifdef CATCACHE_STATS cache->cc_searches++; #endif /* * initialize the search key information */ memcpy(cur_skey, cache->cc_skey, sizeof(cur_skey)); cur_skey[0].sk_argument = v1; cur_skey[1].sk_argument = v2; cur_skey[2].sk_argument = v3; cur_skey[3].sk_argument = v4; /* * find the hash bucket in which to look for the tuple */ hashValue = CatalogCacheComputeHashValue(cache, cache->cc_nkeys, cur_skey); hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets); /* * scan the hash bucket until we find a match or exhaust our tuples */ for (elt = DLGetHead(&cache->cc_bucket[hashIndex]); elt; elt = DLGetSucc(elt)) { bool res; ct = (CatCTup *) DLE_VAL(elt); if (ct->dead) continue; /* ignore dead entries */ if (ct->hash_value != hashValue) continue; /* quickly skip entry if wrong hash val */ /* * see if the cached tuple matches our key. */ HeapKeyTest(&ct->tuple, cache->cc_tupdesc, cache->cc_nkeys, cur_skey, &res); if (!res) continue; /* * We found a match in the cache. Move it to the front of the list * for its hashbucket, in order to speed subsequent searches. (The * most frequently accessed elements in any hashbucket will tend to be * near the front of the hashbucket's list.) */ DLMoveToFront(&ct->cache_elem); /* * If it's a positive entry, bump its refcount and return it. If it's * negative, we can report failure to the caller. */ if (!ct->negative) { ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner); ct->refcount++; ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple); CACHE3_elog(DEBUG2, "SearchCatCache(%s): found in bucket %d", cache->cc_relname, hashIndex); #ifdef CATCACHE_STATS cache->cc_hits++; #endif return &ct->tuple; } else { CACHE3_elog(DEBUG2, "SearchCatCache(%s): found neg entry in bucket %d", cache->cc_relname, hashIndex); #ifdef CATCACHE_STATS cache->cc_neg_hits++; #endif return NULL; } } /* * Tuple was not found in cache, so we have to try to retrieve it directly * from the relation. If found, we will add it to the cache; if not * found, we will add a negative cache entry instead. * * NOTE: it is possible for recursive cache lookups to occur while reading * the relation --- for example, due to shared-cache-inval messages being * processed during heap_open(). This is OK. It's even possible for one * of those lookups to find and enter the very same tuple we are trying to * fetch here. If that happens, we will enter a second copy of the tuple * into the cache. The first copy will never be referenced again, and * will eventually age out of the cache, so there's no functional problem. * This case is rare enough that it's not worth expending extra cycles to * detect. */ relation = heap_open(cache->cc_reloid, AccessShareLock); scandesc = systable_beginscan(relation, cache->cc_indexoid, IndexScanOK(cache, cur_skey), SnapshotNow, cache->cc_nkeys, cur_skey); ct = NULL; while (HeapTupleIsValid(ntp = systable_getnext(scandesc))) { ct = CatalogCacheCreateEntry(cache, ntp, hashValue, hashIndex, false); if (scandesc->inmemonlyscan) { /* Make sure tuple is removed during ReleaseCatCache */ ct->dead = true; } /* immediately set the refcount to 1 */ ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner); ct->refcount++; ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple); break; /* assume only one match */ } systable_endscan(scandesc); heap_close(relation, AccessShareLock); if (ct == NULL) { return NULL; } CACHE4_elog(DEBUG2, "SearchCatCache(%s): Contains %d/%d tuples", cache->cc_relname, cache->cc_ntup, CCacheHdr->ch_ntup); CACHE3_elog(DEBUG2, "SearchCatCache(%s): put in bucket %d", cache->cc_relname, hashIndex); #ifdef CATCACHE_STATS cache->cc_newloads++; #endif return &ct->tuple; }
/* * Test whether an object exists. */ static bool object_exists(ObjectAddress address) { int cache = -1; Oid indexoid = InvalidOid; Relation rel; ScanKeyData skey[1]; SysScanDesc sd; bool found; /* Sub-objects require special treatment. */ if (address.objectSubId != 0) { HeapTuple atttup; /* Currently, attributes are the only sub-objects. */ Assert(address.classId == RelationRelationId); atttup = SearchSysCache2(ATTNUM, ObjectIdGetDatum(address.objectId), Int16GetDatum(address.objectSubId)); if (!HeapTupleIsValid(atttup)) found = false; else { found = ((Form_pg_attribute) GETSTRUCT(atttup))->attisdropped; ReleaseSysCache(atttup); } return found; } /* * For object types that have a relevant syscache, we use it; for * everything else, we'll have to do an index-scan. This switch * sets either the cache to be used for the syscache lookup, or the * index to be used for the index scan. */ switch (address.classId) { case RelationRelationId: cache = RELOID; break; case RewriteRelationId: indexoid = RewriteOidIndexId; break; case TriggerRelationId: indexoid = TriggerOidIndexId; break; case ConstraintRelationId: cache = CONSTROID; break; case DatabaseRelationId: cache = DATABASEOID; break; case TableSpaceRelationId: cache = TABLESPACEOID; break; case AuthIdRelationId: cache = AUTHOID; break; case NamespaceRelationId: cache = NAMESPACEOID; break; case LanguageRelationId: cache = LANGOID; break; case TypeRelationId: cache = TYPEOID; break; case ProcedureRelationId: cache = PROCOID; break; case OperatorRelationId: cache = OPEROID; break; case ConversionRelationId: cache = CONVOID; break; case OperatorClassRelationId: cache = CLAOID; break; case OperatorFamilyRelationId: cache = OPFAMILYOID; break; case LargeObjectRelationId: /* * Weird backward compatibility hack: ObjectAddress notation uses * LargeObjectRelationId for large objects, but since PostgreSQL * 9.0, the relevant catalog is actually * LargeObjectMetadataRelationId. */ address.classId = LargeObjectMetadataRelationId; indexoid = LargeObjectMetadataOidIndexId; break; case CastRelationId: indexoid = CastOidIndexId; break; case TSParserRelationId: cache = TSPARSEROID; break; case TSDictionaryRelationId: cache = TSDICTOID; break; case TSTemplateRelationId: cache = TSTEMPLATEOID; break; case TSConfigRelationId: cache = TSCONFIGOID; break; default: elog(ERROR, "unrecognized classid: %u", address.classId); } /* Found a syscache? */ if (cache != -1) return SearchSysCacheExists1(cache, ObjectIdGetDatum(address.objectId)); /* No syscache, so examine the table directly. */ Assert(OidIsValid(indexoid)); ScanKeyInit(&skey[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(address.objectId)); rel = heap_open(address.classId, AccessShareLock); sd = systable_beginscan(rel, indexoid, true, SnapshotNow, 1, skey); found = HeapTupleIsValid(systable_getnext(sd)); systable_endscan(sd); heap_close(rel, AccessShareLock); return found; }
/* ---------------------------------------------------------------- * TypeShellMake * * This procedure inserts a "shell" tuple into the pg_type relation. * The type tuple inserted has valid but dummy values, and its * "typisdefined" field is false indicating it's not really defined. * * This is used so that a tuple exists in the catalogs. The I/O * functions for the type will link to this tuple. When the full * CREATE TYPE command is issued, the bogus values will be replaced * with correct ones, and "typisdefined" will be set to true. * ---------------------------------------------------------------- */ Oid TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId, Oid shelloid) { Relation pg_type_desc; TupleDesc tupDesc; int i; HeapTuple tup; Datum values[Natts_pg_type]; bool nulls[Natts_pg_type]; Oid typoid; NameData name; Assert(PointerIsValid(typeName)); /* * open pg_type */ pg_type_desc = heap_open(TypeRelationId, RowExclusiveLock); tupDesc = pg_type_desc->rd_att; /* * initialize our *nulls and *values arrays */ for (i = 0; i < Natts_pg_type; ++i) { nulls[i] = false; values[i] = (Datum) 0; /* redundant, but safe */ } /* * initialize *values with the type name and dummy values * * The representational details are the same as int4 ... it doesn't really * matter what they are so long as they are consistent. Also note that we * give it typtype = TYPTYPE_PSEUDO as extra insurance that it won't be * mistaken for a usable type. */ i = 0; namestrcpy(&name, typeName); values[i++] = NameGetDatum(&name); /* typname */ values[i++] = ObjectIdGetDatum(typeNamespace); /* typnamespace */ values[i++] = ObjectIdGetDatum(ownerId); /* typowner */ values[i++] = Int16GetDatum(sizeof(int4)); /* typlen */ values[i++] = BoolGetDatum(true); /* typbyval */ values[i++] = CharGetDatum(TYPTYPE_PSEUDO); /* typtype */ values[i++] = BoolGetDatum(false); /* typisdefined */ values[i++] = CharGetDatum(DEFAULT_TYPDELIM); /* typdelim */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typrelid */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typelem */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typarray */ values[i++] = ObjectIdGetDatum(F_SHELL_IN); /* typinput */ values[i++] = ObjectIdGetDatum(F_SHELL_OUT); /* typoutput */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typreceive */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typsend */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typmodin */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typmodout */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typanalyze */ values[i++] = CharGetDatum('i'); /* typalign */ values[i++] = CharGetDatum('p'); /* typstorage */ values[i++] = BoolGetDatum(false); /* typnotnull */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typbasetype */ values[i++] = Int32GetDatum(-1); /* typtypmod */ values[i++] = Int32GetDatum(0); /* typndims */ nulls[i++] = true; /* typdefaultbin */ nulls[i++] = true; /* typdefault */ /* * create a new type tuple */ tup = heap_form_tuple(tupDesc, values, nulls); /* * MPP: If we are on the QEs, we need to use the same Oid as the QD used */ if (shelloid != InvalidOid) HeapTupleSetOid(tup, shelloid); /* * insert the tuple in the relation and get the tuple's oid. */ typoid = simple_heap_insert(pg_type_desc, tup); CatalogUpdateIndexes(pg_type_desc, tup); /* * Create dependencies. We can/must skip this in bootstrap mode. */ if (!IsBootstrapProcessingMode()) GenerateTypeDependencies(typeNamespace, typoid, InvalidOid, 0, ownerId, F_SHELL_IN, F_SHELL_OUT, InvalidOid, InvalidOid, InvalidOid, InvalidOid, InvalidOid, InvalidOid, false, InvalidOid, NULL, false); /* * clean up and return the type-oid */ heap_freetuple(tup); heap_close(pg_type_desc, RowExclusiveLock); return typoid; }
/* * sepgsql_relation_post_create * * The post creation hook of relation/attribute */ void sepgsql_relation_post_create(Oid relOid) { Relation rel; ScanKeyData skey; SysScanDesc sscan; HeapTuple tuple; Form_pg_class classForm; ObjectAddress object; uint16 tclass; char *scontext; /* subject */ char *tcontext; /* schema */ char *rcontext; /* relation */ char *ccontext; /* column */ char *nsp_name; StringInfoData audit_name; /* * Fetch catalog record of the new relation. Because pg_class entry is not * visible right now, we need to scan the catalog using SnapshotSelf. */ rel = heap_open(RelationRelationId, AccessShareLock); ScanKeyInit(&skey, ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relOid)); sscan = systable_beginscan(rel, ClassOidIndexId, true, SnapshotSelf, 1, &skey); tuple = systable_getnext(sscan); if (!HeapTupleIsValid(tuple)) elog(ERROR, "catalog lookup failed for relation %u", relOid); classForm = (Form_pg_class) GETSTRUCT(tuple); /* ignore indexes on toast tables */ if (classForm->relkind == RELKIND_INDEX && classForm->relnamespace == PG_TOAST_NAMESPACE) goto out; /* * check db_schema:{add_name} permission of the namespace */ object.classId = NamespaceRelationId; object.objectId = classForm->relnamespace; object.objectSubId = 0; sepgsql_avc_check_perms(&object, SEPG_CLASS_DB_SCHEMA, SEPG_DB_SCHEMA__ADD_NAME, getObjectIdentity(&object), true); switch (classForm->relkind) { case RELKIND_RELATION: tclass = SEPG_CLASS_DB_TABLE; break; case RELKIND_SEQUENCE: tclass = SEPG_CLASS_DB_SEQUENCE; break; case RELKIND_VIEW: tclass = SEPG_CLASS_DB_VIEW; break; case RELKIND_INDEX: /* deal with indexes specially; no need for tclass */ sepgsql_index_modify(relOid); goto out; default: /* ignore other relkinds */ goto out; } /* * Compute a default security label when we create a new relation object * under the specified namespace. */ scontext = sepgsql_get_client_label(); tcontext = sepgsql_get_label(NamespaceRelationId, classForm->relnamespace, 0); rcontext = sepgsql_compute_create(scontext, tcontext, tclass, NameStr(classForm->relname)); /* * check db_xxx:{create} permission */ nsp_name = get_namespace_name(classForm->relnamespace); initStringInfo(&audit_name); appendStringInfo(&audit_name, "%s.%s", quote_identifier(nsp_name), quote_identifier(NameStr(classForm->relname))); sepgsql_avc_check_perms_label(rcontext, tclass, SEPG_DB_DATABASE__CREATE, audit_name.data, true); /* * Assign the default security label on the new relation */ object.classId = RelationRelationId; object.objectId = relOid; object.objectSubId = 0; SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, rcontext); /* * We also assigns a default security label on columns of the new regular * tables. */ if (classForm->relkind == RELKIND_RELATION) { Relation arel; ScanKeyData akey; SysScanDesc ascan; HeapTuple atup; Form_pg_attribute attForm; arel = heap_open(AttributeRelationId, AccessShareLock); ScanKeyInit(&akey, Anum_pg_attribute_attrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relOid)); ascan = systable_beginscan(arel, AttributeRelidNumIndexId, true, SnapshotSelf, 1, &akey); while (HeapTupleIsValid(atup = systable_getnext(ascan))) { attForm = (Form_pg_attribute) GETSTRUCT(atup); resetStringInfo(&audit_name); appendStringInfo(&audit_name, "%s.%s.%s", quote_identifier(nsp_name), quote_identifier(NameStr(classForm->relname)), quote_identifier(NameStr(attForm->attname))); ccontext = sepgsql_compute_create(scontext, rcontext, SEPG_CLASS_DB_COLUMN, NameStr(attForm->attname)); /* * check db_column:{create} permission */ sepgsql_avc_check_perms_label(ccontext, SEPG_CLASS_DB_COLUMN, SEPG_DB_COLUMN__CREATE, audit_name.data, true); object.classId = RelationRelationId; object.objectId = relOid; object.objectSubId = attForm->attnum; SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ccontext); pfree(ccontext); } systable_endscan(ascan); heap_close(arel, AccessShareLock); } pfree(rcontext); out: systable_endscan(sscan); heap_close(rel, AccessShareLock); }
/* * create_toast_table --- internal workhorse * * rel is already opened and locked * toastOid and toastIndexOid are normally InvalidOid, but during * bootstrap they can be nonzero to specify hand-assigned OIDs */ static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptions, LOCKMODE lockmode, bool check) { Oid relOid = RelationGetRelid(rel); HeapTuple reltup; TupleDesc tupdesc; bool shared_relation; bool mapped_relation; Relation toast_rel; Relation class_rel; Oid toast_relid; Oid toast_typid = InvalidOid; Oid namespaceid; char toast_relname[NAMEDATALEN]; char toast_idxname[NAMEDATALEN]; IndexInfo *indexInfo; Oid collationObjectId[2]; Oid classObjectId[2]; int16 coloptions[2]; ObjectAddress baseobject, toastobject; /* * Toast table is shared if and only if its parent is. * * We cannot allow toasting a shared relation after initdb (because * there's no way to mark it toasted in other databases' pg_class). */ shared_relation = rel->rd_rel->relisshared; if (shared_relation && !IsBootstrapProcessingMode()) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("shared tables cannot be toasted after initdb"))); /* It's mapped if and only if its parent is, too */ mapped_relation = RelationIsMapped(rel); /* * Is it already toasted? */ if (rel->rd_rel->reltoastrelid != InvalidOid) return false; /* * Check to see whether the table actually needs a TOAST table. */ if (!IsBinaryUpgrade) { /* Normal mode, normal check */ if (!needs_toast_table(rel)) return false; } else { /* * In binary-upgrade mode, create a TOAST table if and only if * pg_upgrade told us to (ie, a TOAST table OID has been provided). * * This indicates that the old cluster had a TOAST table for the * current table. We must create a TOAST table to receive the old * TOAST file, even if the table seems not to need one. * * Contrariwise, if the old cluster did not have a TOAST table, we * should be able to get along without one even if the new version's * needs_toast_table rules suggest we should have one. There is a lot * of daylight between where we will create a TOAST table and where * one is really necessary to avoid failures, so small cross-version * differences in the when-to-create heuristic shouldn't be a problem. * If we tried to create a TOAST table anyway, we would have the * problem that it might take up an OID that will conflict with some * old-cluster table we haven't seen yet. */ if (!OidIsValid(binary_upgrade_next_toast_pg_class_oid) || !OidIsValid(binary_upgrade_next_toast_pg_type_oid)) return false; } /* * If requested check lockmode is sufficient. This is a cross check in * case of errors or conflicting decisions in earlier code. */ if (check && lockmode != AccessExclusiveLock) elog(ERROR, "AccessExclusiveLock required to add toast table."); /* * Create the toast table and its index */ snprintf(toast_relname, sizeof(toast_relname), "pg_toast_%u", relOid); snprintf(toast_idxname, sizeof(toast_idxname), "pg_toast_%u_index", relOid); /* this is pretty painful... need a tuple descriptor */ tupdesc = CreateTemplateTupleDesc(3, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "chunk_id", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "chunk_seq", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "chunk_data", BYTEAOID, -1, 0); /* * Ensure that the toast table doesn't itself get toasted, or we'll be * toast :-(. This is essential for chunk_data because type bytea is * toastable; hit the other two just to be sure. */ TupleDescAttr(tupdesc, 0)->attstorage = 'p'; TupleDescAttr(tupdesc, 1)->attstorage = 'p'; TupleDescAttr(tupdesc, 2)->attstorage = 'p'; /* * Toast tables for regular relations go in pg_toast; those for temp * relations go into the per-backend temp-toast-table namespace. */ if (isTempOrTempToastNamespace(rel->rd_rel->relnamespace)) namespaceid = GetTempToastNamespace(); else namespaceid = PG_TOAST_NAMESPACE; /* * Use binary-upgrade override for pg_type.oid, if supplied. We might be * in the post-schema-restore phase where we are doing ALTER TABLE to * create TOAST tables that didn't exist in the old cluster. */ if (IsBinaryUpgrade && OidIsValid(binary_upgrade_next_toast_pg_type_oid)) { toast_typid = binary_upgrade_next_toast_pg_type_oid; binary_upgrade_next_toast_pg_type_oid = InvalidOid; } toast_relid = heap_create_with_catalog(toast_relname, namespaceid, rel->rd_rel->reltablespace, toastOid, toast_typid, InvalidOid, rel->rd_rel->relowner, tupdesc, NIL, RELKIND_TOASTVALUE, rel->rd_rel->relpersistence, shared_relation, mapped_relation, true, 0, ONCOMMIT_NOOP, reloptions, false, true, true, InvalidOid, NULL); Assert(toast_relid != InvalidOid); /* make the toast relation visible, else heap_open will fail */ CommandCounterIncrement(); /* ShareLock is not really needed here, but take it anyway */ toast_rel = heap_open(toast_relid, ShareLock); /* * Create unique index on chunk_id, chunk_seq. * * NOTE: the normal TOAST access routines could actually function with a * single-column index on chunk_id only. However, the slice access * routines use both columns for faster access to an individual chunk. In * addition, we want it to be unique as a check against the possibility of * duplicate TOAST chunk OIDs. The index might also be a little more * efficient this way, since btree isn't all that happy with large numbers * of equal keys. */ indexInfo = makeNode(IndexInfo); indexInfo->ii_NumIndexAttrs = 2; indexInfo->ii_KeyAttrNumbers[0] = 1; indexInfo->ii_KeyAttrNumbers[1] = 2; indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; indexInfo->ii_Unique = true; indexInfo->ii_ReadyForInserts = true; indexInfo->ii_Concurrent = false; indexInfo->ii_BrokenHotChain = false; indexInfo->ii_ParallelWorkers = 0; indexInfo->ii_Am = BTREE_AM_OID; indexInfo->ii_AmCache = NULL; indexInfo->ii_Context = CurrentMemoryContext; collationObjectId[0] = InvalidOid; collationObjectId[1] = InvalidOid; classObjectId[0] = OID_BTREE_OPS_OID; classObjectId[1] = INT4_BTREE_OPS_OID; coloptions[0] = 0; coloptions[1] = 0; index_create(toast_rel, toast_idxname, toastIndexOid, InvalidOid, InvalidOid, InvalidOid, indexInfo, list_make2("chunk_id", "chunk_seq"), BTREE_AM_OID, rel->rd_rel->reltablespace, collationObjectId, classObjectId, coloptions, (Datum) 0, INDEX_CREATE_IS_PRIMARY, 0, true, true, NULL); heap_close(toast_rel, NoLock); /* * Store the toast table's OID in the parent relation's pg_class row */ class_rel = heap_open(RelationRelationId, RowExclusiveLock); reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid)); if (!HeapTupleIsValid(reltup)) elog(ERROR, "cache lookup failed for relation %u", relOid); ((Form_pg_class) GETSTRUCT(reltup))->reltoastrelid = toast_relid; if (!IsBootstrapProcessingMode()) { /* normal case, use a transactional update */ CatalogTupleUpdate(class_rel, &reltup->t_self, reltup); } else { /* While bootstrapping, we cannot UPDATE, so overwrite in-place */ heap_inplace_update(class_rel, reltup); } heap_freetuple(reltup); heap_close(class_rel, RowExclusiveLock); /* * Register dependency from the toast table to the master, so that the * toast table will be deleted if the master is. Skip this in bootstrap * mode. */ if (!IsBootstrapProcessingMode()) { baseobject.classId = RelationRelationId; baseobject.objectId = relOid; baseobject.objectSubId = 0; toastobject.classId = RelationRelationId; toastobject.objectId = toast_relid; toastobject.objectSubId = 0; recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL); } /* * Make changes visible */ CommandCounterIncrement(); return true; }
/* * sepgsql_relation_setattr * * It checks privileges to set attribute of the supplied relation */ void sepgsql_relation_setattr(Oid relOid) { Relation rel; ScanKeyData skey; SysScanDesc sscan; HeapTuple oldtup; HeapTuple newtup; Form_pg_class oldform; Form_pg_class newform; ObjectAddress object; char *audit_name; uint16_t tclass; switch (get_rel_relkind(relOid)) { case RELKIND_RELATION: tclass = SEPG_CLASS_DB_TABLE; break; case RELKIND_SEQUENCE: tclass = SEPG_CLASS_DB_SEQUENCE; break; case RELKIND_VIEW: tclass = SEPG_CLASS_DB_VIEW; break; case RELKIND_INDEX: /* deal with indexes specially */ sepgsql_index_modify(relOid); return; default: /* other relkinds don't need additional work */ return; } /* * Fetch newer catalog */ rel = heap_open(RelationRelationId, AccessShareLock); ScanKeyInit(&skey, ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relOid)); sscan = systable_beginscan(rel, ClassOidIndexId, true, SnapshotSelf, 1, &skey); newtup = systable_getnext(sscan); if (!HeapTupleIsValid(newtup)) elog(ERROR, "catalog lookup failed for relation %u", relOid); newform = (Form_pg_class) GETSTRUCT(newtup); /* * Fetch older catalog */ oldtup = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid)); if (!HeapTupleIsValid(oldtup)) elog(ERROR, "cache lookup failed for relation %u", relOid); oldform = (Form_pg_class) GETSTRUCT(oldtup); /* * Does this ALTER command takes operation to namespace? */ if (newform->relnamespace != oldform->relnamespace) { sepgsql_schema_remove_name(oldform->relnamespace); sepgsql_schema_add_name(newform->relnamespace); } if (strcmp(NameStr(newform->relname), NameStr(oldform->relname)) != 0) sepgsql_schema_rename(oldform->relnamespace); /* * XXX - In the future version, db_tuple:{use} of system catalog entry * shall be checked, if tablespace configuration is changed. */ /* * check db_xxx:{setattr} permission */ object.classId = RelationRelationId; object.objectId = relOid; object.objectSubId = 0; audit_name = getObjectIdentity(&object); sepgsql_avc_check_perms(&object, tclass, SEPG_DB_TABLE__SETATTR, audit_name, true); pfree(audit_name); ReleaseSysCache(oldtup); systable_endscan(sscan); heap_close(rel, AccessShareLock); }
/* * OperatorShellMake * Make a "shell" entry for a not-yet-existing operator. */ static Oid OperatorShellMake(const char *operatorName, Oid operatorNamespace, Oid leftTypeId, Oid rightTypeId) { Relation pg_operator_desc; Oid operatorObjectId; int i; HeapTuple tup; Datum values[Natts_pg_operator]; bool nulls[Natts_pg_operator]; NameData oname; TupleDesc tupDesc; /* * validate operator name */ if (!validOperatorName(operatorName)) ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("\"%s\" is not a valid operator name", operatorName))); /* * initialize our *nulls and *values arrays */ for (i = 0; i < Natts_pg_operator; ++i) { nulls[i] = false; values[i] = (Datum) NULL; /* redundant, but safe */ } /* * initialize values[] with the operator name and input data types. Note * that oprcode is set to InvalidOid, indicating it's a shell. */ namestrcpy(&oname, operatorName); values[Anum_pg_operator_oprname - 1] = NameGetDatum(&oname); values[Anum_pg_operator_oprnamespace - 1] = ObjectIdGetDatum(operatorNamespace); values[Anum_pg_operator_oprowner - 1] = ObjectIdGetDatum(GetUserId()); values[Anum_pg_operator_oprkind - 1] = CharGetDatum(leftTypeId ? (rightTypeId ? 'b' : 'r') : 'l'); values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(false); values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(false); values[Anum_pg_operator_oprleft - 1] = ObjectIdGetDatum(leftTypeId); values[Anum_pg_operator_oprright - 1] = ObjectIdGetDatum(rightTypeId); values[Anum_pg_operator_oprresult - 1] = ObjectIdGetDatum(InvalidOid); values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(InvalidOid); values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(InvalidOid); values[Anum_pg_operator_oprcode - 1] = ObjectIdGetDatum(InvalidOid); values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(InvalidOid); values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(InvalidOid); /* * open pg_operator */ pg_operator_desc = heap_open(OperatorRelationId, RowExclusiveLock); tupDesc = pg_operator_desc->rd_att; /* * create a new operator tuple */ tup = heap_form_tuple(tupDesc, values, nulls); /* * insert our "shell" operator tuple */ operatorObjectId = simple_heap_insert(pg_operator_desc, tup); CatalogUpdateIndexes(pg_operator_desc, tup); /* Add dependencies for the entry */ makeOperatorDependencies(tup); heap_freetuple(tup); /* Post creation hook for new shell operator */ InvokeObjectAccessHook(OAT_POST_CREATE, OperatorRelationId, operatorObjectId, 0); /* * Make sure the tuple is visible for subsequent lookups/updates. */ CommandCounterIncrement(); /* * close the operator relation and return the oid. */ heap_close(pg_operator_desc, RowExclusiveLock); return operatorObjectId; }
/* * ExecRefreshMatView -- execute a REFRESH MATERIALIZED VIEW command * * This refreshes the materialized view by creating a new table and swapping * the relfilenodes of the new table and the old materialized view, so the OID * of the original materialized view is preserved. Thus we do not lose GRANT * nor references to this materialized view. * * If WITH NO DATA was specified, this is effectively like a TRUNCATE; * otherwise it is like a TRUNCATE followed by an INSERT using the SELECT * statement associated with the materialized view. The statement node's * skipData field shows whether the clause was used. * * Indexes are rebuilt too, via REINDEX. Since we are effectively bulk-loading * the new heap, it's better to create the indexes afterwards than to fill them * incrementally while we load. * * The matview's "populated" state is changed based on whether the contents * reflect the result set of the materialized view's query. */ ObjectAddress ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString, ParamListInfo params, char *completionTag) { Oid matviewOid; Relation matviewRel; RewriteRule *rule; List *actions; Query *dataQuery; Oid tableSpace; Oid relowner; Oid OIDNewHeap; DestReceiver *dest; bool concurrent; LOCKMODE lockmode; char relpersistence; Oid save_userid; int save_sec_context; int save_nestlevel; ObjectAddress address; /* Determine strength of lock needed. */ concurrent = stmt->concurrent; lockmode = concurrent ? ExclusiveLock : AccessExclusiveLock; /* * Get a lock until end of transaction. */ matviewOid = RangeVarGetRelidExtended(stmt->relation, lockmode, false, false, RangeVarCallbackOwnsTable, NULL); matviewRel = heap_open(matviewOid, NoLock); /* Make sure it is a materialized view. */ if (matviewRel->rd_rel->relkind != RELKIND_MATVIEW) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("\"%s\" is not a materialized view", RelationGetRelationName(matviewRel)))); /* Check that CONCURRENTLY is not specified if not populated. */ if (concurrent && !RelationIsPopulated(matviewRel)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("CONCURRENTLY cannot be used when the materialized view is not populated"))); /* Check that conflicting options have not been specified. */ if (concurrent && stmt->skipData) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("CONCURRENTLY and WITH NO DATA options cannot be used together"))); /* We don't allow an oid column for a materialized view. */ Assert(!matviewRel->rd_rel->relhasoids); /* * Check that everything is correct for a refresh. Problems at this point * are internal errors, so elog is sufficient. */ if (matviewRel->rd_rel->relhasrules == false || matviewRel->rd_rules->numLocks < 1) elog(ERROR, "materialized view \"%s\" is missing rewrite information", RelationGetRelationName(matviewRel)); if (matviewRel->rd_rules->numLocks > 1) elog(ERROR, "materialized view \"%s\" has too many rules", RelationGetRelationName(matviewRel)); rule = matviewRel->rd_rules->rules[0]; if (rule->event != CMD_SELECT || !(rule->isInstead)) elog(ERROR, "the rule for materialized view \"%s\" is not a SELECT INSTEAD OF rule", RelationGetRelationName(matviewRel)); actions = rule->actions; if (list_length(actions) != 1) elog(ERROR, "the rule for materialized view \"%s\" is not a single action", RelationGetRelationName(matviewRel)); /* * The stored query was rewritten at the time of the MV definition, but * has not been scribbled on by the planner. */ dataQuery = (Query *) linitial(actions); Assert(IsA(dataQuery, Query)); /* * Check for active uses of the relation in the current transaction, such * as open scans. * * NB: We count on this to protect us against problems with refreshing the * data using HEAP_INSERT_FROZEN. */ CheckTableNotInUse(matviewRel, "REFRESH MATERIALIZED VIEW"); /* * Tentatively mark the matview as populated or not (this will roll back * if we fail later). */ SetMatViewPopulatedState(matviewRel, !stmt->skipData); relowner = matviewRel->rd_rel->relowner; /* * Switch to the owner's userid, so that any functions are run as that * user. Also arrange to make GUC variable changes local to this command. * Don't lock it down too tight to create a temporary table just yet. We * will switch modes when we are about to execute user code. */ GetUserIdAndSecContext(&save_userid, &save_sec_context); SetUserIdAndSecContext(relowner, save_sec_context | SECURITY_LOCAL_USERID_CHANGE); save_nestlevel = NewGUCNestLevel(); /* Concurrent refresh builds new data in temp tablespace, and does diff. */ if (concurrent) { tableSpace = GetDefaultTablespace(RELPERSISTENCE_TEMP); relpersistence = RELPERSISTENCE_TEMP; } else { tableSpace = matviewRel->rd_rel->reltablespace; relpersistence = matviewRel->rd_rel->relpersistence; } /* * Create the transient table that will receive the regenerated data. Lock * it against access by any other process until commit (by which time it * will be gone). */ OIDNewHeap = make_new_heap(matviewOid, tableSpace, relpersistence, ExclusiveLock); LockRelationOid(OIDNewHeap, AccessExclusiveLock); dest = CreateTransientRelDestReceiver(OIDNewHeap); /* * Now lock down security-restricted operations. */ SetUserIdAndSecContext(relowner, save_sec_context | SECURITY_RESTRICTED_OPERATION); /* Generate the data, if wanted. */ if (!stmt->skipData) refresh_matview_datafill(dest, dataQuery, queryString); heap_close(matviewRel, NoLock); /* Make the matview match the newly generated data. */ if (concurrent) { int old_depth = matview_maintenance_depth; PG_TRY(); { refresh_by_match_merge(matviewOid, OIDNewHeap, relowner, save_sec_context); } PG_CATCH(); { matview_maintenance_depth = old_depth; PG_RE_THROW(); } PG_END_TRY(); Assert(matview_maintenance_depth == old_depth); } else refresh_by_heap_swap(matviewOid, OIDNewHeap, relpersistence); /* Roll back any GUC changes */ AtEOXact_GUC(false, save_nestlevel); /* Restore userid and security context */ SetUserIdAndSecContext(save_userid, save_sec_context); ObjectAddressSet(address, RelationRelationId, matviewOid); return address; }
/* * OperatorCreate * * "X" indicates an optional argument (i.e. one that can be NULL or 0) * operatorName name for new operator * operatorNamespace namespace for new operator * leftTypeId X left type ID * rightTypeId X right type ID * procedureId procedure ID for operator * commutatorName X commutator operator * negatorName X negator operator * restrictionId X restriction selectivity procedure ID * joinId X join selectivity procedure ID * canMerge merge join can be used with this operator * canHash hash join can be used with this operator * * The caller should have validated properties and permissions for the * objects passed as OID references. We must handle the commutator and * negator operator references specially, however, since those need not * exist beforehand. * * This routine gets complicated because it allows the user to * specify operators that do not exist. For example, if operator * "op" is being defined, the negator operator "negop" and the * commutator "commop" can also be defined without specifying * any information other than their names. Since in order to * add "op" to the PG_OPERATOR catalog, all the Oid's for these * operators must be placed in the fields of "op", a forward * declaration is done on the commutator and negator operators. * This is called creating a shell, and its main effect is to * create a tuple in the PG_OPERATOR catalog with minimal * information about the operator (just its name and types). * Forward declaration is used only for this purpose, it is * not available to the user as it is for type definition. */ void OperatorCreate(const char *operatorName, Oid operatorNamespace, Oid leftTypeId, Oid rightTypeId, Oid procedureId, List *commutatorName, List *negatorName, Oid restrictionId, Oid joinId, bool canMerge, bool canHash) { Relation pg_operator_desc; HeapTuple tup; bool nulls[Natts_pg_operator]; bool replaces[Natts_pg_operator]; Datum values[Natts_pg_operator]; Oid operatorObjectId; bool operatorAlreadyDefined; Oid operResultType; Oid commutatorId, negatorId; bool selfCommutator = false; NameData oname; TupleDesc tupDesc; int i; /* * Sanity checks */ if (!validOperatorName(operatorName)) ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("\"%s\" is not a valid operator name", operatorName))); if (!(OidIsValid(leftTypeId) && OidIsValid(rightTypeId))) { /* If it's not a binary op, these things mustn't be set: */ if (commutatorName) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only binary operators can have commutators"))); if (OidIsValid(joinId)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only binary operators can have join selectivity"))); if (canMerge) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only binary operators can merge join"))); if (canHash) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only binary operators can hash"))); } operResultType = get_func_rettype(procedureId); if (operResultType != BOOLOID) { /* If it's not a boolean op, these things mustn't be set: */ if (negatorName) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only boolean operators can have negators"))); if (OidIsValid(restrictionId)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only boolean operators can have restriction selectivity"))); if (OidIsValid(joinId)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only boolean operators can have join selectivity"))); if (canMerge) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only boolean operators can merge join"))); if (canHash) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only boolean operators can hash"))); } operatorObjectId = OperatorGet(operatorName, operatorNamespace, leftTypeId, rightTypeId, &operatorAlreadyDefined); if (operatorAlreadyDefined) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_FUNCTION), errmsg("operator %s already exists", operatorName))); /* * At this point, if operatorObjectId is not InvalidOid then we are * filling in a previously-created shell. Insist that the user own any * such shell. */ if (OidIsValid(operatorObjectId) && !pg_oper_ownercheck(operatorObjectId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, operatorName); /* * Set up the other operators. If they do not currently exist, create * shells in order to get ObjectId's. */ if (commutatorName) { /* commutator has reversed arg types */ commutatorId = get_other_operator(commutatorName, rightTypeId, leftTypeId, operatorName, operatorNamespace, leftTypeId, rightTypeId, true); /* Permission check: must own other operator */ if (OidIsValid(commutatorId) && !pg_oper_ownercheck(commutatorId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, NameListToString(commutatorName)); /* * self-linkage to this operator; will fix below. Note that only * self-linkage for commutation makes sense. */ if (!OidIsValid(commutatorId)) selfCommutator = true; } else commutatorId = InvalidOid; if (negatorName) { /* negator has same arg types */ negatorId = get_other_operator(negatorName, leftTypeId, rightTypeId, operatorName, operatorNamespace, leftTypeId, rightTypeId, false); /* Permission check: must own other operator */ if (OidIsValid(negatorId) && !pg_oper_ownercheck(negatorId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, NameListToString(negatorName)); } else negatorId = InvalidOid; /* * set up values in the operator tuple */ for (i = 0; i < Natts_pg_operator; ++i) { values[i] = (Datum) NULL; replaces[i] = true; nulls[i] = false; } namestrcpy(&oname, operatorName); values[Anum_pg_operator_oprname - 1] = NameGetDatum(&oname); values[Anum_pg_operator_oprnamespace - 1] = ObjectIdGetDatum(operatorNamespace); values[Anum_pg_operator_oprowner - 1] = ObjectIdGetDatum(GetUserId()); values[Anum_pg_operator_oprkind - 1] = CharGetDatum(leftTypeId ? (rightTypeId ? 'b' : 'r') : 'l'); values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(canMerge); values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(canHash); values[Anum_pg_operator_oprleft - 1] = ObjectIdGetDatum(leftTypeId); values[Anum_pg_operator_oprright - 1] = ObjectIdGetDatum(rightTypeId); values[Anum_pg_operator_oprresult - 1] = ObjectIdGetDatum(operResultType); values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(commutatorId); values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(negatorId); values[Anum_pg_operator_oprcode - 1] = ObjectIdGetDatum(procedureId); values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(restrictionId); values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(joinId); pg_operator_desc = heap_open(OperatorRelationId, RowExclusiveLock); /* * If we are replacing an operator shell, update; else insert */ if (operatorObjectId) { tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(operatorObjectId)); if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for operator %u", operatorObjectId); tup = heap_modify_tuple(tup, RelationGetDescr(pg_operator_desc), values, nulls, replaces); simple_heap_update(pg_operator_desc, &tup->t_self, tup); } else { tupDesc = pg_operator_desc->rd_att; tup = heap_form_tuple(tupDesc, values, nulls); operatorObjectId = simple_heap_insert(pg_operator_desc, tup); } /* Must update the indexes in either case */ CatalogUpdateIndexes(pg_operator_desc, tup); /* Add dependencies for the entry */ makeOperatorDependencies(tup); /* Post creation hook for new operator */ InvokeObjectAccessHook(OAT_POST_CREATE, OperatorRelationId, operatorObjectId, 0); heap_close(pg_operator_desc, RowExclusiveLock); /* * If a commutator and/or negator link is provided, update the other * operator(s) to point at this one, if they don't already have a link. * This supports an alternative style of operator definition wherein the * user first defines one operator without giving negator or commutator, * then defines the other operator of the pair with the proper commutator * or negator attribute. That style doesn't require creation of a shell, * and it's the only style that worked right before Postgres version 6.5. * This code also takes care of the situation where the new operator is * its own commutator. */ if (selfCommutator) commutatorId = operatorObjectId; if (OidIsValid(commutatorId) || OidIsValid(negatorId)) OperatorUpd(operatorObjectId, commutatorId, negatorId); }
/* * Guts of language creation. */ static Oid create_proc_lang(const char *languageName, bool replace, Oid languageOwner, Oid handlerOid, Oid inlineOid, Oid valOid, bool trusted) { Relation rel; TupleDesc tupDesc; Datum values[Natts_pg_language]; bool nulls[Natts_pg_language]; bool replaces[Natts_pg_language]; NameData langname; HeapTuple oldtup; HeapTuple tup; bool is_update; ObjectAddress myself, referenced; rel = heap_open(LanguageRelationId, RowExclusiveLock); tupDesc = RelationGetDescr(rel); /* Prepare data to be inserted */ memset(values, 0, sizeof(values)); memset(nulls, false, sizeof(nulls)); memset(replaces, true, sizeof(replaces)); namestrcpy(&langname, languageName); values[Anum_pg_language_lanname - 1] = NameGetDatum(&langname); values[Anum_pg_language_lanowner - 1] = ObjectIdGetDatum(languageOwner); values[Anum_pg_language_lanispl - 1] = BoolGetDatum(true); values[Anum_pg_language_lanpltrusted - 1] = BoolGetDatum(trusted); values[Anum_pg_language_lanplcallfoid - 1] = ObjectIdGetDatum(handlerOid); values[Anum_pg_language_laninline - 1] = ObjectIdGetDatum(inlineOid); values[Anum_pg_language_lanvalidator - 1] = ObjectIdGetDatum(valOid); nulls[Anum_pg_language_lanacl - 1] = true; /* Check for pre-existing definition */ oldtup = SearchSysCache1(LANGNAME, PointerGetDatum(languageName)); if (HeapTupleIsValid(oldtup)) { /* There is one; okay to replace it? */ if (!replace) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("language \"%s\" already exists", languageName))); if (!pg_language_ownercheck(HeapTupleGetOid(oldtup), languageOwner)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE, languageName); /* * Do not change existing ownership or permissions. Note * dependency-update code below has to agree with this decision. */ replaces[Anum_pg_language_lanowner - 1] = false; replaces[Anum_pg_language_lanacl - 1] = false; /* Okay, do it... */ tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces); simple_heap_update(rel, &tup->t_self, tup); ReleaseSysCache(oldtup); is_update = true; } else { /* Creating a new language */ tup = heap_form_tuple(tupDesc, values, nulls); simple_heap_insert(rel, tup); is_update = false; } /* Need to update indexes for either the insert or update case */ CatalogUpdateIndexes(rel, tup); /* * Create dependencies for the new language. If we are updating an * existing language, first delete any existing pg_depend entries. * (However, since we are not changing ownership or permissions, the * shared dependencies do *not* need to change, and we leave them alone.) */ myself.classId = LanguageRelationId; myself.objectId = HeapTupleGetOid(tup); myself.objectSubId = 0; if (is_update) deleteDependencyRecordsFor(myself.classId, myself.objectId, true); /* dependency on owner of language */ if (!is_update) recordDependencyOnOwner(myself.classId, myself.objectId, languageOwner); /* dependency on extension */ recordDependencyOnCurrentExtension(&myself, is_update); /* dependency on the PL handler function */ referenced.classId = ProcedureRelationId; referenced.objectId = handlerOid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* dependency on the inline handler function, if any */ if (OidIsValid(inlineOid)) { referenced.classId = ProcedureRelationId; referenced.objectId = inlineOid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* dependency on the validator function, if any */ if (OidIsValid(valOid)) { referenced.classId = ProcedureRelationId; referenced.objectId = valOid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Post creation hook for new procedural language */ InvokeObjectPostCreateHook(LanguageRelationId, myself.objectId, 0); heap_close(rel, RowExclusiveLock); return myself.objectId; }
/* * OperatorUpd * * For a given operator, look up its negator and commutator operators. * If they are defined, but their negator and commutator fields * (respectively) are empty, then use the new operator for neg or comm. * This solves a problem for users who need to insert two new operators * which are the negator or commutator of each other. */ static void OperatorUpd(Oid baseId, Oid commId, Oid negId) { int i; Relation pg_operator_desc; HeapTuple tup; bool nulls[Natts_pg_operator]; bool replaces[Natts_pg_operator]; Datum values[Natts_pg_operator]; for (i = 0; i < Natts_pg_operator; ++i) { values[i] = (Datum) 0; replaces[i] = false; nulls[i] = false; } /* * check and update the commutator & negator, if necessary * * We need a CommandCounterIncrement here in case of a self-commutator * operator: we'll need to update the tuple that we just inserted. */ CommandCounterIncrement(); pg_operator_desc = heap_open(OperatorRelationId, RowExclusiveLock); tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(commId)); /* * if the commutator and negator are the same operator, do one update. XXX * this is probably useless code --- I doubt it ever makes sense for * commutator and negator to be the same thing... */ if (commId == negId) { if (HeapTupleIsValid(tup)) { Form_pg_operator t = (Form_pg_operator) GETSTRUCT(tup); if (!OidIsValid(t->oprcom) || !OidIsValid(t->oprnegate)) { if (!OidIsValid(t->oprnegate)) { values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(baseId); replaces[Anum_pg_operator_oprnegate - 1] = true; } if (!OidIsValid(t->oprcom)) { values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(baseId); replaces[Anum_pg_operator_oprcom - 1] = true; } tup = heap_modify_tuple(tup, RelationGetDescr(pg_operator_desc), values, nulls, replaces); simple_heap_update(pg_operator_desc, &tup->t_self, tup); CatalogUpdateIndexes(pg_operator_desc, tup); } } heap_close(pg_operator_desc, RowExclusiveLock); return; } /* if commutator and negator are different, do two updates */ if (HeapTupleIsValid(tup) && !(OidIsValid(((Form_pg_operator) GETSTRUCT(tup))->oprcom))) { values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(baseId); replaces[Anum_pg_operator_oprcom - 1] = true; tup = heap_modify_tuple(tup, RelationGetDescr(pg_operator_desc), values, nulls, replaces); simple_heap_update(pg_operator_desc, &tup->t_self, tup); CatalogUpdateIndexes(pg_operator_desc, tup); values[Anum_pg_operator_oprcom - 1] = (Datum) NULL; replaces[Anum_pg_operator_oprcom - 1] = false; } /* check and update the negator, if necessary */ tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(negId)); if (HeapTupleIsValid(tup) && !(OidIsValid(((Form_pg_operator) GETSTRUCT(tup))->oprnegate))) { values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(baseId); replaces[Anum_pg_operator_oprnegate - 1] = true; tup = heap_modify_tuple(tup, RelationGetDescr(pg_operator_desc), values, nulls, replaces); simple_heap_update(pg_operator_desc, &tup->t_self, tup); CatalogUpdateIndexes(pg_operator_desc, tup); } heap_close(pg_operator_desc, RowExclusiveLock); }
/* * regtypein - converts "typename" to type OID * * We also accept a numeric OID, for symmetry with the output routine. * * '-' signifies unknown (OID 0). In all other cases, the input must * match an existing pg_type entry. * * In bootstrap mode the name must just equal some existing name in pg_type. * In normal mode the type name can be specified using the full type syntax * recognized by the parser; for example, DOUBLE PRECISION and INTEGER[] will * work and be translated to the correct type names. (We ignore any typmod * info generated by the parser, however.) */ Datum regtypein(PG_FUNCTION_ARGS) { char *typ_name_or_oid = PG_GETARG_CSTRING(0); Oid result = InvalidOid; int32 typmod; /* '-' ? */ if (strcmp(typ_name_or_oid, "-") == 0) PG_RETURN_OID(InvalidOid); /* Numeric OID? */ if (typ_name_or_oid[0] >= '0' && typ_name_or_oid[0] <= '9' && strspn(typ_name_or_oid, "0123456789") == strlen(typ_name_or_oid)) { result = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(typ_name_or_oid))); PG_RETURN_OID(result); } /* Else it's a type name, possibly schema-qualified or decorated */ /* * In bootstrap mode we assume the given name is not schema-qualified, and * just search pg_type for a match. This is needed for initializing other * system catalogs (pg_namespace may not exist yet, and certainly there * are no schemas other than pg_catalog). */ if (IsBootstrapProcessingMode()) { Relation hdesc; ScanKeyData skey[1]; SysScanDesc sysscan; HeapTuple tuple; ScanKeyInit(&skey[0], Anum_pg_type_typname, BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(typ_name_or_oid)); hdesc = heap_open(TypeRelationId, AccessShareLock); sysscan = systable_beginscan(hdesc, TypeNameNspIndexId, true, NULL, 1, skey); if (HeapTupleIsValid(tuple = systable_getnext(sysscan))) result = HeapTupleGetOid(tuple); else ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("type \"%s\" does not exist", typ_name_or_oid))); /* We assume there can be only one match */ systable_endscan(sysscan); heap_close(hdesc, AccessShareLock); PG_RETURN_OID(result); } /* * Normal case: invoke the full parser to deal with special cases such as * array syntax. */ parseTypeString(typ_name_or_oid, &result, &typmod, false); PG_RETURN_OID(result); }
/* * Adjust dependency record(s) to point to a different object of the same type * * classId/objectId specify the referencing object. * refClassId/oldRefObjectId specify the old referenced object. * newRefObjectId is the new referenced object (must be of class refClassId). * * Note the lack of objsubid parameters. If there are subobject references * they will all be readjusted. * * Returns the number of records updated. */ long changeDependencyFor(Oid classId, Oid objectId, Oid refClassId, Oid oldRefObjectId, Oid newRefObjectId) { long count = 0; Relation depRel; ScanKeyData key[2]; SysScanDesc scan; HeapTuple tup; ObjectAddress objAddr; bool newIsPinned; depRel = heap_open(DependRelationId, RowExclusiveLock); /* * If oldRefObjectId is pinned, there won't be any dependency entries on * it --- we can't cope in that case. (This isn't really worth expending * code to fix, in current usage; it just means you can't rename stuff out * of pg_catalog, which would likely be a bad move anyway.) */ objAddr.classId = refClassId; objAddr.objectId = oldRefObjectId; objAddr.objectSubId = 0; if (isObjectPinned(&objAddr, depRel)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot remove dependency on %s because it is a system object", getObjectDescription(&objAddr)))); /* * We can handle adding a dependency on something pinned, though, since * that just means deleting the dependency entry. */ objAddr.objectId = newRefObjectId; newIsPinned = isObjectPinned(&objAddr, depRel); /* Now search for dependency records */ ScanKeyInit(&key[0], Anum_pg_depend_classid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(classId)); ScanKeyInit(&key[1], Anum_pg_depend_objid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(objectId)); scan = systable_beginscan(depRel, DependDependerIndexId, true, NULL, 2, key); while (HeapTupleIsValid((tup = systable_getnext(scan)))) { Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup); if (depform->refclassid == refClassId && depform->refobjid == oldRefObjectId) { if (newIsPinned) simple_heap_delete(depRel, &tup->t_self); else { /* make a modifiable copy */ tup = heap_copytuple(tup); depform = (Form_pg_depend) GETSTRUCT(tup); depform->refobjid = newRefObjectId; simple_heap_update(depRel, &tup->t_self, tup); CatalogUpdateIndexes(depRel, tup); heap_freetuple(tup); } count++; } } systable_endscan(scan); heap_close(depRel, RowExclusiveLock); return count; }
/* * regclassin - converts "classname" to class OID * * We also accept a numeric OID, for symmetry with the output routine. * * '-' signifies unknown (OID 0). In all other cases, the input must * match an existing pg_class entry. */ Datum regclassin(PG_FUNCTION_ARGS) { char *class_name_or_oid = PG_GETARG_CSTRING(0); Oid result = InvalidOid; List *names; /* '-' ? */ if (strcmp(class_name_or_oid, "-") == 0) PG_RETURN_OID(InvalidOid); /* Numeric OID? */ if (class_name_or_oid[0] >= '0' && class_name_or_oid[0] <= '9' && strspn(class_name_or_oid, "0123456789") == strlen(class_name_or_oid)) { result = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(class_name_or_oid))); PG_RETURN_OID(result); } /* Else it's a name, possibly schema-qualified */ /* * In bootstrap mode we assume the given name is not schema-qualified, and * just search pg_class for a match. This is needed for initializing * other system catalogs (pg_namespace may not exist yet, and certainly * there are no schemas other than pg_catalog). */ if (IsBootstrapProcessingMode()) { Relation hdesc; ScanKeyData skey[1]; SysScanDesc sysscan; HeapTuple tuple; ScanKeyInit(&skey[0], Anum_pg_class_relname, BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(class_name_or_oid)); hdesc = heap_open(RelationRelationId, AccessShareLock); sysscan = systable_beginscan(hdesc, ClassNameNspIndexId, true, NULL, 1, skey); if (HeapTupleIsValid(tuple = systable_getnext(sysscan))) result = HeapTupleGetOid(tuple); else ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), errmsg("relation \"%s\" does not exist", class_name_or_oid))); /* We assume there can be only one match */ systable_endscan(sysscan); heap_close(hdesc, AccessShareLock); PG_RETURN_OID(result); } /* * Normal case: parse the name into components and see if it matches any * pg_class entries in the current search path. */ names = stringToQualifiedNameList(class_name_or_oid); /* We might not even have permissions on this relation; don't lock it. */ result = RangeVarGetRelid(makeRangeVarFromNameList(names), NoLock, false); PG_RETURN_OID(result); }
/* * Record multiple dependencies (of the same kind) for a single dependent * object. This has a little less overhead than recording each separately. */ void recordMultipleDependencies(const ObjectAddress *depender, const ObjectAddress *referenced, int nreferenced, DependencyType behavior) { Relation dependDesc; CatalogIndexState indstate; HeapTuple tup; int i; bool nulls[Natts_pg_depend]; Datum values[Natts_pg_depend]; if (nreferenced <= 0) return; /* nothing to do */ /* * During bootstrap, do nothing since pg_depend may not exist yet. initdb * will fill in appropriate pg_depend entries after bootstrap. */ if (IsBootstrapProcessingMode()) return; dependDesc = heap_open(DependRelationId, RowExclusiveLock); /* Don't open indexes unless we need to make an update */ indstate = NULL; memset(nulls, false, sizeof(nulls)); for (i = 0; i < nreferenced; i++, referenced++) { /* * If the referenced object is pinned by the system, there's no real * need to record dependencies on it. This saves lots of space in * pg_depend, so it's worth the time taken to check. */ if (!isObjectPinned(referenced, dependDesc)) { /* * Record the Dependency. Note we don't bother to check for * duplicate dependencies; there's no harm in them. */ values[Anum_pg_depend_classid - 1] = ObjectIdGetDatum(depender->classId); values[Anum_pg_depend_objid - 1] = ObjectIdGetDatum(depender->objectId); values[Anum_pg_depend_objsubid - 1] = Int32GetDatum(depender->objectSubId); values[Anum_pg_depend_refclassid - 1] = ObjectIdGetDatum(referenced->classId); values[Anum_pg_depend_refobjid - 1] = ObjectIdGetDatum(referenced->objectId); values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(referenced->objectSubId); values[Anum_pg_depend_deptype - 1] = CharGetDatum((char) behavior); tup = heap_form_tuple(dependDesc->rd_att, values, nulls); simple_heap_insert(dependDesc, tup); /* keep indexes current */ if (indstate == NULL) indstate = CatalogOpenIndexes(dependDesc); CatalogIndexInsert(indstate, tup); heap_freetuple(tup); } } if (indstate != NULL) CatalogCloseIndexes(indstate); heap_close(dependDesc, RowExclusiveLock); }
Datum gp_persistent_build_all(PG_FUNCTION_ARGS) { bool mirrored = PG_GETARG_BOOL(0); Relation pg_filespace; Relation pg_tablespace; Relation pg_database; HeapScanDesc scan; HeapTuple tuple; Datum *d; bool *null; // UNDONE: Verify we are in some sort of single-user mode. // clear hash PersistentFilespace_Reset(); //clear dispatched file space info DispatchedFilespace_SeqSearch_Term(); /* * Re-build filespaces. */ d = (Datum *) palloc(sizeof(Datum) * Natts_pg_filespace); null = (bool *) palloc(sizeof(bool) * Natts_pg_filespace); pg_filespace = heap_open( FileSpaceRelationId, AccessShareLock); scan = heap_beginscan(pg_filespace, SnapshotNow, 0, NULL); while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { Oid filespaceOid; if (!HeapTupleIsValid(tuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("filespace tuple is invalid"))); filespaceOid = HeapTupleGetOid(tuple); heap_deform_tuple(tuple, pg_filespace->rd_att, d, null); if (filespaceOid == SYSTEMFILESPACE_OID) { if (Debug_persistent_print) elog(Persistent_DebugPrintLevel(), "gp_persistent_build_all: skip pg_system filespaceOid %u", filespaceOid); continue; } if (Debug_persistent_print) elog(Persistent_DebugPrintLevel(), "gp_persistent_build_all: filespaceOid %u", filespaceOid); /* * Special call here to scan the persistent meta-data structures so we are open for * business and then we can add information. */ PersistentFileSysObj_BuildInitScan(); PersistentFilespace_AddCreated( filespaceOid, /* flushToXLog */ false); } heap_endscan(scan); heap_close(pg_filespace, AccessShareLock); pfree(d); pfree(null); /* * Re-build tablespaces. */ d = (Datum *) palloc(sizeof(Datum) * Natts_pg_tablespace); null = (bool *) palloc(sizeof(bool) * Natts_pg_tablespace); pg_tablespace = heap_open( TableSpaceRelationId, AccessShareLock); scan = heap_beginscan(pg_tablespace, SnapshotNow, 0, NULL); while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { Oid tablespaceOid; if (!HeapTupleIsValid(tuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("tablespace tuple is invalid"))); tablespaceOid = HeapTupleGetOid(tuple); heap_deform_tuple(tuple, pg_tablespace->rd_att, d, null); if (tablespaceOid == DEFAULTTABLESPACE_OID || tablespaceOid == GLOBALTABLESPACE_OID) { if (Debug_persistent_print) elog(Persistent_DebugPrintLevel(), "gp_persistent_build_all: skip pg_default and pg_global tablespaceOid %u", tablespaceOid); continue; } if (Debug_persistent_print) elog(Persistent_DebugPrintLevel(), "gp_persistent_build_all: tablespaceOid %u filespaceOid %u", tablespaceOid, DatumGetInt32(d[Anum_pg_tablespace_spcfsoid - 1])); /* * Special call here to scan the persistent meta-data structures so we are open for * business and then we can add information. */ PersistentFileSysObj_BuildInitScan(); PersistentTablespace_AddCreated( DatumGetInt32(d[Anum_pg_tablespace_spcfsoid - 1]), tablespaceOid, /* flushToXLog */ false); } heap_endscan(scan); heap_close(pg_tablespace, AccessShareLock); pfree(d); pfree(null); /* * Re-build databases. * Do template1 first since it will also populate the shared-object persistent objects. */ PersistentBuild_BuildDb( TemplateDbOid, mirrored); if (Debug_persistent_print) elog(Persistent_DebugPrintLevel(), "gp_persistent_build_all: template1 complete"); /* * Now, the remaining databases. */ pg_database = heap_open( DatabaseRelationId, AccessShareLock); scan = heap_beginscan(pg_database, SnapshotNow, 0, NULL); while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { Oid dbOid; dbOid = HeapTupleGetOid(tuple); if (dbOid == TemplateDbOid) { if (Debug_persistent_print) elog(Persistent_DebugPrintLevel(), "gp_persistent_build_all: skip template1"); continue; } if (Debug_persistent_print) elog(Persistent_DebugPrintLevel(), "gp_persistent_build_all: dbOid %u", dbOid); PersistentBuild_BuildDb( dbOid, mirrored); } heap_endscan(scan); heap_close(pg_database, AccessShareLock); PersistentStore_FlushXLog(); PG_RETURN_INT32(1); }
/* * Change tablespace owner */ void AlterTableSpaceOwner(const char *name, Oid newOwnerId) { Relation rel; ScanKeyData entry[1]; HeapScanDesc scandesc; Form_pg_tablespace spcForm; HeapTuple tup; /* Search pg_tablespace */ rel = heap_open(TableSpaceRelationId, RowExclusiveLock); ScanKeyInit(&entry[0], Anum_pg_tablespace_spcname, BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(name)); scandesc = heap_beginscan(rel, SnapshotNow, 1, entry); tup = heap_getnext(scandesc, ForwardScanDirection); if (!HeapTupleIsValid(tup)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("tablespace \"%s\" does not exist", name))); spcForm = (Form_pg_tablespace) GETSTRUCT(tup); /* * If the new owner is the same as the existing owner, consider the * command to have succeeded. This is for dump restoration purposes. */ if (spcForm->spcowner != newOwnerId) { Datum repl_val[Natts_pg_tablespace]; char repl_null[Natts_pg_tablespace]; char repl_repl[Natts_pg_tablespace]; Acl *newAcl; Datum aclDatum; bool isNull; HeapTuple newtuple; /* Otherwise, must be owner of the existing object */ if (!pg_tablespace_ownercheck(HeapTupleGetOid(tup), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE, name); /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); /* * Normally we would also check for create permissions here, but there * are none for tablespaces so we follow what rename tablespace does * and omit the create permissions check. * * NOTE: Only superusers may create tablespaces to begin with and so * initially only a superuser would be able to change its ownership * anyway. */ memset(repl_null, ' ', sizeof(repl_null)); memset(repl_repl, ' ', sizeof(repl_repl)); repl_repl[Anum_pg_tablespace_spcowner - 1] = 'r'; repl_val[Anum_pg_tablespace_spcowner - 1] = ObjectIdGetDatum(newOwnerId); /* * Determine the modified ACL for the new owner. This is only * necessary when the ACL is non-null. */ aclDatum = heap_getattr(tup, Anum_pg_tablespace_spcacl, RelationGetDescr(rel), &isNull); if (!isNull) { newAcl = aclnewowner(DatumGetAclP(aclDatum), spcForm->spcowner, newOwnerId); repl_repl[Anum_pg_tablespace_spcacl - 1] = 'r'; repl_val[Anum_pg_tablespace_spcacl - 1] = PointerGetDatum(newAcl); } newtuple = heap_modifytuple(tup, RelationGetDescr(rel), repl_val, repl_null, repl_repl); simple_heap_update(rel, &newtuple->t_self, newtuple); CatalogUpdateIndexes(rel, newtuple); heap_freetuple(newtuple); /* Update owner dependency reference */ changeDependencyOnOwner(TableSpaceRelationId, HeapTupleGetOid(tup), newOwnerId); } heap_endscan(scandesc); heap_close(rel, NoLock); }