mirror of
https://github.com/postgres/postgres.git
synced 2025-08-25 20:23:07 +03:00
Implement the DO statement to support execution of PL code without having
to create a function for it. Procedural languages now have an additional entry point, namely a function to execute an inline code block. This seemed a better design than trying to hide the transient-ness of the code from the PL. As of this patch, only plpgsql has an inline handler, but probably people will soon write handlers for the other standard PLs. In passing, remove the long-dead LANCOMPILER option of CREATE LANGUAGE. Petr Jelinek
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.110 2009/06/11 14:48:55 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.111 2009/09/22 23:43:37 tgl Exp $
|
||||
*
|
||||
* DESCRIPTION
|
||||
* These routines take the parse tree and pick out the
|
||||
@@ -1915,3 +1915,110 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
|
||||
|
||||
heap_close(procRel, RowExclusiveLock);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ExecuteDoStmt
|
||||
* Execute inline procedural-language code
|
||||
*/
|
||||
void
|
||||
ExecuteDoStmt(DoStmt *stmt)
|
||||
{
|
||||
InlineCodeBlock *codeblock = makeNode(InlineCodeBlock);
|
||||
ListCell *arg;
|
||||
DefElem *as_item = NULL;
|
||||
DefElem *language_item = NULL;
|
||||
char *language;
|
||||
char *languageName;
|
||||
Oid laninline;
|
||||
HeapTuple languageTuple;
|
||||
Form_pg_language languageStruct;
|
||||
|
||||
/* Process options we got from gram.y */
|
||||
foreach(arg, stmt->args)
|
||||
{
|
||||
DefElem *defel = (DefElem *) lfirst(arg);
|
||||
|
||||
if (strcmp(defel->defname, "as") == 0)
|
||||
{
|
||||
if (as_item)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("conflicting or redundant options")));
|
||||
as_item = defel;
|
||||
}
|
||||
else if (strcmp(defel->defname, "language") == 0)
|
||||
{
|
||||
if (language_item)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("conflicting or redundant options")));
|
||||
language_item = defel;
|
||||
}
|
||||
else
|
||||
elog(ERROR, "option \"%s\" not recognized",
|
||||
defel->defname);
|
||||
}
|
||||
|
||||
if (as_item)
|
||||
codeblock->source_text = strVal(as_item->arg);
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("no inline code specified")));
|
||||
|
||||
/* if LANGUAGE option wasn't specified, use the default language */
|
||||
if (language_item)
|
||||
language = strVal(language_item->arg);
|
||||
else
|
||||
language = default_do_language;
|
||||
|
||||
/* Convert language name to canonical case */
|
||||
languageName = case_translate_language_name(language);
|
||||
|
||||
/* Look up the language and validate permissions */
|
||||
languageTuple = SearchSysCache(LANGNAME,
|
||||
PointerGetDatum(languageName),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(languageTuple))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("language \"%s\" does not exist", languageName),
|
||||
(PLTemplateExists(languageName) ?
|
||||
errhint("Use CREATE LANGUAGE to load the language into the database.") : 0)));
|
||||
|
||||
codeblock->langOid = HeapTupleGetOid(languageTuple);
|
||||
languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
|
||||
|
||||
if (languageStruct->lanpltrusted)
|
||||
{
|
||||
/* if trusted language, need USAGE privilege */
|
||||
AclResult aclresult;
|
||||
|
||||
aclresult = pg_language_aclcheck(codeblock->langOid, GetUserId(),
|
||||
ACL_USAGE);
|
||||
if (aclresult != ACLCHECK_OK)
|
||||
aclcheck_error(aclresult, ACL_KIND_LANGUAGE,
|
||||
NameStr(languageStruct->lanname));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* if untrusted language, must be superuser */
|
||||
if (!superuser())
|
||||
aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_LANGUAGE,
|
||||
NameStr(languageStruct->lanname));
|
||||
}
|
||||
|
||||
/* get the handler function's OID */
|
||||
laninline = languageStruct->laninline;
|
||||
if (!OidIsValid(laninline))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("language \"%s\" does not support inline code execution",
|
||||
NameStr(languageStruct->lanname))));
|
||||
|
||||
ReleaseSysCache(languageTuple);
|
||||
|
||||
/* execute the inline handler */
|
||||
OidFunctionCall1(laninline, PointerGetDatum(codeblock));
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/proclang.c,v 1.86 2009/07/12 17:12:33 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/proclang.c,v 1.87 2009/09/22 23:43:37 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -44,12 +44,14 @@ 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 void create_proc_lang(const char *languageName,
|
||||
Oid languageOwner, Oid handlerOid, Oid valOid, bool trusted);
|
||||
Oid languageOwner, Oid handlerOid, Oid inlineOid,
|
||||
Oid valOid, bool trusted);
|
||||
static PLTemplate *find_language_template(const char *languageName);
|
||||
static void AlterLanguageOwner_internal(HeapTuple tup, Relation rel,
|
||||
Oid newOwnerId);
|
||||
@@ -65,6 +67,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
||||
char *languageName;
|
||||
PLTemplate *pltemplate;
|
||||
Oid handlerOid,
|
||||
inlineOid,
|
||||
valOid;
|
||||
Oid funcrettype;
|
||||
Oid funcargtypes[1];
|
||||
@@ -154,6 +157,44 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
||||
0);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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))
|
||||
{
|
||||
inlineOid = ProcedureCreate(pltemplate->tmplinline,
|
||||
PG_CATALOG_NAMESPACE,
|
||||
false, /* replace */
|
||||
false, /* returnsSet */
|
||||
VOIDOID,
|
||||
ClanguageId,
|
||||
F_FMGR_C_VALIDATOR,
|
||||
pltemplate->tmplinline,
|
||||
pltemplate->tmpllibrary,
|
||||
false, /* isAgg */
|
||||
false, /* isWindowFunc */
|
||||
false, /* security_definer */
|
||||
true, /* isStrict */
|
||||
PROVOLATILE_VOLATILE,
|
||||
buildoidvector(funcargtypes, 1),
|
||||
PointerGetDatum(NULL),
|
||||
PointerGetDatum(NULL),
|
||||
PointerGetDatum(NULL),
|
||||
NIL,
|
||||
PointerGetDatum(NULL),
|
||||
1,
|
||||
0);
|
||||
}
|
||||
}
|
||||
else
|
||||
inlineOid = InvalidOid;
|
||||
|
||||
/*
|
||||
* Likewise for the validator, if required; but we don't care about
|
||||
* its return type.
|
||||
@@ -177,7 +218,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
||||
false, /* isAgg */
|
||||
false, /* isWindowFunc */
|
||||
false, /* security_definer */
|
||||
false, /* isStrict */
|
||||
true, /* isStrict */
|
||||
PROVOLATILE_VOLATILE,
|
||||
buildoidvector(funcargtypes, 1),
|
||||
PointerGetDatum(NULL),
|
||||
@@ -193,8 +234,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
||||
valOid = InvalidOid;
|
||||
|
||||
/* ok, create it */
|
||||
create_proc_lang(languageName, GetUserId(), handlerOid, valOid,
|
||||
pltemplate->tmpltrusted);
|
||||
create_proc_lang(languageName, GetUserId(), handlerOid, inlineOid,
|
||||
valOid, pltemplate->tmpltrusted);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -246,6 +287,16 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
||||
NameListToString(stmt->plhandler))));
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
@@ -257,8 +308,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
||||
valOid = InvalidOid;
|
||||
|
||||
/* ok, create it */
|
||||
create_proc_lang(languageName, GetUserId(), handlerOid, valOid,
|
||||
stmt->pltrusted);
|
||||
create_proc_lang(languageName, GetUserId(), handlerOid, inlineOid,
|
||||
valOid, stmt->pltrusted);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,7 +318,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
||||
*/
|
||||
static void
|
||||
create_proc_lang(const char *languageName,
|
||||
Oid languageOwner, Oid handlerOid, Oid valOid, bool trusted)
|
||||
Oid languageOwner, Oid handlerOid, Oid inlineOid,
|
||||
Oid valOid, bool trusted)
|
||||
{
|
||||
Relation rel;
|
||||
TupleDesc tupDesc;
|
||||
@@ -293,6 +345,7 @@ create_proc_lang(const char *languageName,
|
||||
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;
|
||||
|
||||
@@ -321,6 +374,15 @@ create_proc_lang(const char *languageName,
|
||||
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))
|
||||
{
|
||||
@@ -371,6 +433,11 @@ find_language_template(const char *languageName)
|
||||
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)
|
||||
|
Reference in New Issue
Block a user