mirror of
https://github.com/postgres/postgres.git
synced 2025-04-24 10:47:04 +03:00
This commit improves the dependency registrations by taking advantage of the preliminary work done in 63110c62, to group together the insertion of dependencies of the same type to pg_depend. With the current layer of routines available, and as only dependencies of the same type can be grouped, there are code paths still doing more than one multi-insert when it is necessary to register dependencies of multiple types (constraint and index creation are two cases doing that). While on it, this refactors some of the code to use ObjectAddressSet() when manipulating object addresses. Author: Daniel Gustafsson, Michael Paquier Reviewed-by: Andres Freund, Álvaro Herrera Discussion: https://postgr.es/m/20200807061619.GA23955@paquier.xyz
240 lines
6.8 KiB
C
240 lines
6.8 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* proclang.c
|
|
* PostgreSQL LANGUAGE support code.
|
|
*
|
|
* Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* IDENTIFICATION
|
|
* src/backend/commands/proclang.c
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "access/table.h"
|
|
#include "catalog/catalog.h"
|
|
#include "catalog/dependency.h"
|
|
#include "catalog/indexing.h"
|
|
#include "catalog/objectaccess.h"
|
|
#include "catalog/pg_language.h"
|
|
#include "catalog/pg_namespace.h"
|
|
#include "catalog/pg_proc.h"
|
|
#include "catalog/pg_type.h"
|
|
#include "commands/defrem.h"
|
|
#include "commands/proclang.h"
|
|
#include "miscadmin.h"
|
|
#include "parser/parse_func.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/lsyscache.h"
|
|
#include "utils/rel.h"
|
|
#include "utils/syscache.h"
|
|
|
|
|
|
/*
|
|
* CREATE LANGUAGE
|
|
*/
|
|
ObjectAddress
|
|
CreateProceduralLanguage(CreatePLangStmt *stmt)
|
|
{
|
|
const char *languageName = stmt->plname;
|
|
Oid languageOwner = GetUserId();
|
|
Oid handlerOid,
|
|
inlineOid,
|
|
valOid;
|
|
Oid funcrettype;
|
|
Oid funcargtypes[1];
|
|
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;
|
|
Oid langoid;
|
|
bool is_update;
|
|
ObjectAddress myself,
|
|
referenced;
|
|
ObjectAddresses *addrs;
|
|
|
|
/*
|
|
* Check permission
|
|
*/
|
|
if (!superuser())
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
errmsg("must be superuser to create custom procedural language")));
|
|
|
|
/*
|
|
* Lookup the PL handler function and check that it is of the expected
|
|
* return type
|
|
*/
|
|
Assert(stmt->plhandler);
|
|
handlerOid = LookupFuncName(stmt->plhandler, 0, NULL, false);
|
|
funcrettype = get_func_rettype(handlerOid);
|
|
if (funcrettype != LANGUAGE_HANDLEROID)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
errmsg("function %s must return type %s",
|
|
NameListToString(stmt->plhandler), "language_handler")));
|
|
|
|
/* validate the inline function */
|
|
if (stmt->plinline)
|
|
{
|
|
funcargtypes[0] = INTERNALOID;
|
|
inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes, false);
|
|
/* return value is ignored, so we don't check the type */
|
|
}
|
|
else
|
|
inlineOid = InvalidOid;
|
|
|
|
/* validate the validator function */
|
|
if (stmt->plvalidator)
|
|
{
|
|
funcargtypes[0] = OIDOID;
|
|
valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false);
|
|
/* return value is ignored, so we don't check the type */
|
|
}
|
|
else
|
|
valOid = InvalidOid;
|
|
|
|
/* ok to create it */
|
|
rel = table_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(stmt->pltrusted);
|
|
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))
|
|
{
|
|
Form_pg_language oldform = (Form_pg_language) GETSTRUCT(oldtup);
|
|
|
|
/* There is one; okay to replace it? */
|
|
if (!stmt->replace)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
|
errmsg("language \"%s\" already exists", languageName)));
|
|
|
|
/* This is currently pointless, since we already checked superuser */
|
|
#ifdef NOT_USED
|
|
if (!pg_language_ownercheck(oldform->oid, languageOwner))
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_LANGUAGE,
|
|
languageName);
|
|
#endif
|
|
|
|
/*
|
|
* Do not change existing oid, ownership or permissions. Note
|
|
* dependency-update code below has to agree with this decision.
|
|
*/
|
|
replaces[Anum_pg_language_oid - 1] = false;
|
|
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);
|
|
CatalogTupleUpdate(rel, &tup->t_self, tup);
|
|
|
|
langoid = oldform->oid;
|
|
ReleaseSysCache(oldtup);
|
|
is_update = true;
|
|
}
|
|
else
|
|
{
|
|
/* Creating a new language */
|
|
langoid = GetNewOidWithIndex(rel, LanguageOidIndexId,
|
|
Anum_pg_language_oid);
|
|
values[Anum_pg_language_oid - 1] = ObjectIdGetDatum(langoid);
|
|
tup = heap_form_tuple(tupDesc, values, nulls);
|
|
CatalogTupleInsert(rel, tup);
|
|
is_update = false;
|
|
}
|
|
|
|
/*
|
|
* 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 = langoid;
|
|
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);
|
|
|
|
addrs = new_object_addresses();
|
|
|
|
/* dependency on the PL handler function */
|
|
ObjectAddressSet(referenced, ProcedureRelationId, handlerOid);
|
|
add_exact_object_address(&referenced, addrs);
|
|
|
|
/* dependency on the inline handler function, if any */
|
|
if (OidIsValid(inlineOid))
|
|
{
|
|
ObjectAddressSet(referenced, ProcedureRelationId, inlineOid);
|
|
add_exact_object_address(&referenced, addrs);
|
|
}
|
|
|
|
/* dependency on the validator function, if any */
|
|
if (OidIsValid(valOid))
|
|
{
|
|
ObjectAddressSet(referenced, ProcedureRelationId, valOid);
|
|
add_exact_object_address(&referenced, addrs);
|
|
}
|
|
|
|
record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
|
|
free_object_addresses(addrs);
|
|
|
|
/* Post creation hook for new procedural language */
|
|
InvokeObjectPostCreateHook(LanguageRelationId, myself.objectId, 0);
|
|
|
|
table_close(rel, RowExclusiveLock);
|
|
|
|
return myself;
|
|
}
|
|
|
|
/*
|
|
* get_language_oid - given a language name, look up the OID
|
|
*
|
|
* If missing_ok is false, throw an error if language name not found. If
|
|
* true, just return InvalidOid.
|
|
*/
|
|
Oid
|
|
get_language_oid(const char *langname, bool missing_ok)
|
|
{
|
|
Oid oid;
|
|
|
|
oid = GetSysCacheOid1(LANGNAME, Anum_pg_language_oid,
|
|
CStringGetDatum(langname));
|
|
if (!OidIsValid(oid) && !missing_ok)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
errmsg("language \"%s\" does not exist", langname)));
|
|
return oid;
|
|
}
|