blob: 64e669810c33138532294944f9685d88d19384cb [file] [log] [blame]
/*-------------------------------------------------------------------------
*
* proclang.c
* PostgreSQL PROCEDURAL LANGUAGE support code.
*
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/proclang.c,v 1.69 2006/10/04 00:29:51 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "catalog/catquery.h"
#include "catalog/dependency.h"
#include "catalog/indexing.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/defrem.h"
#include "commands/proclang.h"
#include "miscadmin.h"
#include "parser/gramparse.h"
#include "parser/parse_func.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "cdb/cdbvars.h"
#include "cdb/cdbdisp.h"
typedef struct
{
bool tmpltrusted; /* trusted? */
char *tmplhandler; /* name of handler function */
char *tmplvalidator; /* name of validator function, or NULL */
char *tmpllibrary; /* path of shared library */
} PLTemplate;
static void create_proc_lang(const char *languageName,
Oid handlerOid, Oid valOid, bool trusted, Oid *plangOid);
static PLTemplate *find_language_template(const char *languageName);
/* ---------------------------------------------------------------------
* CREATE PROCEDURAL LANGUAGE
* ---------------------------------------------------------------------
*/
void
CreateProceduralLanguage(CreatePLangStmt *stmt)
{
char *languageName;
PLTemplate *pltemplate;
Oid handlerOid,
valOid;
Oid funcrettype;
Oid funcargtypes[1];
/*
* Check permission
*/
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to create procedural language"),
errOmitLocation(true)));
/*
* Translate the language name and check that this language doesn't
* already exist
*/
languageName = case_translate_language_name(stmt->plname);
if (caql_getcount(
NULL,
cql("SELECT COUNT(*) FROM pg_language "
" WHERE lanname = :1 ",
PointerGetDatum(languageName))))
{
/*
* MPP-7563: special case plpgsql to omit a notice if it already exists
* rather than an error. This allows us to install plpgsql by default
* while allowing it to be dropped and not create issues for
* dump/restore. This should be phased out in a later releases if/when
* plpgsql becomes a true internal language that can not be dropped.
*
* Note: hardcoding this on the name is semi-safe since we would ignore
* any handler functions anyways since plpgsql exists in pg_pltemplate.
* Alternatively this logic could be extended to apply to all languages
* in pg_pltemplate.
*/
if (strcmp(languageName, "plpgsql") == 0)
{
ereport(NOTICE,
(errmsg("language \"plpgsql\" already exists, skipping")));
return;
}
else
{
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("language \"%s\" already exists", languageName),
errOmitLocation(true)));
}
}
/*
* If we have template information for the language, ignore the supplied
* parameters (if any) and use the template information.
*/
if ((pltemplate = find_language_template(languageName)) != NULL)
{
List *funcname;
/*
* Give a notice if we are ignoring supplied parameters.
*/
if (stmt->plhandler)
if (Gp_role != GP_ROLE_EXECUTE)
ereport(NOTICE,
(errmsg("using pg_pltemplate information instead of "
"CREATE LANGUAGE parameters"),
errOmitLocation(true)));
/*
* 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 \"language_handler\"",
NameListToString(funcname)),
errOmitLocation(true)));
}
else
{
handlerOid = ProcedureCreate(pltemplate->tmplhandler,
PG_CATALOG_NAMESPACE,
false, /* replace */
false, /* returnsSet */
LANGUAGE_HANDLEROID,
ClanguageId,
F_FMGR_C_VALIDATOR,
InvalidOid, /* describeFuncOid */
pltemplate->tmplhandler,
pltemplate->tmpllibrary,
false, /* isAgg */
false, /* isWin */
false, /* security_definer */
false, /* isStrict */
PROVOLATILE_VOLATILE,
buildoidvector(funcargtypes, 0),
PointerGetDatum(NULL),
PointerGetDatum(NULL),
PointerGetDatum(NULL),
PRODATAACCESS_NONE,
stmt->plhandlerOid);
}
/*
* 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))
{
valOid = ProcedureCreate(pltemplate->tmplvalidator,
PG_CATALOG_NAMESPACE,
false, /* replace */
false, /* returnsSet */
VOIDOID,
ClanguageId,
F_FMGR_C_VALIDATOR,
InvalidOid, /* describeFuncOid */
pltemplate->tmplvalidator,
pltemplate->tmpllibrary,
false, /* isAgg */
false, /* isWin */
false, /* security_definer */
false, /* isStrict */
PROVOLATILE_IMMUTABLE,
buildoidvector(funcargtypes, 1),
PointerGetDatum(NULL),
PointerGetDatum(NULL),
PointerGetDatum(NULL),
PRODATAACCESS_NONE,
stmt->plvalidatorOid);
}
}
else
valOid = InvalidOid;
/* ok, create it */
create_proc_lang(languageName, handlerOid, valOid,
pltemplate->tmpltrusted, &(stmt->plangOid));
}
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\"",
languageName),
errhint("The supported languages are listed in the pg_pltemplate system catalog."),
errOmitLocation(true)));
/*
* 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)
{
if (Gp_role != GP_ROLE_EXECUTE)
ereport(WARNING,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("changing return type of function %s from \"opaque\" to \"language_handler\"",
NameListToString(stmt->plhandler)),
errOmitLocation(true)));
SetFunctionReturnType(handlerOid, LANGUAGE_HANDLEROID);
}
else
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("function %s must return type \"language_handler\"",
NameListToString(stmt->plhandler)),
errOmitLocation(true)));
}
/* 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 */
create_proc_lang(languageName, handlerOid, valOid, stmt->pltrusted, &(stmt->plangOid));
}
}
/*
* Guts of language creation.
*/
static void
create_proc_lang(const char *languageName,
Oid handlerOid, Oid valOid, bool trusted, Oid *plangoid)
{
Datum values[Natts_pg_language];
bool nulls[Natts_pg_language];
NameData langname;
HeapTuple tup;
cqContext *pcqCtx;
ObjectAddress myself,
referenced;
/*
* Insert the new language into pg_language
*/
pcqCtx = caql_beginscan(
NULL,
cql("INSERT INTO pg_language",
NULL));
memset(values, 0, sizeof(values));
memset(nulls, false, sizeof(nulls));
namestrcpy(&langname, languageName);
values[Anum_pg_language_lanname - 1] = NameGetDatum(&langname);
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_lanvalidator - 1] = ObjectIdGetDatum(valOid);
nulls[Anum_pg_language_lanacl - 1] = true;
tup = caql_form_tuple(pcqCtx, values, nulls);
/* Keep oids synchronized between master and segments */
if (OidIsValid(*plangoid))
HeapTupleSetOid(tup, *plangoid);
*plangoid = caql_insert(pcqCtx, tup); /* implicit update of index as well*/
/*
* Create dependencies for language
*/
myself.classId = LanguageRelationId;
myself.objectId = HeapTupleGetOid(tup);
myself.objectSubId = 0;
/* dependency on the PL handler function */
referenced.classId = ProcedureRelationId;
referenced.objectId = handlerOid;
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);
}
caql_endscan(pcqCtx);
}
/*
* 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;
HeapTuple tup;
cqContext cqc;
rel = heap_open(PLTemplateRelationId, AccessShareLock);
tup = caql_getfirst(
caql_addrel(cqclr(&cqc), rel),
cql("SELECT * FROM pg_pltemplate "
" WHERE tmplname = :1 ",
CStringGetDatum((char *) languageName)));
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;
/* 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 =
DatumGetCString(DirectFunctionCall1(textout, datum));
datum = heap_getattr(tup, Anum_pg_pltemplate_tmplvalidator,
RelationGetDescr(rel), &isnull);
if (!isnull)
result->tmplvalidator =
DatumGetCString(DirectFunctionCall1(textout, datum));
datum = heap_getattr(tup, Anum_pg_pltemplate_tmpllibrary,
RelationGetDescr(rel), &isnull);
if (!isnull)
result->tmpllibrary =
DatumGetCString(DirectFunctionCall1(textout, datum));
/* Ignore template if handler or library info is missing */
if (!result->tmplhandler || !result->tmpllibrary)
result = NULL;
}
else
result = NULL;
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);
}
/* ---------------------------------------------------------------------
* DROP PROCEDURAL LANGUAGE
* ---------------------------------------------------------------------
*/
void
DropProceduralLanguage(DropPLangStmt *stmt)
{
char *languageName;
int fetchCount;
Oid langOid;
ObjectAddress object;
/*
* Check permission
*/
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to drop procedural language"),
errOmitLocation(true)));
/*
* Translate the language name, check that the language exists
*/
languageName = case_translate_language_name(stmt->plname);
langOid = caql_getoid_plus(
NULL,
&fetchCount,
NULL,
cql("SELECT oid FROM pg_language "
" WHERE lanname = :1 ",
CStringGetDatum(languageName)));
if (0 == fetchCount)
{
if (!stmt->missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("language \"%s\" does not exist", languageName),
errOmitLocation(true)));
else
ereport(NOTICE,
(errmsg("language \"%s\" does not exist, skipping",
languageName),
errOmitLocation(true)));
return;
}
object.classId = LanguageRelationId;
object.objectId = langOid;
object.objectSubId = 0;
/*
* Do the deletion
*/
performDeletion(&object, stmt->behavior);
}
/*
* Guts of language dropping.
*/
void
DropProceduralLanguageById(Oid langOid)
{
if (!caql_getcount(
NULL,
cql("DELETE FROM pg_language "
" WHERE oid = :1 ",
ObjectIdGetDatum(langOid))))
{
/* should not happen */
elog(ERROR, "cache lookup failed for language %u", langOid);
}
}
/*
* Rename language
*/
void
RenameLanguage(const char *oldname, const char *newname)
{
HeapTuple tup;
Relation rel;
cqContext cqc2;
cqContext cqc;
cqContext *pcqCtx;
/* Translate both names for consistency with CREATE */
oldname = case_translate_language_name(oldname);
newname = case_translate_language_name(newname);
rel = heap_open(LanguageRelationId, RowExclusiveLock);
pcqCtx = caql_addrel(cqclr(&cqc), rel);
tup = caql_getfirst(
pcqCtx,
cql("SELECT * FROM pg_language "
" WHERE lanname = :1 "
" FOR UPDATE ",
CStringGetDatum((char *) oldname)));
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("language \"%s\" does not exist", oldname),
errOmitLocation(true)));
/* make sure the new name doesn't exist */
if (caql_getcount(
caql_addrel(cqclr(&cqc2), rel),
cql("SELECT COUNT(*) FROM pg_language "
" WHERE lanname = :1 ",
CStringGetDatum((char *) newname))))
{
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("language \"%s\" already exists", newname),
errOmitLocation(true)));
}
/* must be superuser, since we do not have owners for PLs */
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to rename procedural language"),
errOmitLocation(true)));
/* rename */
namestrcpy(&(((Form_pg_language) GETSTRUCT(tup))->lanname), newname);
caql_update_current(pcqCtx, tup); /* implicit update of index as well */
heap_close(rel, NoLock);
heap_freetuple(tup);
}