/* * inv_create -- create a new large object * * Arguments: * lobjId - OID to use for new large object, or InvalidOid to pick one * * Returns: * OID of new object * * If lobjId is not InvalidOid, then an error occurs if the OID is already * in use. */ Oid inv_create(Oid lobjId) { Oid lobjId_new; /* * Create a new largeobject with empty data pages */ lobjId_new = LargeObjectCreate(lobjId); /* * dependency on the owner of largeobject * * The reason why we use LargeObjectRelationId instead of * LargeObjectMetadataRelationId here is to provide backward compatibility * to the applications which utilize a knowledge about internal layout of * system catalogs. OID of pg_largeobject_metadata and loid of * pg_largeobject are same value, so there are no actual differences here. */ recordDependencyOnOwner(LargeObjectRelationId, lobjId_new, GetUserId()); /* Post creation hook for new large object */ InvokeObjectAccessHook(OAT_POST_CREATE, LargeObjectRelationId, lobjId_new, 0); /* * Advance command counter to make new tuple visible to later operations. */ CommandCounterIncrement(); return lobjId_new; }
/* ---------------- * NamespaceCreate * --------------- */ Oid NamespaceCreate(const char *nspName, Oid ownerId, Oid forceOid) { Relation nspdesc; HeapTuple tup; Oid nspoid; bool nulls[Natts_pg_namespace]; Datum values[Natts_pg_namespace]; NameData nname; TupleDesc tupDesc; int i; /* sanity checks */ if (!nspName) elog(ERROR, "no namespace name supplied"); /* make sure there is no existing namespace of same name */ if (SearchSysCacheExists(NAMESPACENAME, PointerGetDatum(nspName), 0, 0, 0)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_SCHEMA), errmsg("schema \"%s\" already exists", nspName))); /* initialize nulls and values */ for (i = 0; i < Natts_pg_namespace; i++) { nulls[i] = false; values[i] = (Datum) 0; } namestrcpy(&nname, nspName); values[Anum_pg_namespace_nspname - 1] = NameGetDatum(&nname); values[Anum_pg_namespace_nspowner - 1] = ObjectIdGetDatum(ownerId); nulls[Anum_pg_namespace_nspacl - 1] = true; nspdesc = heap_open(NamespaceRelationId, RowExclusiveLock); tupDesc = nspdesc->rd_att; tup = heap_form_tuple(tupDesc, values, nulls); if (forceOid != InvalidOid) HeapTupleSetOid(tup, forceOid); /* override heap_insert's OID * selection */ nspoid = simple_heap_insert(nspdesc, tup); Assert(OidIsValid(nspoid)); CatalogUpdateIndexes(nspdesc, tup); heap_close(nspdesc, RowExclusiveLock); /* Record dependency on owner */ recordDependencyOnOwner(NamespaceRelationId, nspoid, ownerId); return nspoid; }
/* * Create dependencies for an operator (either a freshly inserted * complete operator, a new shell operator, a just-updated shell, * or an operator that's being modified by ALTER OPERATOR). * * NB: the OidIsValid tests in this routine are necessary, in case * the given operator is a shell. */ ObjectAddress makeOperatorDependencies(HeapTuple tuple, bool isUpdate) { Form_pg_operator oper = (Form_pg_operator) GETSTRUCT(tuple); ObjectAddress myself, referenced; myself.classId = OperatorRelationId; myself.objectId = HeapTupleGetOid(tuple); myself.objectSubId = 0; /* * If we are updating the operator, delete any existing entries, except * for extension membership which should remain the same. */ if (isUpdate) { deleteDependencyRecordsFor(myself.classId, myself.objectId, true); deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0); } /* Dependency on namespace */ if (OidIsValid(oper->oprnamespace)) { referenced.classId = NamespaceRelationId; referenced.objectId = oper->oprnamespace; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Dependency on left type */ if (OidIsValid(oper->oprleft)) { referenced.classId = TypeRelationId; referenced.objectId = oper->oprleft; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Dependency on right type */ if (OidIsValid(oper->oprright)) { referenced.classId = TypeRelationId; referenced.objectId = oper->oprright; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Dependency on result type */ if (OidIsValid(oper->oprresult)) { referenced.classId = TypeRelationId; referenced.objectId = oper->oprresult; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* * NOTE: we do not consider the operator to depend on the associated * operators oprcom and oprnegate. We would not want to delete this * operator if those go away, but only reset the link fields; which is not * a function that the dependency code can presently handle. (Something * could perhaps be done with objectSubId though.) For now, it's okay to * let those links dangle if a referenced operator is removed. */ /* Dependency on implementation function */ if (OidIsValid(oper->oprcode)) { referenced.classId = ProcedureRelationId; referenced.objectId = oper->oprcode; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Dependency on restriction selectivity function */ if (OidIsValid(oper->oprrest)) { referenced.classId = ProcedureRelationId; referenced.objectId = oper->oprrest; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Dependency on join selectivity function */ if (OidIsValid(oper->oprjoin)) { referenced.classId = ProcedureRelationId; referenced.objectId = oper->oprjoin; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Dependency on owner */ recordDependencyOnOwner(OperatorRelationId, HeapTupleGetOid(tuple), oper->oprowner); /* Dependency on extension */ recordDependencyOnCurrentExtension(&myself, true); return myself; }
/* * Create a table space * * Only superusers can create a tablespace. This seems a reasonable restriction * since we're determining the system layout and, anyway, we probably have * root if we're doing this kind of activity */ Oid CreateTableSpace(CreateTableSpaceStmt *stmt) { #ifdef HAVE_SYMLINK Relation rel; Datum values[Natts_pg_tablespace]; bool nulls[Natts_pg_tablespace]; HeapTuple tuple; Oid tablespaceoid; char *location; Oid ownerId; Datum newOptions; /* Must be super user */ if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to create tablespace \"%s\"", stmt->tablespacename), errhint("Must be superuser to create a tablespace."))); /* However, the eventual owner of the tablespace need not be */ if (stmt->owner) ownerId = get_rolespec_oid(stmt->owner, false); else ownerId = GetUserId(); /* Unix-ify the offered path, and strip any trailing slashes */ location = pstrdup(stmt->location); canonicalize_path(location); /* disallow quotes, else CREATE DATABASE would be at risk */ if (strchr(location, '\'')) ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("tablespace location cannot contain single quotes"))); /* * Allowing relative paths seems risky * * this also helps us ensure that location is not empty or whitespace */ if (!is_absolute_path(location)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("tablespace location must be an absolute path"))); /* * Check that location isn't too long. Remember that we're going to append * 'PG_XXX/<dboid>/<relid>_<fork>.<nnn>'. FYI, we never actually * reference the whole path here, but mkdir() uses the first two parts. */ if (strlen(location) + 1 + strlen(TABLESPACE_VERSION_DIRECTORY) + 1 + OIDCHARS + 1 + OIDCHARS + 1 + FORKNAMECHARS + 1 + OIDCHARS > MAXPGPATH) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("tablespace location \"%s\" is too long", location))); /* Warn if the tablespace is in the data directory. */ if (path_is_prefix_of_path(DataDir, location)) ereport(WARNING, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("tablespace location should not be inside the data directory"))); /* * Disallow creation of tablespaces named "pg_xxx"; we reserve this * namespace for system purposes. */ if (!allowSystemTableMods && IsReservedName(stmt->tablespacename)) ereport(ERROR, (errcode(ERRCODE_RESERVED_NAME), errmsg("unacceptable tablespace name \"%s\"", stmt->tablespacename), errdetail("The prefix \"pg_\" is reserved for system tablespaces."))); /* * Check that there is no other tablespace by this name. (The unique * index would catch this anyway, but might as well give a friendlier * message.) */ if (OidIsValid(get_tablespace_oid(stmt->tablespacename, true))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("tablespace \"%s\" already exists", stmt->tablespacename))); /* * Insert tuple into pg_tablespace. The purpose of doing this first is to * lock the proposed tablename against other would-be creators. The * insertion will roll back if we find problems below. */ rel = heap_open(TableSpaceRelationId, RowExclusiveLock); MemSet(nulls, false, sizeof(nulls)); values[Anum_pg_tablespace_spcname - 1] = DirectFunctionCall1(namein, CStringGetDatum(stmt->tablespacename)); values[Anum_pg_tablespace_spcowner - 1] = ObjectIdGetDatum(ownerId); nulls[Anum_pg_tablespace_spcacl - 1] = true; /* Generate new proposed spcoptions (text array) */ newOptions = transformRelOptions((Datum) 0, stmt->options, NULL, NULL, false, false); (void) tablespace_reloptions(newOptions, true); if (newOptions != (Datum) 0) values[Anum_pg_tablespace_spcoptions - 1] = newOptions; else nulls[Anum_pg_tablespace_spcoptions - 1] = true; tuple = heap_form_tuple(rel->rd_att, values, nulls); tablespaceoid = simple_heap_insert(rel, tuple); CatalogUpdateIndexes(rel, tuple); heap_freetuple(tuple); /* Record dependency on owner */ recordDependencyOnOwner(TableSpaceRelationId, tablespaceoid, ownerId); /* Post creation hook for new tablespace */ InvokeObjectPostCreateHook(TableSpaceRelationId, tablespaceoid, 0); create_tablespace_directories(location, tablespaceoid); /* Record the filesystem change in XLOG */ { xl_tblspc_create_rec xlrec; xlrec.ts_id = tablespaceoid; XLogBeginInsert(); XLogRegisterData((char *) &xlrec, offsetof(xl_tblspc_create_rec, ts_path)); XLogRegisterData((char *) location, strlen(location) + 1); (void) XLogInsert(RM_TBLSPC_ID, XLOG_TBLSPC_CREATE); } /* * Force synchronous commit, to minimize the window between creating the * symlink on-disk and marking the transaction committed. It's not great * that there is any window at all, but definitely we don't want to make * it larger than necessary. */ ForceSyncCommit(); pfree(location); /* We keep the lock on pg_tablespace until commit */ heap_close(rel, NoLock); return tablespaceoid; #else /* !HAVE_SYMLINK */ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("tablespaces are not supported on this platform"))); return InvalidOid; /* keep compiler quiet */ #endif /* HAVE_SYMLINK */ }
/* * ExtProtocolCreateWithOid */ Oid ExtProtocolCreateWithOid(const char *protocolName, List *readfuncName, List *writefuncName, List *validatorfuncName, Oid protOid, bool trusted) { Relation rel; HeapTuple tup; bool nulls[Natts_pg_extprotocol]; Datum values[Natts_pg_extprotocol]; Oid readfn = InvalidOid; Oid writefn = InvalidOid; Oid validatorfn = InvalidOid; NameData prtname; int i; ObjectAddress myself, referenced; Oid ownerId = GetUserId(); cqContext cqc; cqContext cqc2; cqContext *pcqCtx; /* sanity checks (caller should have caught these) */ if (!protocolName) elog(ERROR, "no protocol name supplied"); if (!readfuncName && !writefuncName) elog(ERROR, "protocol must have at least one of readfunc or writefunc"); /* * Until we add system protocols to pg_extprotocol, make sure no * protocols with the same name are created. */ if (strcasecmp(protocolName, "file") == 0 || strcasecmp(protocolName, "http") == 0 || strcasecmp(protocolName, "gpfdist") == 0 || strcasecmp(protocolName, "gpfdists") == 0) { ereport(ERROR, (errcode(ERRCODE_RESERVED_NAME), errmsg("protocol \"%s\" already exists", protocolName), errhint("pick a different protocol name"))); } rel = heap_open(ExtprotocolRelationId, RowExclusiveLock); pcqCtx = caql_beginscan( caql_addrel(cqclr(&cqc), rel), cql("INSERT INTO pg_extprotocol", NULL)); /* make sure there is no existing protocol of same name */ if (caql_getcount( caql_addrel(cqclr(&cqc2), rel), cql("SELECT COUNT(*) FROM pg_extprotocol " " WHERE ptcname = :1 ", CStringGetDatum((char *) protocolName)))) { ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("protocol \"%s\" already exists", protocolName))); } /* * function checks: if supplied, check existence and correct signature in the catalog */ if (readfuncName) readfn = ValidateProtocolFunction(readfuncName, EXTPTC_FUNC_READER); if (writefuncName) writefn = ValidateProtocolFunction(writefuncName, EXTPTC_FUNC_WRITER); if (validatorfuncName) validatorfn = ValidateProtocolFunction(validatorfuncName, EXTPTC_FUNC_VALIDATOR); /* * Everything looks okay. Try to create the pg_extprotocol entry for the * protocol. (This could fail if there's already a conflicting entry.) */ /* initialize nulls and values */ for (i = 0; i < Natts_pg_extprotocol; i++) { nulls[i] = false; values[i] = (Datum) 0; } namestrcpy(&prtname, protocolName); values[Anum_pg_extprotocol_ptcname - 1] = NameGetDatum(&prtname); values[Anum_pg_extprotocol_ptcreadfn - 1] = ObjectIdGetDatum(readfn); values[Anum_pg_extprotocol_ptcwritefn - 1] = ObjectIdGetDatum(writefn); values[Anum_pg_extprotocol_ptcvalidatorfn - 1] = ObjectIdGetDatum(validatorfn); values[Anum_pg_extprotocol_ptcowner - 1] = ObjectIdGetDatum(ownerId); values[Anum_pg_extprotocol_ptctrusted - 1] = BoolGetDatum(trusted); nulls[Anum_pg_extprotocol_ptcacl - 1] = true; tup = caql_form_tuple(pcqCtx, values, nulls); if (protOid != (Oid) 0) HeapTupleSetOid(tup, protOid); /* insert a new tuple */ protOid = caql_insert(pcqCtx, tup); /* implicit update of index as well */ caql_endscan(pcqCtx); heap_close(rel, RowExclusiveLock); /* * Create dependencies for the protocol */ myself.classId = ExtprotocolRelationId; myself.objectId = protOid; myself.objectSubId = 0; /* Depends on read function, if any */ if (OidIsValid(readfn)) { referenced.classId = ProcedureRelationId; referenced.objectId = readfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Depends on write function, if any */ if (OidIsValid(writefn)) { referenced.classId = ProcedureRelationId; referenced.objectId = writefn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* dependency on owner */ recordDependencyOnOwner(ExtprotocolRelationId, protOid, GetUserId()); return protOid; }
/* * CollationCreate * * Add a new tuple to pg_collation. */ Oid CollationCreate(const char *collname, Oid collnamespace, Oid collowner, int32 collencoding, const char *collcollate, const char *collctype) { Relation rel; TupleDesc tupDesc; HeapTuple tup; Datum values[Natts_pg_collation]; bool nulls[Natts_pg_collation]; NameData name_name, name_collate, name_ctype; Oid oid; ObjectAddress myself, referenced; AssertArg(collname); AssertArg(collnamespace); AssertArg(collowner); AssertArg(collcollate); AssertArg(collctype); /* * Make sure there is no existing collation of same name & encoding. * * This would be caught by the unique index anyway; we're just giving a * friendlier error message. The unique index provides a backstop against * race conditions. */ if (SearchSysCacheExists3(COLLNAMEENCNSP, PointerGetDatum(collname), Int32GetDatum(collencoding), ObjectIdGetDatum(collnamespace))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("collation \"%s\" for encoding \"%s\" already exists", collname, pg_encoding_to_char(collencoding)))); /* * Also forbid matching an any-encoding entry. This test of course is not * backed up by the unique index, but it's not a problem since we don't * support adding any-encoding entries after initdb. */ if (SearchSysCacheExists3(COLLNAMEENCNSP, PointerGetDatum(collname), Int32GetDatum(-1), ObjectIdGetDatum(collnamespace))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("collation \"%s\" already exists", collname))); /* open pg_collation */ rel = heap_open(CollationRelationId, RowExclusiveLock); tupDesc = RelationGetDescr(rel); /* form a tuple */ memset(nulls, 0, sizeof(nulls)); namestrcpy(&name_name, collname); values[Anum_pg_collation_collname - 1] = NameGetDatum(&name_name); values[Anum_pg_collation_collnamespace - 1] = ObjectIdGetDatum(collnamespace); values[Anum_pg_collation_collowner - 1] = ObjectIdGetDatum(collowner); values[Anum_pg_collation_collencoding - 1] = Int32GetDatum(collencoding); namestrcpy(&name_collate, collcollate); values[Anum_pg_collation_collcollate - 1] = NameGetDatum(&name_collate); namestrcpy(&name_ctype, collctype); values[Anum_pg_collation_collctype - 1] = NameGetDatum(&name_ctype); tup = heap_form_tuple(tupDesc, values, nulls); /* insert a new tuple */ oid = simple_heap_insert(rel, tup); Assert(OidIsValid(oid)); /* update the index if any */ CatalogUpdateIndexes(rel, tup); /* set up dependencies for the new collation */ myself.classId = CollationRelationId; myself.objectId = oid; myself.objectSubId = 0; /* create dependency on namespace */ referenced.classId = NamespaceRelationId; referenced.objectId = collnamespace; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* create dependency on owner */ recordDependencyOnOwner(CollationRelationId, HeapTupleGetOid(tup), collowner); /* dependency on extension */ recordDependencyOnCurrentExtension(&myself, false); /* Post creation hook for new collation */ InvokeObjectPostCreateHook(CollationRelationId, oid, 0); heap_freetuple(tup); heap_close(rel, RowExclusiveLock); return oid; }
/* * ConversionCreate * * Add a new tuple to pg_conversion. */ Oid ConversionCreate(const char *conname, Oid connamespace, Oid conowner, int32 conforencoding, int32 contoencoding, Oid conproc, bool def) { int i; Relation rel; TupleDesc tupDesc; HeapTuple tup; bool nulls[Natts_pg_conversion]; Datum values[Natts_pg_conversion]; NameData cname; Oid oid; ObjectAddress myself, referenced; /* sanity checks */ if (!conname) elog(ERROR, "no conversion name supplied"); /* make sure there is no existing conversion of same name */ if (SearchSysCacheExists2(CONNAMENSP, PointerGetDatum(conname), ObjectIdGetDatum(connamespace))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("conversion \"%s\" already exists", conname))); if (def) { /* * make sure there is no existing default <for encoding><to encoding> * pair in this name space */ if (FindDefaultConversion(connamespace, conforencoding, contoencoding)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("default conversion for %s to %s already exists", pg_encoding_to_char(conforencoding), pg_encoding_to_char(contoencoding)))); } /* open pg_conversion */ rel = heap_open(ConversionRelationId, RowExclusiveLock); tupDesc = rel->rd_att; /* initialize nulls and values */ for (i = 0; i < Natts_pg_conversion; i++) { nulls[i] = false; values[i] = (Datum) NULL; } /* form a tuple */ namestrcpy(&cname, conname); values[Anum_pg_conversion_conname - 1] = NameGetDatum(&cname); values[Anum_pg_conversion_connamespace - 1] = ObjectIdGetDatum(connamespace); values[Anum_pg_conversion_conowner - 1] = ObjectIdGetDatum(conowner); values[Anum_pg_conversion_conforencoding - 1] = Int32GetDatum(conforencoding); values[Anum_pg_conversion_contoencoding - 1] = Int32GetDatum(contoencoding); values[Anum_pg_conversion_conproc - 1] = ObjectIdGetDatum(conproc); values[Anum_pg_conversion_condefault - 1] = BoolGetDatum(def); tup = heap_form_tuple(tupDesc, values, nulls); /* insert a new tuple */ oid = simple_heap_insert(rel, tup); Assert(OidIsValid(oid)); /* update the index if any */ CatalogUpdateIndexes(rel, tup); myself.classId = ConversionRelationId; myself.objectId = HeapTupleGetOid(tup); myself.objectSubId = 0; /* create dependency on conversion procedure */ referenced.classId = ProcedureRelationId; referenced.objectId = conproc; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* create dependency on namespace */ referenced.classId = NamespaceRelationId; referenced.objectId = connamespace; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* create dependency on owner */ recordDependencyOnOwner(ConversionRelationId, HeapTupleGetOid(tup), conowner); /* dependency on extension */ recordDependencyOnCurrentExtension(&myself, false); /* Post creation hook for new conversion */ InvokeObjectPostCreateHook(ConversionRelationId, HeapTupleGetOid(tup), 0); heap_freetuple(tup); heap_close(rel, RowExclusiveLock); return oid; }
/* * CollationCreate * * Add a new tuple to pg_collation. * * if_not_exists: if true, don't fail on duplicate name, just print a notice * and return InvalidOid. * quiet: if true, don't fail on duplicate name, just silently return * InvalidOid (overrides if_not_exists). */ Oid CollationCreate(const char *collname, Oid collnamespace, Oid collowner, char collprovider, int32 collencoding, const char *collcollate, const char *collctype, const char *collversion, bool if_not_exists, bool quiet) { Relation rel; TupleDesc tupDesc; HeapTuple tup; Datum values[Natts_pg_collation]; bool nulls[Natts_pg_collation]; NameData name_name, name_collate, name_ctype; Oid oid; ObjectAddress myself, referenced; AssertArg(collname); AssertArg(collnamespace); AssertArg(collowner); AssertArg(collcollate); AssertArg(collctype); /* * Make sure there is no existing collation of same name & encoding. * * This would be caught by the unique index anyway; we're just giving a * friendlier error message. The unique index provides a backstop against * race conditions. */ if (SearchSysCacheExists3(COLLNAMEENCNSP, PointerGetDatum(collname), Int32GetDatum(collencoding), ObjectIdGetDatum(collnamespace))) { if (quiet) return InvalidOid; else if (if_not_exists) { ereport(NOTICE, (errcode(ERRCODE_DUPLICATE_OBJECT), collencoding == -1 ? errmsg("collation \"%s\" already exists, skipping", collname) : errmsg("collation \"%s\" for encoding \"%s\" already exists, skipping", collname, pg_encoding_to_char(collencoding)))); return InvalidOid; } else ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), collencoding == -1 ? errmsg("collation \"%s\" already exists", collname) : errmsg("collation \"%s\" for encoding \"%s\" already exists", collname, pg_encoding_to_char(collencoding)))); } /* open pg_collation; see below about the lock level */ rel = heap_open(CollationRelationId, ShareRowExclusiveLock); /* * Also forbid a specific-encoding collation shadowing an any-encoding * collation, or an any-encoding collation being shadowed (see * get_collation_name()). This test is not backed up by the unique index, * so we take a ShareRowExclusiveLock earlier, to protect against * concurrent changes fooling this check. */ if ((collencoding == -1 && SearchSysCacheExists3(COLLNAMEENCNSP, PointerGetDatum(collname), Int32GetDatum(GetDatabaseEncoding()), ObjectIdGetDatum(collnamespace))) || (collencoding != -1 && SearchSysCacheExists3(COLLNAMEENCNSP, PointerGetDatum(collname), Int32GetDatum(-1), ObjectIdGetDatum(collnamespace)))) { if (quiet) { heap_close(rel, NoLock); return InvalidOid; } else if (if_not_exists) { heap_close(rel, NoLock); ereport(NOTICE, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("collation \"%s\" already exists, skipping", collname))); return InvalidOid; } else ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("collation \"%s\" already exists", collname))); } tupDesc = RelationGetDescr(rel); /* form a tuple */ memset(nulls, 0, sizeof(nulls)); namestrcpy(&name_name, collname); values[Anum_pg_collation_collname - 1] = NameGetDatum(&name_name); values[Anum_pg_collation_collnamespace - 1] = ObjectIdGetDatum(collnamespace); values[Anum_pg_collation_collowner - 1] = ObjectIdGetDatum(collowner); values[Anum_pg_collation_collprovider - 1] = CharGetDatum(collprovider); values[Anum_pg_collation_collencoding - 1] = Int32GetDatum(collencoding); namestrcpy(&name_collate, collcollate); values[Anum_pg_collation_collcollate - 1] = NameGetDatum(&name_collate); namestrcpy(&name_ctype, collctype); values[Anum_pg_collation_collctype - 1] = NameGetDatum(&name_ctype); if (collversion) values[Anum_pg_collation_collversion - 1] = CStringGetTextDatum(collversion); else nulls[Anum_pg_collation_collversion - 1] = true; tup = heap_form_tuple(tupDesc, values, nulls); /* insert a new tuple */ oid = CatalogTupleInsert(rel, tup); Assert(OidIsValid(oid)); /* set up dependencies for the new collation */ myself.classId = CollationRelationId; myself.objectId = oid; myself.objectSubId = 0; /* create dependency on namespace */ referenced.classId = NamespaceRelationId; referenced.objectId = collnamespace; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* create dependency on owner */ recordDependencyOnOwner(CollationRelationId, HeapTupleGetOid(tup), collowner); /* dependency on extension */ recordDependencyOnCurrentExtension(&myself, false); /* Post creation hook for new collation */ InvokeObjectPostCreateHook(CollationRelationId, oid, 0); heap_freetuple(tup); heap_close(rel, NoLock); return oid; }
/* ---------------------------------------------------------------- * ProcedureCreate * * Note: allParameterTypes, parameterModes, parameterNames are either arrays * of the proper types or NULL. We declare them Datum, not "ArrayType *", * to avoid importing array.h into pg_proc.h. * ---------------------------------------------------------------- */ Oid ProcedureCreate(const char *procedureName, Oid procNamespace, bool replace, bool returnsSet, Oid returnType, Oid languageObjectId, Oid languageValidator, const char *prosrc, const char *probin, bool isAgg, bool security_definer, bool isStrict, char volatility, oidvector *parameterTypes, Datum allParameterTypes, Datum parameterModes, Datum parameterNames) { Oid retval; int parameterCount; int allParamCount; Oid *allParams; bool genericInParam = false; bool genericOutParam = false; bool internalInParam = false; bool internalOutParam = false; Relation rel; HeapTuple tup; HeapTuple oldtup; char nulls[Natts_pg_proc]; Datum values[Natts_pg_proc]; char replaces[Natts_pg_proc]; Oid relid; NameData procname; TupleDesc tupDesc; bool is_update; ObjectAddress myself, referenced; int i; /* * sanity checks */ Assert(PointerIsValid(prosrc)); Assert(PointerIsValid(probin)); parameterCount = parameterTypes->dim1; if (parameterCount < 0 || parameterCount > FUNC_MAX_ARGS) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_ARGUMENTS), errmsg("functions cannot have more than %d arguments", FUNC_MAX_ARGS))); /* note: the above is correct, we do NOT count output arguments */ if (allParameterTypes != PointerGetDatum(NULL)) { /* * We expect the array to be a 1-D OID array; verify that. We don't * need to use deconstruct_array() since the array data is just going * to look like a C array of OID values. */ allParamCount = ARR_DIMS(DatumGetPointer(allParameterTypes))[0]; if (ARR_NDIM(DatumGetPointer(allParameterTypes)) != 1 || allParamCount <= 0 || ARR_ELEMTYPE(DatumGetPointer(allParameterTypes)) != OIDOID) elog(ERROR, "allParameterTypes is not a 1-D Oid array"); allParams = (Oid *) ARR_DATA_PTR(DatumGetPointer(allParameterTypes)); Assert(allParamCount >= parameterCount); /* we assume caller got the contents right */ } else { allParamCount = parameterCount; allParams = parameterTypes->values; } /* * Do not allow return type ANYARRAY or ANYELEMENT unless at least one * input argument is ANYARRAY or ANYELEMENT. Also, do not allow return * type INTERNAL unless at least one input argument is INTERNAL. */ for (i = 0; i < parameterCount; i++) { switch (parameterTypes->values[i]) { case ANYARRAYOID: case ANYELEMENTOID: genericInParam = true; break; case INTERNALOID: internalInParam = true; break; } } if (allParameterTypes != PointerGetDatum(NULL)) { for (i = 0; i < allParamCount; i++) { /* * We don't bother to distinguish input and output params here, so * if there is, say, just an input INTERNAL param then we will * still set internalOutParam. This is OK since we don't really * care. */ switch (allParams[i]) { case ANYARRAYOID: case ANYELEMENTOID: genericOutParam = true; break; case INTERNALOID: internalOutParam = true; break; } } } if ((returnType == ANYARRAYOID || returnType == ANYELEMENTOID || genericOutParam) && !genericInParam) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine result data type"), errdetail("A function returning \"anyarray\" or \"anyelement\" must have at least one argument of either type."))); if ((returnType == INTERNALOID || internalOutParam) && !internalInParam) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("unsafe use of pseudo-type \"internal\""), errdetail("A function returning \"internal\" must have at least one \"internal\" argument."))); /* * don't allow functions of complex types that have the same name as * existing attributes of the type */ if (parameterCount == 1 && OidIsValid(parameterTypes->values[0]) && (relid = typeidTypeRelid(parameterTypes->values[0])) != InvalidOid && get_attnum(relid, procedureName) != InvalidAttrNumber) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_COLUMN), errmsg("\"%s\" is already an attribute of type %s", procedureName, format_type_be(parameterTypes->values[0])))); /* * All seems OK; prepare the data to be inserted into pg_proc. */ for (i = 0; i < Natts_pg_proc; ++i) { nulls[i] = ' '; values[i] = (Datum) 0; replaces[i] = 'r'; } namestrcpy(&procname, procedureName); values[Anum_pg_proc_proname - 1] = NameGetDatum(&procname); values[Anum_pg_proc_pronamespace - 1] = ObjectIdGetDatum(procNamespace); values[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(GetUserId()); values[Anum_pg_proc_prolang - 1] = ObjectIdGetDatum(languageObjectId); values[Anum_pg_proc_proisagg - 1] = BoolGetDatum(isAgg); values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer); values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict); values[Anum_pg_proc_proretset - 1] = BoolGetDatum(returnsSet); values[Anum_pg_proc_provolatile - 1] = CharGetDatum(volatility); values[Anum_pg_proc_pronargs - 1] = UInt16GetDatum(parameterCount); values[Anum_pg_proc_prorettype - 1] = ObjectIdGetDatum(returnType); values[Anum_pg_proc_proargtypes - 1] = PointerGetDatum(parameterTypes); if (allParameterTypes != PointerGetDatum(NULL)) values[Anum_pg_proc_proallargtypes - 1] = allParameterTypes; else nulls[Anum_pg_proc_proallargtypes - 1] = 'n'; if (parameterModes != PointerGetDatum(NULL)) values[Anum_pg_proc_proargmodes - 1] = parameterModes; else nulls[Anum_pg_proc_proargmodes - 1] = 'n'; if (parameterNames != PointerGetDatum(NULL)) values[Anum_pg_proc_proargnames - 1] = parameterNames; else nulls[Anum_pg_proc_proargnames - 1] = 'n'; values[Anum_pg_proc_prosrc - 1] = DirectFunctionCall1(textin, CStringGetDatum(prosrc)); values[Anum_pg_proc_probin - 1] = DirectFunctionCall1(textin, CStringGetDatum(probin)); /* start out with empty permissions */ nulls[Anum_pg_proc_proacl - 1] = 'n'; rel = heap_open(ProcedureRelationId, RowExclusiveLock); tupDesc = RelationGetDescr(rel); /* Check for pre-existing definition */ oldtup = SearchSysCache(PROCNAMEARGSNSP, PointerGetDatum(procedureName), PointerGetDatum(parameterTypes), ObjectIdGetDatum(procNamespace), 0); if (HeapTupleIsValid(oldtup)) { /* There is one; okay to replace it? */ Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup); if (!replace) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_FUNCTION), errmsg("function \"%s\" already exists with same argument types", procedureName))); if (!pg_proc_ownercheck(HeapTupleGetOid(oldtup), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, procedureName); /* * Not okay to change the return type of the existing proc, since * existing rules, views, etc may depend on the return type. */ if (returnType != oldproc->prorettype || returnsSet != oldproc->proretset) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot change return type of existing function"), errhint("Use DROP FUNCTION first."))); /* * If it returns RECORD, check for possible change of record type * implied by OUT parameters */ if (returnType == RECORDOID) { TupleDesc olddesc; TupleDesc newdesc; olddesc = build_function_result_tupdesc_t(oldtup); newdesc = build_function_result_tupdesc_d(allParameterTypes, parameterModes, parameterNames); if (olddesc == NULL && newdesc == NULL) /* ok, both are runtime-defined RECORDs */ ; else if (olddesc == NULL || newdesc == NULL || !equalTupleDescs(olddesc, newdesc)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot change return type of existing function"), errdetail("Row type defined by OUT parameters is different."), errhint("Use DROP FUNCTION first."))); } /* Can't change aggregate status, either */ if (oldproc->proisagg != isAgg) { if (oldproc->proisagg) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("function \"%s\" is an aggregate", procedureName))); else ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("function \"%s\" is not an aggregate", procedureName))); } /* do not change existing ownership or permissions, either */ replaces[Anum_pg_proc_proowner - 1] = ' '; replaces[Anum_pg_proc_proacl - 1] = ' '; /* Okay, do it... */ tup = heap_modifytuple(oldtup, tupDesc, values, nulls, replaces); simple_heap_update(rel, &tup->t_self, tup); ReleaseSysCache(oldtup); is_update = true; } else { /* Creating a new procedure */ tup = heap_formtuple(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); retval = HeapTupleGetOid(tup); /* * Create dependencies for the new function. If we are updating an * existing function, first delete any existing pg_depend entries. */ if (is_update) { deleteDependencyRecordsFor(ProcedureRelationId, retval); deleteSharedDependencyRecordsFor(ProcedureRelationId, retval); } myself.classId = ProcedureRelationId; myself.objectId = retval; myself.objectSubId = 0; /* dependency on namespace */ referenced.classId = NamespaceRelationId; referenced.objectId = procNamespace; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* dependency on implementation language */ referenced.classId = LanguageRelationId; referenced.objectId = languageObjectId; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* dependency on return type */ referenced.classId = TypeRelationId; referenced.objectId = returnType; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* dependency on parameter types */ for (i = 0; i < allParamCount; i++) { referenced.classId = TypeRelationId; referenced.objectId = allParams[i]; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* dependency on owner */ recordDependencyOnOwner(ProcedureRelationId, retval, GetUserId()); heap_freetuple(tup); heap_close(rel, RowExclusiveLock); /* Verify function body */ if (OidIsValid(languageValidator)) { /* Advance command counter so new tuple can be seen by validator */ CommandCounterIncrement(); OidFunctionCall1(languageValidator, ObjectIdGetDatum(retval)); } return retval; }
/* * GenerateTypeDependencies: build the dependencies needed for a type * * Most of what this function needs to know about the type is passed as the * new pg_type row, typeForm. But we can't get at the varlena fields through * that, so defaultExpr and typacl are passed separately. (typacl is really * "Acl *", but we declare it "void *" to avoid including acl.h in pg_type.h.) * * relationKind and isImplicitArray aren't visible in the pg_type row either, * so they're also passed separately. * * isDependentType is true if this is an implicit array or relation rowtype; * that means it doesn't need its own dependencies on owner etc. * * If rebuild is true, we remove existing dependencies and rebuild them * from scratch. This is needed for ALTER TYPE, and also when replacing * a shell type. We don't remove an existing extension dependency, though. * (That means an extension can't absorb a shell type created in another * extension, nor ALTER a type created by another extension. Also, if it * replaces a free-standing shell type or ALTERs a free-standing type, * that type will become a member of the extension.) */ void GenerateTypeDependencies(Oid typeObjectId, Form_pg_type typeForm, Node *defaultExpr, void *typacl, char relationKind, /* only for relation rowtypes */ bool isImplicitArray, bool isDependentType, bool rebuild) { ObjectAddress myself, referenced; /* If rebuild, first flush old dependencies, except extension deps */ if (rebuild) { deleteDependencyRecordsFor(TypeRelationId, typeObjectId, true); deleteSharedDependencyRecordsFor(TypeRelationId, typeObjectId, 0); } myself.classId = TypeRelationId; myself.objectId = typeObjectId; myself.objectSubId = 0; /* * Make dependencies on namespace, owner, ACL, extension. * * Skip these for a dependent type, since it will have such dependencies * indirectly through its depended-on type or relation. */ if (!isDependentType) { referenced.classId = NamespaceRelationId; referenced.objectId = typeForm->typnamespace; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); recordDependencyOnOwner(TypeRelationId, typeObjectId, typeForm->typowner); recordDependencyOnNewAcl(TypeRelationId, typeObjectId, 0, typeForm->typowner, typacl); recordDependencyOnCurrentExtension(&myself, rebuild); } /* Normal dependencies on the I/O functions */ if (OidIsValid(typeForm->typinput)) { referenced.classId = ProcedureRelationId; referenced.objectId = typeForm->typinput; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(typeForm->typoutput)) { referenced.classId = ProcedureRelationId; referenced.objectId = typeForm->typoutput; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(typeForm->typreceive)) { referenced.classId = ProcedureRelationId; referenced.objectId = typeForm->typreceive; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(typeForm->typsend)) { referenced.classId = ProcedureRelationId; referenced.objectId = typeForm->typsend; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(typeForm->typmodin)) { referenced.classId = ProcedureRelationId; referenced.objectId = typeForm->typmodin; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(typeForm->typmodout)) { referenced.classId = ProcedureRelationId; referenced.objectId = typeForm->typmodout; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(typeForm->typanalyze)) { referenced.classId = ProcedureRelationId; referenced.objectId = typeForm->typanalyze; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* * If the type is a rowtype for a relation, mark it as internally * dependent on the relation, *unless* it is a stand-alone composite type * relation. For the latter case, we have to reverse the dependency. * * In the former case, this allows the type to be auto-dropped when the * relation is, and not otherwise. And in the latter, of course we get the * opposite effect. */ if (OidIsValid(typeForm->typrelid)) { referenced.classId = RelationRelationId; referenced.objectId = typeForm->typrelid; referenced.objectSubId = 0; if (relationKind != RELKIND_COMPOSITE_TYPE) recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); else recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL); } /* * If the type is an implicitly-created array type, mark it as internally * dependent on the element type. Otherwise, if it has an element type, * the dependency is a normal one. */ if (OidIsValid(typeForm->typelem)) { referenced.classId = TypeRelationId; referenced.objectId = typeForm->typelem; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, isImplicitArray ? DEPENDENCY_INTERNAL : DEPENDENCY_NORMAL); } /* Normal dependency from a domain to its base type. */ if (OidIsValid(typeForm->typbasetype)) { referenced.classId = TypeRelationId; referenced.objectId = typeForm->typbasetype; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Normal dependency from a domain to its collation. */ /* We know the default collation is pinned, so don't bother recording it */ if (OidIsValid(typeForm->typcollation) && typeForm->typcollation != DEFAULT_COLLATION_OID) { referenced.classId = CollationRelationId; referenced.objectId = typeForm->typcollation; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Normal dependency on the default expression. */ if (defaultExpr) recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL); }
/* * Create a table space * * Only superusers can create a tablespace. This seems a reasonable restriction * since we're determining the system layout and, anyway, we probably have * root if we're doing this kind of activity */ void CreateTableSpace(CreateTableSpaceStmt *stmt) { #ifdef HAVE_SYMLINK Relation rel; Datum values[Natts_pg_tablespace]; bool nulls[Natts_pg_tablespace]; HeapTuple tuple; Oid tablespaceoid; char *location; Oid ownerId; /* Must be super user */ if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to create tablespace \"%s\"", stmt->tablespacename), errhint("Must be superuser to create a tablespace."))); /* However, the eventual owner of the tablespace need not be */ if (stmt->owner) ownerId = get_role_oid(stmt->owner, false); else ownerId = GetUserId(); /* Unix-ify the offered path, and strip any trailing slashes */ location = pstrdup(stmt->location); canonicalize_path(location); /* disallow quotes, else CREATE DATABASE would be at risk */ if (strchr(location, '\'')) ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("tablespace location cannot contain single quotes"))); /* * Allowing relative paths seems risky * * this also helps us ensure that location is not empty or whitespace */ if (!is_absolute_path(location)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("tablespace location must be an absolute path"))); /* * Check that location isn't too long. Remember that we're going to append * 'PG_XXX/<dboid>/<relid>.<nnn>'. FYI, we never actually reference the * whole path, but mkdir() uses the first two parts. */ if (strlen(location) + 1 + strlen(TABLESPACE_VERSION_DIRECTORY) + 1 + #ifdef PGXC /* * In Postgres-XC, node name is added in the tablespace folder name to * insure unique names for nodes sharing the same server. * So real format is PG_XXX_<nodename>/<dboid>/<relid>.<nnn>'' */ strlen(PGXCNodeName) + 1 + #endif OIDCHARS + 1 + OIDCHARS + 1 + OIDCHARS > MAXPGPATH) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("tablespace location \"%s\" is too long", location))); /* * Disallow creation of tablespaces named "pg_xxx"; we reserve this * namespace for system purposes. */ if (!allowSystemTableMods && IsReservedName(stmt->tablespacename)) ereport(ERROR, (errcode(ERRCODE_RESERVED_NAME), errmsg("unacceptable tablespace name \"%s\"", stmt->tablespacename), errdetail("The prefix \"pg_\" is reserved for system tablespaces."))); /* * Check that there is no other tablespace by this name. (The unique * index would catch this anyway, but might as well give a friendlier * message.) */ if (OidIsValid(get_tablespace_oid(stmt->tablespacename, true))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("tablespace \"%s\" already exists", stmt->tablespacename))); /* * Insert tuple into pg_tablespace. The purpose of doing this first is to * lock the proposed tablename against other would-be creators. The * insertion will roll back if we find problems below. */ rel = heap_open(TableSpaceRelationId, RowExclusiveLock); MemSet(nulls, false, sizeof(nulls)); values[Anum_pg_tablespace_spcname - 1] = DirectFunctionCall1(namein, CStringGetDatum(stmt->tablespacename)); values[Anum_pg_tablespace_spcowner - 1] = ObjectIdGetDatum(ownerId); nulls[Anum_pg_tablespace_spcacl - 1] = true; nulls[Anum_pg_tablespace_spcoptions - 1] = true; tuple = heap_form_tuple(rel->rd_att, values, nulls); tablespaceoid = simple_heap_insert(rel, tuple); CatalogUpdateIndexes(rel, tuple); heap_freetuple(tuple); /* Record dependency on owner */ recordDependencyOnOwner(TableSpaceRelationId, tablespaceoid, ownerId); /* Post creation hook for new tablespace */ InvokeObjectAccessHook(OAT_POST_CREATE, TableSpaceRelationId, tablespaceoid, 0, NULL); create_tablespace_directories(location, tablespaceoid); #ifdef PGXC /* * Even if we have succeeded, the transaction can be aborted because of * failure on other nodes. So register for cleanup. */ set_dbcleanup_callback(createtbspc_abort_callback, &tablespaceoid, sizeof(tablespaceoid)); #endif /* Record the filesystem change in XLOG */ { xl_tblspc_create_rec xlrec; XLogRecData rdata[2]; xlrec.ts_id = tablespaceoid; rdata[0].data = (char *) &xlrec; rdata[0].len = offsetof(xl_tblspc_create_rec, ts_path); rdata[0].buffer = InvalidBuffer; rdata[0].next = &(rdata[1]); rdata[1].data = (char *) location; rdata[1].len = strlen(location) + 1; rdata[1].buffer = InvalidBuffer; rdata[1].next = NULL; (void) XLogInsert(RM_TBLSPC_ID, XLOG_TBLSPC_CREATE, rdata); } /* * Force synchronous commit, to minimize the window between creating the * symlink on-disk and marking the transaction committed. It's not great * that there is any window at all, but definitely we don't want to make * it larger than necessary. */ ForceSyncCommit(); pfree(location); /* We keep the lock on pg_tablespace until commit */ heap_close(rel, NoLock); #else /* !HAVE_SYMLINK */ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("tablespaces are not supported on this platform"))); #endif /* HAVE_SYMLINK */ }
/* ---------------- * NamespaceCreate * --------------- */ Oid NamespaceCreate(const char *nspName, Oid ownerId, Oid forceOid) { Relation nspdesc; HeapTuple tup; Oid nspoid; bool nulls[Natts_pg_namespace]; Datum values[Natts_pg_namespace]; NameData nname; int i; cqContext cqc; cqContext cqc2; cqContext *pcqCtx; /* sanity checks */ if (!nspName) elog(ERROR, "no namespace name supplied"); nspdesc = heap_open(NamespaceRelationId, RowExclusiveLock); pcqCtx = caql_beginscan( caql_addrel(cqclr(&cqc), nspdesc), cql("INSERT INTO pg_namespace", NULL)); /* make sure there is no existing namespace of same name in the current database */ if (caql_getcount( caql_addrel(cqclr(&cqc2), nspdesc), cql("SELECT COUNT(*) FROM pg_namespace " " WHERE nspname = :1 and nspdboid = :2", PointerGetDatum((char *) nspName), ObjectIdGetDatum((Oid) NSPDBOID_CURRENT)))) { ereport(ERROR, (errcode(ERRCODE_DUPLICATE_SCHEMA), errmsg("schema \"%s\" already exists", nspName))); } /* initialize nulls and values */ for (i = 0; i < Natts_pg_namespace; i++) { nulls[i] = false; values[i] = (Datum) 0; } namestrcpy(&nname, nspName); values[Anum_pg_namespace_nspname - 1] = NameGetDatum(&nname); values[Anum_pg_namespace_nspowner - 1] = ObjectIdGetDatum(ownerId); nulls[Anum_pg_namespace_nspacl - 1] = true; values[Anum_pg_namespace_nspdboid - 1] = ObjectIdGetDatum((Oid) NSPDBOID_CURRENT); tup = caql_form_tuple(pcqCtx, values, nulls); if (forceOid != InvalidOid) HeapTupleSetOid(tup, forceOid); /* override heap_insert's OID * selection */ /* insert a new tuple */ nspoid = caql_insert(pcqCtx, tup); /* implicit update of index as well */ Assert(OidIsValid(nspoid)); caql_endscan(pcqCtx); heap_close(nspdesc, RowExclusiveLock); /* Record dependency on owner */ recordDependencyOnOwner(NamespaceRelationId, nspoid, ownerId); return nspoid; }
/* * GenerateTypeDependencies: build the dependencies needed for a type * * If rebuild is true, we remove existing dependencies and rebuild them * from scratch. This is needed for ALTER TYPE, and also when replacing * a shell type. */ void GenerateTypeDependencies(Oid typeNamespace, Oid typeObjectId, Oid relationOid, /* only for 'c'atalog types */ char relationKind, /* ditto */ Oid owner, Oid inputProcedure, Oid outputProcedure, Oid receiveProcedure, Oid sendProcedure, Oid analyzeProcedure, Oid elementType, Oid baseType, Node *defaultExpr, bool rebuild) { ObjectAddress myself, referenced; if (rebuild) { deleteDependencyRecordsFor(TypeRelationId, typeObjectId); deleteSharedDependencyRecordsFor(TypeRelationId, typeObjectId); } myself.classId = TypeRelationId; myself.objectId = typeObjectId; myself.objectSubId = 0; /* dependency on namespace */ /* skip for relation rowtype, since we have indirect dependency */ if (!OidIsValid(relationOid) || relationKind == RELKIND_COMPOSITE_TYPE) { referenced.classId = NamespaceRelationId; referenced.objectId = typeNamespace; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); recordDependencyOnOwner(TypeRelationId, typeObjectId, owner); } /* Normal dependencies on the I/O functions */ if (OidIsValid(inputProcedure)) { referenced.classId = ProcedureRelationId; referenced.objectId = inputProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(outputProcedure)) { referenced.classId = ProcedureRelationId; referenced.objectId = outputProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(receiveProcedure)) { referenced.classId = ProcedureRelationId; referenced.objectId = receiveProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(sendProcedure)) { referenced.classId = ProcedureRelationId; referenced.objectId = sendProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(analyzeProcedure)) { referenced.classId = ProcedureRelationId; referenced.objectId = analyzeProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* * If the type is a rowtype for a relation, mark it as internally * dependent on the relation, *unless* it is a stand-alone composite type * relation. For the latter case, we have to reverse the dependency. * * In the former case, this allows the type to be auto-dropped when the * relation is, and not otherwise. And in the latter, of course we get the * opposite effect. */ if (OidIsValid(relationOid)) { referenced.classId = RelationRelationId; referenced.objectId = relationOid; referenced.objectSubId = 0; if (relationKind != RELKIND_COMPOSITE_TYPE) recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); else recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL); } /* * If the type is an array type, mark it auto-dependent on the base type. * (This is a compromise between the typical case where the array type is * automatically generated and the case where it is manually created: we'd * prefer INTERNAL for the former case and NORMAL for the latter.) */ if (OidIsValid(elementType)) { referenced.classId = TypeRelationId; referenced.objectId = elementType; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); } /* Normal dependency from a domain to its base type. */ if (OidIsValid(baseType)) { referenced.classId = TypeRelationId; referenced.objectId = baseType; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Normal dependency on the default expression. */ if (defaultExpr) recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL); }
/* * ExtProtocolCreate */ Oid ExtProtocolCreate(const char *protocolName, List *readfuncName, List *writefuncName, List *validatorfuncName, bool trusted) { Relation rel; HeapTuple tup; bool nulls[Natts_pg_extprotocol]; Datum values[Natts_pg_extprotocol]; Oid readfn = InvalidOid; Oid writefn = InvalidOid; Oid validatorfn = InvalidOid; NameData prtname; int i; ObjectAddress myself, referenced; Oid ownerId = GetUserId(); ScanKeyData skey; SysScanDesc scan; Oid protOid; /* sanity checks (caller should have caught these) */ if (!protocolName) elog(ERROR, "no protocol name supplied"); if (!readfuncName && !writefuncName) elog(ERROR, "protocol must have at least one of readfunc or writefunc"); /* * Until we add system protocols to pg_extprotocol, make sure no * protocols with the same name are created. */ if (strcasecmp(protocolName, "file") == 0 || strcasecmp(protocolName, "http") == 0 || strcasecmp(protocolName, "gpfdist") == 0 || strcasecmp(protocolName, "gpfdists") == 0) { ereport(ERROR, (errcode(ERRCODE_RESERVED_NAME), errmsg("protocol \"%s\" already exists", protocolName), errhint("pick a different protocol name"))); } rel = heap_open(ExtprotocolRelationId, RowExclusiveLock); /* make sure there is no existing protocol of same name */ ScanKeyInit(&skey, Anum_pg_extprotocol_ptcname, BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(protocolName)); scan = systable_beginscan(rel, ExtprotocolPtcnameIndexId, true, SnapshotNow, 1, &skey); tup = systable_getnext(scan); if (HeapTupleIsValid(tup)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("protocol \"%s\" already exists", protocolName))); systable_endscan(scan); /* * function checks: if supplied, check existence and correct signature in the catalog */ if (readfuncName) readfn = ValidateProtocolFunction(readfuncName, EXTPTC_FUNC_READER); if (writefuncName) writefn = ValidateProtocolFunction(writefuncName, EXTPTC_FUNC_WRITER); if (validatorfuncName) validatorfn = ValidateProtocolFunction(validatorfuncName, EXTPTC_FUNC_VALIDATOR); /* * Everything looks okay. Try to create the pg_extprotocol entry for the * protocol. (This could fail if there's already a conflicting entry.) */ /* initialize nulls and values */ for (i = 0; i < Natts_pg_extprotocol; i++) { nulls[i] = false; values[i] = (Datum) 0; } namestrcpy(&prtname, protocolName); values[Anum_pg_extprotocol_ptcname - 1] = NameGetDatum(&prtname); values[Anum_pg_extprotocol_ptcreadfn - 1] = ObjectIdGetDatum(readfn); values[Anum_pg_extprotocol_ptcwritefn - 1] = ObjectIdGetDatum(writefn); values[Anum_pg_extprotocol_ptcvalidatorfn - 1] = ObjectIdGetDatum(validatorfn); values[Anum_pg_extprotocol_ptcowner - 1] = ObjectIdGetDatum(ownerId); values[Anum_pg_extprotocol_ptctrusted - 1] = BoolGetDatum(trusted); nulls[Anum_pg_extprotocol_ptcacl - 1] = true; tup = heap_form_tuple(RelationGetDescr(rel), values, nulls); /* insert a new tuple */ protOid = simple_heap_insert(rel, tup); CatalogUpdateIndexes(rel, tup); heap_close(rel, RowExclusiveLock); /* * Create dependencies for the protocol */ myself.classId = ExtprotocolRelationId; myself.objectId = protOid; myself.objectSubId = 0; /* Depends on read function, if any */ if (OidIsValid(readfn)) { referenced.classId = ProcedureRelationId; referenced.objectId = readfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Depends on write function, if any */ if (OidIsValid(writefn)) { referenced.classId = ProcedureRelationId; referenced.objectId = writefn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* dependency on owner */ recordDependencyOnOwner(ExtprotocolRelationId, protOid, GetUserId()); /* dependency on extension */ recordDependencyOnCurrentExtension(&myself, false); return protOid; }
/* ---------------- * NamespaceCreate * * Create a namespace (schema) with the given name and owner OID. * * If isTemp is true, this schema is a per-backend schema for holding * temporary tables. Currently, it is used to prevent it from being * linked as a member of any active extension. (If someone does CREATE * TEMP TABLE in an extension script, we don't want the temp schema to * become part of the extension). And to avoid checking for default ACL * for temp namespace (as it is not necessary). * --------------- */ Oid NamespaceCreate(const char *nspName, Oid ownerId, bool isTemp) { Relation nspdesc; HeapTuple tup; Oid nspoid; bool nulls[Natts_pg_namespace]; Datum values[Natts_pg_namespace]; NameData nname; TupleDesc tupDesc; ObjectAddress myself; int i; Acl *nspacl; /* sanity checks */ if (!nspName) elog(ERROR, "no namespace name supplied"); /* make sure there is no existing namespace of same name */ if (SearchSysCacheExists1(NAMESPACENAME, PointerGetDatum(nspName))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_SCHEMA), errmsg("schema \"%s\" already exists", nspName))); if (!isTemp) nspacl = get_user_default_acl(OBJECT_SCHEMA, ownerId, InvalidOid); else nspacl = NULL; nspdesc = heap_open(NamespaceRelationId, RowExclusiveLock); tupDesc = nspdesc->rd_att; /* initialize nulls and values */ for (i = 0; i < Natts_pg_namespace; i++) { nulls[i] = false; values[i] = (Datum) NULL; } nspoid = GetNewOidWithIndex(nspdesc, NamespaceOidIndexId, Anum_pg_namespace_oid); values[Anum_pg_namespace_oid - 1] = ObjectIdGetDatum(nspoid); namestrcpy(&nname, nspName); values[Anum_pg_namespace_nspname - 1] = NameGetDatum(&nname); values[Anum_pg_namespace_nspowner - 1] = ObjectIdGetDatum(ownerId); if (nspacl != NULL) values[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(nspacl); else nulls[Anum_pg_namespace_nspacl - 1] = true; tup = heap_form_tuple(tupDesc, values, nulls); CatalogTupleInsert(nspdesc, tup); Assert(OidIsValid(nspoid)); heap_close(nspdesc, RowExclusiveLock); /* Record dependencies */ myself.classId = NamespaceRelationId; myself.objectId = nspoid; myself.objectSubId = 0; /* dependency on owner */ recordDependencyOnOwner(NamespaceRelationId, nspoid, ownerId); /* dependences on roles mentioned in default ACL */ recordDependencyOnNewAcl(NamespaceRelationId, nspoid, 0, ownerId, nspacl); /* dependency on extension ... but not for magic temp schemas */ if (!isTemp) recordDependencyOnCurrentExtension(&myself, false); /* Post creation hook for new schema */ InvokeObjectPostCreateHook(NamespaceRelationId, nspoid, 0); return nspoid; }
/* * Create a table space * * Only superusers can create a tablespace. This seems a reasonable restriction * since we're determining the system layout and, anyway, we probably have * root if we're doing this kind of activity */ void CreateTableSpace(CreateTableSpaceStmt *stmt) { #ifdef HAVE_SYMLINK Relation rel; Datum values[Natts_pg_tablespace]; char nulls[Natts_pg_tablespace]; HeapTuple tuple; Oid tablespaceoid; char *location; char *linkloc; Oid ownerId; /* validate */ /* don't call this in a transaction block */ PreventTransactionChain((void *) stmt, "CREATE TABLESPACE"); /* Must be super user */ if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to create tablespace \"%s\"", stmt->tablespacename), errhint("Must be superuser to create a tablespace."))); /* However, the eventual owner of the tablespace need not be */ if (stmt->owner) ownerId = get_roleid_checked(stmt->owner); else ownerId = GetUserId(); /* Unix-ify the offered path, and strip any trailing slashes */ location = pstrdup(stmt->location); canonicalize_path(location); /* disallow quotes, else CREATE DATABASE would be at risk */ if (strchr(location, '\'')) ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("tablespace location may not contain single quotes"))); /* * Allowing relative paths seems risky * * this also helps us ensure that location is not empty or whitespace */ if (!is_absolute_path(location)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("tablespace location must be an absolute path"))); /* * Check that location isn't too long. Remember that we're going to append * '/<dboid>/<relid>.<nnn>' (XXX but do we ever form the whole path * explicitly? This may be overly conservative.) */ if (strlen(location) >= (MAXPGPATH - 1 - 10 - 1 - 10 - 1 - 10)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("tablespace location \"%s\" is too long", location))); /* * Disallow creation of tablespaces named "pg_xxx"; we reserve this * namespace for system purposes. */ if (!allowSystemTableMods && IsReservedName(stmt->tablespacename)) ereport(ERROR, (errcode(ERRCODE_RESERVED_NAME), errmsg("unacceptable tablespace name \"%s\"", stmt->tablespacename), errdetail("The prefix \"pg_\" is reserved for system tablespaces."))); /* * Check that there is no other tablespace by this name. (The unique * index would catch this anyway, but might as well give a friendlier * message.) */ if (OidIsValid(get_tablespace_oid(stmt->tablespacename))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("tablespace \"%s\" already exists", stmt->tablespacename))); /* * Insert tuple into pg_tablespace. The purpose of doing this first is to * lock the proposed tablename against other would-be creators. The * insertion will roll back if we find problems below. */ rel = heap_open(TableSpaceRelationId, RowExclusiveLock); MemSet(nulls, ' ', Natts_pg_tablespace); values[Anum_pg_tablespace_spcname - 1] = DirectFunctionCall1(namein, CStringGetDatum(stmt->tablespacename)); values[Anum_pg_tablespace_spcowner - 1] = ObjectIdGetDatum(ownerId); values[Anum_pg_tablespace_spclocation - 1] = DirectFunctionCall1(textin, CStringGetDatum(location)); nulls[Anum_pg_tablespace_spcacl - 1] = 'n'; tuple = heap_formtuple(rel->rd_att, values, nulls); tablespaceoid = simple_heap_insert(rel, tuple); CatalogUpdateIndexes(rel, tuple); heap_freetuple(tuple); /* Record dependency on owner */ recordDependencyOnOwner(TableSpaceRelationId, tablespaceoid, ownerId); /* * Attempt to coerce target directory to safe permissions. If this fails, * it doesn't exist or has the wrong owner. */ if (chmod(location, 0700) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not set permissions on directory \"%s\": %m", location))); /* * Check the target directory is empty. */ if (!directory_is_empty(location)) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("directory \"%s\" is not empty", location))); /* * Create the PG_VERSION file in the target directory. This has several * purposes: to make sure we can write in the directory, to prevent * someone from creating another tablespace pointing at the same directory * (the emptiness check above will fail), and to label tablespace * directories by PG version. */ set_short_version(location); /* * All seems well, create the symlink */ linkloc = (char *) palloc(10 + 10 + 1); sprintf(linkloc, "pg_tblspc/%u", tablespaceoid); if (symlink(location, linkloc) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not create symbolic link \"%s\": %m", linkloc))); /* Record the filesystem change in XLOG */ { xl_tblspc_create_rec xlrec; XLogRecData rdata[2]; xlrec.ts_id = tablespaceoid; rdata[0].data = (char *) &xlrec; rdata[0].len = offsetof(xl_tblspc_create_rec, ts_path); rdata[0].buffer = InvalidBuffer; rdata[0].next = &(rdata[1]); rdata[1].data = (char *) location; rdata[1].len = strlen(location) + 1; rdata[1].buffer = InvalidBuffer; rdata[1].next = NULL; (void) XLogInsert(RM_TBLSPC_ID, XLOG_TBLSPC_CREATE, rdata); } pfree(linkloc); pfree(location); /* We keep the lock on pg_tablespace until commit */ heap_close(rel, NoLock); #else /* !HAVE_SYMLINK */ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("tablespaces are not supported on this platform"))); #endif /* HAVE_SYMLINK */ }
/* * GenerateTypeDependencies: build the dependencies needed for a type * * If rebuild is true, we remove existing dependencies and rebuild them * from scratch. This is needed for ALTER TYPE, and also when replacing * a shell type. We don't remove an existing extension dependency, though. * (That means an extension can't absorb a shell type created in another * extension, nor ALTER a type created by another extension. Also, if it * replaces a free-standing shell type or ALTERs a free-standing type, * that type will become a member of the extension.) */ void GenerateTypeDependencies(Oid typeNamespace, Oid typeObjectId, Oid relationOid, /* only for relation rowtypes */ char relationKind, /* ditto */ Oid owner, Oid inputProcedure, Oid outputProcedure, Oid receiveProcedure, Oid sendProcedure, Oid typmodinProcedure, Oid typmodoutProcedure, Oid analyzeProcedure, Oid elementType, bool isImplicitArray, Oid baseType, Oid typeCollation, Node *defaultExpr, bool rebuild) { ObjectAddress myself, referenced; /* If rebuild, first flush old dependencies, except extension deps */ if (rebuild) { deleteDependencyRecordsFor(TypeRelationId, typeObjectId, true); deleteSharedDependencyRecordsFor(TypeRelationId, typeObjectId, 0); } myself.classId = TypeRelationId; myself.objectId = typeObjectId; myself.objectSubId = 0; /* * Make dependencies on namespace, owner, extension. * * For a relation rowtype (that's not a composite type), we should skip * these because we'll depend on them indirectly through the pg_class * entry. Likewise, skip for implicit arrays since we'll depend on them * through the element type. */ if ((!OidIsValid(relationOid) || relationKind == RELKIND_COMPOSITE_TYPE) && !isImplicitArray) { referenced.classId = NamespaceRelationId; referenced.objectId = typeNamespace; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); recordDependencyOnOwner(TypeRelationId, typeObjectId, owner); recordDependencyOnCurrentExtension(&myself, rebuild); } /* Normal dependencies on the I/O functions */ if (OidIsValid(inputProcedure)) { referenced.classId = ProcedureRelationId; referenced.objectId = inputProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(outputProcedure)) { referenced.classId = ProcedureRelationId; referenced.objectId = outputProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(receiveProcedure)) { referenced.classId = ProcedureRelationId; referenced.objectId = receiveProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(sendProcedure)) { referenced.classId = ProcedureRelationId; referenced.objectId = sendProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(typmodinProcedure)) { referenced.classId = ProcedureRelationId; referenced.objectId = typmodinProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(typmodoutProcedure)) { referenced.classId = ProcedureRelationId; referenced.objectId = typmodoutProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(analyzeProcedure)) { referenced.classId = ProcedureRelationId; referenced.objectId = analyzeProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* * If the type is a rowtype for a relation, mark it as internally * dependent on the relation, *unless* it is a stand-alone composite type * relation. For the latter case, we have to reverse the dependency. * * In the former case, this allows the type to be auto-dropped when the * relation is, and not otherwise. And in the latter, of course we get the * opposite effect. */ if (OidIsValid(relationOid)) { referenced.classId = RelationRelationId; referenced.objectId = relationOid; referenced.objectSubId = 0; if (relationKind != RELKIND_COMPOSITE_TYPE) recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); else recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL); } /* * If the type is an implicitly-created array type, mark it as internally * dependent on the element type. Otherwise, if it has an element type, * the dependency is a normal one. */ if (OidIsValid(elementType)) { referenced.classId = TypeRelationId; referenced.objectId = elementType; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, isImplicitArray ? DEPENDENCY_INTERNAL : DEPENDENCY_NORMAL); } /* Normal dependency from a domain to its base type. */ if (OidIsValid(baseType)) { referenced.classId = TypeRelationId; referenced.objectId = baseType; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Normal dependency from a domain to its collation. */ /* We know the default collation is pinned, so don't bother recording it */ if (OidIsValid(typeCollation) && typeCollation != DEFAULT_COLLATION_OID) { referenced.classId = CollationRelationId; referenced.objectId = typeCollation; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Normal dependency on the default expression. */ if (defaultExpr) recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL); }
/* * Create a table space * * Only superusers can create a tablespace. This seems a reasonable restriction * since we're determining the system layout and, anyway, we probably have * root if we're doing this kind of activity */ void CreateTableSpace(CreateTableSpaceStmt *stmt) { Relation rel; Relation filespaceRel; Datum values[Natts_pg_tablespace]; bool nulls[Natts_pg_tablespace]; HeapTuple tuple; Oid tablespaceoid; Oid filespaceoid; Oid ownerId; TablespaceDirNode tablespaceDirNode; ItemPointerData persistentTid; int64 persistentSerialNum; cqContext cqc; cqContext *pcqCtx; /* Must be super user */ if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to create tablespace \"%s\"", stmt->tablespacename), errhint("Must be superuser to create a tablespace."))); /* However, the eventual owner of the tablespace need not be */ if (stmt->owner) ownerId = get_roleid_checked(stmt->owner); else ownerId = GetUserId(); /* * Disallow creation of tablespaces named "pg_xxx"; we reserve this * namespace for system purposes. */ if (!allowSystemTableModsDDL && IsReservedName(stmt->tablespacename)) { ereport(ERROR, (errcode(ERRCODE_RESERVED_NAME), errmsg("unacceptable tablespace name \"%s\"", stmt->tablespacename), errdetail("The prefix \"%s\" is reserved for system tablespaces.", GetReservedPrefix(stmt->tablespacename)))); } /* * Check the specified filespace */ filespaceRel = heap_open(FileSpaceRelationId, RowShareLock); filespaceoid = get_filespace_oid(filespaceRel, stmt->filespacename); heap_close(filespaceRel, NoLock); /* hold lock until commit/abort */ if (!OidIsValid(filespaceoid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("filespace \"%s\" does not exist", stmt->filespacename))); /* * Filespace pg_system is reserved for system use: * - Used for pg_global and pg_default tablespaces only * * Directory layout is slightly different for the system filespace. * Instead of having subdirectories for individual tablespaces instead * the two system tablespaces have specific locations within it: * pg_global : $PG_SYSTEM/global/relfilenode * pg_default : $PG_SYSTEM/base/dboid/relfilenode * * In other words PG_SYSTEM points to the segments "datadir", or in * postgres vocabulary $PGDATA. * */ if (filespaceoid == SYSTEMFILESPACE_OID && !IsBootstrapProcessingMode()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to create tablespace \"%s\"", stmt->tablespacename), errhint("filespace %s is reserved for system use", stmt->filespacename))); /* * Check that there is no other tablespace by this name. (The unique * index would catch this anyway, but might as well give a friendlier * message.) */ if (OidIsValid(get_tablespace_oid(stmt->tablespacename))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("tablespace \"%s\" already exists", stmt->tablespacename))); /* * Insert tuple into pg_tablespace. The purpose of doing this first is to * lock the proposed tablename against other would-be creators. The * insertion will roll back if we find problems below. */ rel = heap_open(TableSpaceRelationId, RowExclusiveLock); pcqCtx = caql_beginscan( caql_addrel(cqclr(&cqc), rel), cql("INSERT INTO pg_tablespace", NULL)); MemSet(nulls, true, sizeof(nulls)); values[Anum_pg_tablespace_spcname - 1] = DirectFunctionCall1(namein, CStringGetDatum(stmt->tablespacename)); values[Anum_pg_tablespace_spcowner - 1] = ObjectIdGetDatum(ownerId); values[Anum_pg_tablespace_spcfsoid - 1] = ObjectIdGetDatum(filespaceoid); nulls[Anum_pg_tablespace_spcname - 1] = false; nulls[Anum_pg_tablespace_spcowner - 1] = false; nulls[Anum_pg_tablespace_spcfsoid - 1] = false; tuple = caql_form_tuple(pcqCtx, values, nulls); /* Keep oids synchonized between master and segments */ if (OidIsValid(stmt->tsoid)) HeapTupleSetOid(tuple, stmt->tsoid); tablespaceoid = caql_insert(pcqCtx, tuple); /* and Update indexes (implicit) */ heap_freetuple(tuple); /* We keep the lock on pg_tablespace until commit */ caql_endscan(pcqCtx); heap_close(rel, NoLock); /* Create the persistent directory for the tablespace */ tablespaceDirNode.tablespace = tablespaceoid; tablespaceDirNode.filespace = filespaceoid; MirroredFileSysObj_TransactionCreateTablespaceDir( &tablespaceDirNode, &persistentTid, &persistentSerialNum); /* * Record dependency on owner * * We do not record the dependency on pg_filespace because we do not track * dependencies between shared objects. Additionally the pg_tablespace * table itself contains the foreign key back to pg_filespace and can be * used to fulfill the same purpose that an entry in pg_shdepend would. */ recordDependencyOnOwner(TableSpaceRelationId, tablespaceoid, ownerId); /* * Create the PG_VERSION file in the target directory. This has several * purposes: to make sure we can write in the directory, to prevent * someone from creating another tablespace pointing at the same directory * (the emptiness check above will fail), and to label tablespace * directories by PG version. */ // set_short_version(sublocation); if (Gp_role == GP_ROLE_DISPATCH) { stmt->tsoid = tablespaceoid; CdbDispatchUtilityStatement((Node *) stmt, DF_CANCEL_ON_ERROR| DF_WITH_SNAPSHOT| DF_NEED_TWO_PHASE, NULL); /* MPP-6929: metadata tracking */ MetaTrackAddObject(TableSpaceRelationId, tablespaceoid, GetUserId(), "CREATE", "TABLESPACE" ); } /* * Force synchronous commit, to minimize the window between creating the * symlink on-disk and marking the transaction committed. It's not great * that there is any window at all, but definitely we don't want to make * it larger than necessary. */ ForceSyncCommit(); }
/* * Guts of language creation. */ static void 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); /* dependency on owner of language */ if (!is_update) recordDependencyOnOwner(myself.classId, myself.objectId, languageOwner); /* 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 */ InvokeObjectAccessHook(OAT_POST_CREATE, LanguageRelationId, myself.objectId, 0); heap_close(rel, RowExclusiveLock); }
/* * ConversionCreate * * Add a new tuple to pg_conversion. */ Oid ConversionCreate(const char *conname, Oid connamespace, Oid conowner, int32 conforencoding, int32 contoencoding, Oid conproc, bool def, Oid newOid) { int i; Relation rel; HeapTuple tup; bool nulls[Natts_pg_conversion]; Datum values[Natts_pg_conversion]; NameData cname; Oid oid; ObjectAddress myself, referenced; cqContext cqc; cqContext *pcqCtx; /* sanity checks */ if (!conname) elog(ERROR, "no conversion name supplied"); /* open pg_conversion */ rel = heap_open(ConversionRelationId, RowExclusiveLock); /* make sure there is no existing conversion of same name */ if (caql_getcount( caql_addrel(cqclr(&cqc), rel), cql("SELECT COUNT(*) FROM pg_conversion " " WHERE conname = :1 " " AND connamespace = :2 ", CStringGetDatum((char *) conname), ObjectIdGetDatum(connamespace)))) { ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("conversion \"%s\" already exists", conname))); } if (def) { /* * make sure there is no existing default <for encoding><to encoding> * pair in this name space */ if (FindDefaultConversion(connamespace, conforencoding, contoencoding)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("default conversion for %s to %s already exists", pg_encoding_to_char(conforencoding), pg_encoding_to_char(contoencoding)))); } pcqCtx = caql_beginscan( caql_addrel(cqclr(&cqc), rel), cql("INSERT INTO pg_conversion", NULL)); /* initialize nulls and values */ for (i = 0; i < Natts_pg_conversion; i++) { nulls[i] = false; values[i] = (Datum) 0; } /* form a tuple */ namestrcpy(&cname, conname); values[Anum_pg_conversion_conname - 1] = NameGetDatum(&cname); values[Anum_pg_conversion_connamespace - 1] = ObjectIdGetDatum(connamespace); values[Anum_pg_conversion_conowner - 1] = ObjectIdGetDatum(conowner); values[Anum_pg_conversion_conforencoding - 1] = Int32GetDatum(conforencoding); values[Anum_pg_conversion_contoencoding - 1] = Int32GetDatum(contoencoding); values[Anum_pg_conversion_conproc - 1] = ObjectIdGetDatum(conproc); values[Anum_pg_conversion_condefault - 1] = BoolGetDatum(def); tup = caql_form_tuple(pcqCtx, values, nulls); if (newOid != 0) HeapTupleSetOid(tup, newOid); /* insert a new tuple */ oid = caql_insert(pcqCtx, tup); /* implicit update of index as well */ Assert(OidIsValid(oid)); myself.classId = ConversionRelationId; myself.objectId = HeapTupleGetOid(tup); myself.objectSubId = 0; /* create dependency on conversion procedure */ referenced.classId = ProcedureRelationId; referenced.objectId = conproc; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* create dependency on namespace */ referenced.classId = NamespaceRelationId; referenced.objectId = connamespace; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* create dependency on owner */ recordDependencyOnOwner(ConversionRelationId, HeapTupleGetOid(tup), conowner); heap_freetuple(tup); caql_endscan(pcqCtx); heap_close(rel, RowExclusiveLock); return oid; }