mirror of
https://github.com/zebrajr/postgres.git
synced 2025-12-06 12:20:15 +01:00
Previously tables declared WITH OIDS, including a significant fraction of the catalog tables, stored the oid column not as a normal column, but as part of the tuple header. This special column was not shown by default, which was somewhat odd, as it's often (consider e.g. pg_class.oid) one of the more important parts of a row. Neither pg_dump nor COPY included the contents of the oid column by default. The fact that the oid column was not an ordinary column necessitated a significant amount of special case code to support oid columns. That already was painful for the existing, but upcoming work aiming to make table storage pluggable, would have required expanding and duplicating that "specialness" significantly. WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0). Remove it. Removing includes: - CREATE TABLE and ALTER TABLE syntax for declaring the table to be WITH OIDS has been removed (WITH (oids[ = true]) will error out) - pg_dump does not support dumping tables declared WITH OIDS and will issue a warning when dumping one (and ignore the oid column). - restoring an pg_dump archive with pg_restore will warn when restoring a table with oid contents (and ignore the oid column) - COPY will refuse to load binary dump that includes oids. - pg_upgrade will error out when encountering tables declared WITH OIDS, they have to be altered to remove the oid column first. - Functionality to access the oid of the last inserted row (like plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed. The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false) for CREATE TABLE) is still supported. While that requires a bit of support code, it seems unnecessary to break applications / dumps that do not use oids, and are explicit about not using them. The biggest user of WITH OID columns was postgres' catalog. This commit changes all 'magic' oid columns to be columns that are normally declared and stored. To reduce unnecessary query breakage all the newly added columns are still named 'oid', even if a table's column naming scheme would indicate 'reloid' or such. This obviously requires adapting a lot code, mostly replacing oid access via HeapTupleGetOid() with access to the underlying Form_pg_*->oid column. The bootstrap process now assigns oids for all oid columns in genbki.pl that do not have an explicit value (starting at the largest oid previously used), only oids assigned later by oids will be above FirstBootstrapObjectId. As the oid column now is a normal column the special bootstrap syntax for oids has been removed. Oids are not automatically assigned during insertion anymore, all backend code explicitly assigns oids with GetNewOidWithIndex(). For the rare case that insertions into the catalog via SQL are called for the new pg_nextoid() function can be used (which only works on catalog tables). The fact that oid columns on system tables are now normal columns means that they will be included in the set of columns expanded by * (i.e. SELECT * FROM pg_class will now include the table's oid, previously it did not). It'd not technically be hard to hide oid column by default, but that'd mean confusing behavior would either have to be carried forward forever, or it'd cause breakage down the line. While it's not unlikely that further adjustments are needed, the scope/invasiveness of the patch makes it worthwhile to get merge this now. It's painful to maintain externally, too complicated to commit after the code code freeze, and a dependency of a number of other patches. Catversion bump, for obvious reasons. Author: Andres Freund, with contributions by John Naylor Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
570 lines
16 KiB
C
570 lines
16 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* proclang.c
|
|
* PostgreSQL PROCEDURAL LANGUAGE support code.
|
|
*
|
|
* Portions Copyright (c) 1996-2018, 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/genam.h"
|
|
#include "access/heapam.h"
|
|
#include "access/htup_details.h"
|
|
#include "catalog/catalog.h"
|
|
#include "catalog/dependency.h"
|
|
#include "catalog/indexing.h"
|
|
#include "catalog/objectaccess.h"
|
|
#include "catalog/pg_authid.h"
|
|
#include "catalog/pg_language.h"
|
|
#include "catalog/pg_namespace.h"
|
|
#include "catalog/pg_pltemplate.h"
|
|
#include "catalog/pg_proc.h"
|
|
#include "catalog/pg_type.h"
|
|
#include "commands/dbcommands.h"
|
|
#include "commands/defrem.h"
|
|
#include "commands/proclang.h"
|
|
#include "miscadmin.h"
|
|
#include "parser/parse_func.h"
|
|
#include "parser/parser.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/fmgroids.h"
|
|
#include "utils/lsyscache.h"
|
|
#include "utils/rel.h"
|
|
#include "utils/syscache.h"
|
|
#include "utils/tqual.h"
|
|
|
|
|
|
typedef struct
|
|
{
|
|
bool tmpltrusted; /* trusted? */
|
|
bool tmpldbacreate; /* db owner allowed to create? */
|
|
char *tmplhandler; /* name of handler function */
|
|
char *tmplinline; /* name of anonymous-block handler, or NULL */
|
|
char *tmplvalidator; /* name of validator function, or NULL */
|
|
char *tmpllibrary; /* path of shared library */
|
|
} PLTemplate;
|
|
|
|
static ObjectAddress create_proc_lang(const char *languageName, bool replace,
|
|
Oid languageOwner, Oid handlerOid, Oid inlineOid,
|
|
Oid valOid, bool trusted);
|
|
static PLTemplate *find_language_template(const char *languageName);
|
|
|
|
/* ---------------------------------------------------------------------
|
|
* CREATE PROCEDURAL LANGUAGE
|
|
* ---------------------------------------------------------------------
|
|
*/
|
|
ObjectAddress
|
|
CreateProceduralLanguage(CreatePLangStmt *stmt)
|
|
{
|
|
PLTemplate *pltemplate;
|
|
ObjectAddress tmpAddr;
|
|
Oid handlerOid,
|
|
inlineOid,
|
|
valOid;
|
|
Oid funcrettype;
|
|
Oid funcargtypes[1];
|
|
|
|
/*
|
|
* If we have template information for the language, ignore the supplied
|
|
* parameters (if any) and use the template information.
|
|
*/
|
|
if ((pltemplate = find_language_template(stmt->plname)) != NULL)
|
|
{
|
|
List *funcname;
|
|
|
|
/*
|
|
* Give a notice if we are ignoring supplied parameters.
|
|
*/
|
|
if (stmt->plhandler)
|
|
ereport(NOTICE,
|
|
(errmsg("using pg_pltemplate information instead of CREATE LANGUAGE parameters")));
|
|
|
|
/*
|
|
* Check permission
|
|
*/
|
|
if (!superuser())
|
|
{
|
|
if (!pltemplate->tmpldbacreate)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
errmsg("must be superuser to create procedural language \"%s\"",
|
|
stmt->plname)));
|
|
if (!pg_database_ownercheck(MyDatabaseId, GetUserId()))
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_DATABASE,
|
|
get_database_name(MyDatabaseId));
|
|
}
|
|
|
|
/*
|
|
* Find or create the handler function, which we force to be in the
|
|
* pg_catalog schema. If already present, it must have the correct
|
|
* return type.
|
|
*/
|
|
funcname = SystemFuncName(pltemplate->tmplhandler);
|
|
handlerOid = LookupFuncName(funcname, 0, funcargtypes, true);
|
|
if (OidIsValid(handlerOid))
|
|
{
|
|
funcrettype = get_func_rettype(handlerOid);
|
|
if (funcrettype != LANGUAGE_HANDLEROID)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
errmsg("function %s must return type %s",
|
|
NameListToString(funcname), "language_handler")));
|
|
}
|
|
else
|
|
{
|
|
tmpAddr = ProcedureCreate(pltemplate->tmplhandler,
|
|
PG_CATALOG_NAMESPACE,
|
|
false, /* replace */
|
|
false, /* returnsSet */
|
|
LANGUAGE_HANDLEROID,
|
|
BOOTSTRAP_SUPERUSERID,
|
|
ClanguageId,
|
|
F_FMGR_C_VALIDATOR,
|
|
pltemplate->tmplhandler,
|
|
pltemplate->tmpllibrary,
|
|
PROKIND_FUNCTION,
|
|
false, /* security_definer */
|
|
false, /* isLeakProof */
|
|
false, /* isStrict */
|
|
PROVOLATILE_VOLATILE,
|
|
PROPARALLEL_UNSAFE,
|
|
buildoidvector(funcargtypes, 0),
|
|
PointerGetDatum(NULL),
|
|
PointerGetDatum(NULL),
|
|
PointerGetDatum(NULL),
|
|
NIL,
|
|
PointerGetDatum(NULL),
|
|
PointerGetDatum(NULL),
|
|
1,
|
|
0);
|
|
handlerOid = tmpAddr.objectId;
|
|
}
|
|
|
|
/*
|
|
* Likewise for the anonymous block handler, if required; but we don't
|
|
* care about its return type.
|
|
*/
|
|
if (pltemplate->tmplinline)
|
|
{
|
|
funcname = SystemFuncName(pltemplate->tmplinline);
|
|
funcargtypes[0] = INTERNALOID;
|
|
inlineOid = LookupFuncName(funcname, 1, funcargtypes, true);
|
|
if (!OidIsValid(inlineOid))
|
|
{
|
|
tmpAddr = ProcedureCreate(pltemplate->tmplinline,
|
|
PG_CATALOG_NAMESPACE,
|
|
false, /* replace */
|
|
false, /* returnsSet */
|
|
VOIDOID,
|
|
BOOTSTRAP_SUPERUSERID,
|
|
ClanguageId,
|
|
F_FMGR_C_VALIDATOR,
|
|
pltemplate->tmplinline,
|
|
pltemplate->tmpllibrary,
|
|
PROKIND_FUNCTION,
|
|
false, /* security_definer */
|
|
false, /* isLeakProof */
|
|
true, /* isStrict */
|
|
PROVOLATILE_VOLATILE,
|
|
PROPARALLEL_UNSAFE,
|
|
buildoidvector(funcargtypes, 1),
|
|
PointerGetDatum(NULL),
|
|
PointerGetDatum(NULL),
|
|
PointerGetDatum(NULL),
|
|
NIL,
|
|
PointerGetDatum(NULL),
|
|
PointerGetDatum(NULL),
|
|
1,
|
|
0);
|
|
inlineOid = tmpAddr.objectId;
|
|
}
|
|
}
|
|
else
|
|
inlineOid = InvalidOid;
|
|
|
|
/*
|
|
* Likewise for the validator, if required; but we don't care about
|
|
* its return type.
|
|
*/
|
|
if (pltemplate->tmplvalidator)
|
|
{
|
|
funcname = SystemFuncName(pltemplate->tmplvalidator);
|
|
funcargtypes[0] = OIDOID;
|
|
valOid = LookupFuncName(funcname, 1, funcargtypes, true);
|
|
if (!OidIsValid(valOid))
|
|
{
|
|
tmpAddr = ProcedureCreate(pltemplate->tmplvalidator,
|
|
PG_CATALOG_NAMESPACE,
|
|
false, /* replace */
|
|
false, /* returnsSet */
|
|
VOIDOID,
|
|
BOOTSTRAP_SUPERUSERID,
|
|
ClanguageId,
|
|
F_FMGR_C_VALIDATOR,
|
|
pltemplate->tmplvalidator,
|
|
pltemplate->tmpllibrary,
|
|
PROKIND_FUNCTION,
|
|
false, /* security_definer */
|
|
false, /* isLeakProof */
|
|
true, /* isStrict */
|
|
PROVOLATILE_VOLATILE,
|
|
PROPARALLEL_UNSAFE,
|
|
buildoidvector(funcargtypes, 1),
|
|
PointerGetDatum(NULL),
|
|
PointerGetDatum(NULL),
|
|
PointerGetDatum(NULL),
|
|
NIL,
|
|
PointerGetDatum(NULL),
|
|
PointerGetDatum(NULL),
|
|
1,
|
|
0);
|
|
valOid = tmpAddr.objectId;
|
|
}
|
|
}
|
|
else
|
|
valOid = InvalidOid;
|
|
|
|
/* ok, create it */
|
|
return create_proc_lang(stmt->plname, stmt->replace, GetUserId(),
|
|
handlerOid, inlineOid,
|
|
valOid, pltemplate->tmpltrusted);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* No template, so use the provided information. If there's no
|
|
* handler clause, the user is trying to rely on a template that we
|
|
* don't have, so complain accordingly.
|
|
*/
|
|
if (!stmt->plhandler)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
errmsg("unsupported language \"%s\"",
|
|
stmt->plname),
|
|
errhint("The supported languages are listed in the pg_pltemplate system catalog.")));
|
|
|
|
/*
|
|
* 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
|
|
*/
|
|
handlerOid = LookupFuncName(stmt->plhandler, 0, funcargtypes, false);
|
|
funcrettype = get_func_rettype(handlerOid);
|
|
if (funcrettype != LANGUAGE_HANDLEROID)
|
|
{
|
|
/*
|
|
* We allow OPAQUE just so we can load old dump files. When we
|
|
* see a handler function declared OPAQUE, change it to
|
|
* LANGUAGE_HANDLER. (This is probably obsolete and removable?)
|
|
*/
|
|
if (funcrettype == OPAQUEOID)
|
|
{
|
|
ereport(WARNING,
|
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
errmsg("changing return type of function %s from %s to %s",
|
|
NameListToString(stmt->plhandler),
|
|
"opaque", "language_handler")));
|
|
SetFunctionReturnType(handlerOid, LANGUAGE_HANDLEROID);
|
|
}
|
|
else
|
|
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, create it */
|
|
return create_proc_lang(stmt->plname, stmt->replace, GetUserId(),
|
|
handlerOid, inlineOid,
|
|
valOid, stmt->pltrusted);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Guts of language creation.
|
|
*/
|
|
static ObjectAddress
|
|
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;
|
|
Oid langoid;
|
|
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))
|
|
{
|
|
Form_pg_language oldform = (Form_pg_language) GETSTRUCT(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(oldform->oid, languageOwner))
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_LANGUAGE,
|
|
languageName);
|
|
|
|
/*
|
|
* 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);
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/*
|
|
* 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,
|
|
CStringGetDatum(languageName));
|
|
scan = systable_beginscan(rel, PLTemplateNameIndexId, true,
|
|
NULL, 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;
|
|
}
|
|
|
|
|
|
/*
|
|
* This just returns true if we have a valid template for a given language
|
|
*/
|
|
bool
|
|
PLTemplateExists(const char *languageName)
|
|
{
|
|
return (find_language_template(languageName) != NULL);
|
|
}
|
|
|
|
/*
|
|
* Guts of language dropping.
|
|
*/
|
|
void
|
|
DropProceduralLanguageById(Oid langOid)
|
|
{
|
|
Relation rel;
|
|
HeapTuple langTup;
|
|
|
|
rel = heap_open(LanguageRelationId, RowExclusiveLock);
|
|
|
|
langTup = SearchSysCache1(LANGOID, ObjectIdGetDatum(langOid));
|
|
if (!HeapTupleIsValid(langTup)) /* should not happen */
|
|
elog(ERROR, "cache lookup failed for language %u", langOid);
|
|
|
|
CatalogTupleDelete(rel, &langTup->t_self);
|
|
|
|
ReleaseSysCache(langTup);
|
|
|
|
heap_close(rel, RowExclusiveLock);
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
}
|