1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-05 23:56:58 +03:00
Tom Lane bb03010b9f Remove the "opaque" pseudo-type and associated compatibility hacks.
A long time ago, it was necessary to declare datatype I/O functions,
triggers, and language handler support functions in a very type-unsafe
way involving a single pseudo-type "opaque".  We got rid of those
conventions in 7.3, but there was still support in various places to
automatically convert such functions to the modern declaration style,
to be able to transparently re-load dumps from pre-7.3 servers.
It seems unnecessary to continue to support that anymore, so take out
the hacks; whereupon the "opaque" pseudo-type itself is no longer
needed and can be dropped.

This is part of a group of patches removing various server-side kluges
for transparently upgrading pre-8.0 dump files.  Since we've had few
complaints about dropping pg_dump's support for dumping from pre-8.0
servers (commit 64f3524e2), it seems okay to now remove these kluges.

Discussion: https://postgr.es/m/4110.1583255415@sss.pgh.pa.us
2020-03-05 15:48:56 -05:00

262 lines
7.3 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;
/*
* 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);
/* 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);
table_close(rel, RowExclusiveLock);
return myself;
}
/*
* Guts of language dropping.
*/
void
DropProceduralLanguageById(Oid langOid)
{
Relation rel;
HeapTuple langTup;
rel = table_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);
table_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;
}