Header And Logo

PostgreSQL
| The world's most advanced open source database.

proclang.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * proclang.c
00004  *    PostgreSQL PROCEDURAL LANGUAGE support code.
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  * Portions Copyright (c) 1994, Regents of the University of California
00008  *
00009  * IDENTIFICATION
00010  *    src/backend/commands/proclang.c
00011  *
00012  *-------------------------------------------------------------------------
00013  */
00014 #include "postgres.h"
00015 
00016 #include "access/genam.h"
00017 #include "access/heapam.h"
00018 #include "access/htup_details.h"
00019 #include "catalog/dependency.h"
00020 #include "catalog/indexing.h"
00021 #include "catalog/objectaccess.h"
00022 #include "catalog/pg_authid.h"
00023 #include "catalog/pg_language.h"
00024 #include "catalog/pg_namespace.h"
00025 #include "catalog/pg_pltemplate.h"
00026 #include "catalog/pg_proc.h"
00027 #include "catalog/pg_proc_fn.h"
00028 #include "catalog/pg_type.h"
00029 #include "commands/dbcommands.h"
00030 #include "commands/defrem.h"
00031 #include "commands/proclang.h"
00032 #include "miscadmin.h"
00033 #include "parser/parse_func.h"
00034 #include "parser/parser.h"
00035 #include "utils/acl.h"
00036 #include "utils/builtins.h"
00037 #include "utils/fmgroids.h"
00038 #include "utils/lsyscache.h"
00039 #include "utils/rel.h"
00040 #include "utils/syscache.h"
00041 #include "utils/tqual.h"
00042 
00043 
00044 typedef struct
00045 {
00046     bool        tmpltrusted;    /* trusted? */
00047     bool        tmpldbacreate;  /* db owner allowed to create? */
00048     char       *tmplhandler;    /* name of handler function */
00049     char       *tmplinline;     /* name of anonymous-block handler, or NULL */
00050     char       *tmplvalidator;  /* name of validator function, or NULL */
00051     char       *tmpllibrary;    /* path of shared library */
00052 } PLTemplate;
00053 
00054 static Oid create_proc_lang(const char *languageName, bool replace,
00055                             Oid languageOwner, Oid handlerOid, Oid inlineOid,
00056                             Oid valOid, bool trusted);
00057 static PLTemplate *find_language_template(const char *languageName);
00058 
00059 /* ---------------------------------------------------------------------
00060  * CREATE PROCEDURAL LANGUAGE
00061  * ---------------------------------------------------------------------
00062  */
00063 Oid
00064 CreateProceduralLanguage(CreatePLangStmt *stmt)
00065 {
00066     PLTemplate *pltemplate;
00067     Oid         handlerOid,
00068                 inlineOid,
00069                 valOid;
00070     Oid         funcrettype;
00071     Oid         funcargtypes[1];
00072 
00073     /*
00074      * If we have template information for the language, ignore the supplied
00075      * parameters (if any) and use the template information.
00076      */
00077     if ((pltemplate = find_language_template(stmt->plname)) != NULL)
00078     {
00079         List       *funcname;
00080 
00081         /*
00082          * Give a notice if we are ignoring supplied parameters.
00083          */
00084         if (stmt->plhandler)
00085             ereport(NOTICE,
00086                     (errmsg("using pg_pltemplate information instead of CREATE LANGUAGE parameters")));
00087 
00088         /*
00089          * Check permission
00090          */
00091         if (!superuser())
00092         {
00093             if (!pltemplate->tmpldbacreate)
00094                 ereport(ERROR,
00095                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00096                          errmsg("must be superuser to create procedural language \"%s\"",
00097                                 stmt->plname)));
00098             if (!pg_database_ownercheck(MyDatabaseId, GetUserId()))
00099                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
00100                                get_database_name(MyDatabaseId));
00101         }
00102 
00103         /*
00104          * Find or create the handler function, which we force to be in the
00105          * pg_catalog schema.  If already present, it must have the correct
00106          * return type.
00107          */
00108         funcname = SystemFuncName(pltemplate->tmplhandler);
00109         handlerOid = LookupFuncName(funcname, 0, funcargtypes, true);
00110         if (OidIsValid(handlerOid))
00111         {
00112             funcrettype = get_func_rettype(handlerOid);
00113             if (funcrettype != LANGUAGE_HANDLEROID)
00114                 ereport(ERROR,
00115                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
00116                   errmsg("function %s must return type \"language_handler\"",
00117                          NameListToString(funcname))));
00118         }
00119         else
00120         {
00121             handlerOid = ProcedureCreate(pltemplate->tmplhandler,
00122                                          PG_CATALOG_NAMESPACE,
00123                                          false, /* replace */
00124                                          false, /* returnsSet */
00125                                          LANGUAGE_HANDLEROID,
00126                                          BOOTSTRAP_SUPERUSERID,
00127                                          ClanguageId,
00128                                          F_FMGR_C_VALIDATOR,
00129                                          pltemplate->tmplhandler,
00130                                          pltemplate->tmpllibrary,
00131                                          false, /* isAgg */
00132                                          false, /* isWindowFunc */
00133                                          false, /* security_definer */
00134                                          false, /* isLeakProof */
00135                                          false, /* isStrict */
00136                                          PROVOLATILE_VOLATILE,
00137                                          buildoidvector(funcargtypes, 0),
00138                                          PointerGetDatum(NULL),
00139                                          PointerGetDatum(NULL),
00140                                          PointerGetDatum(NULL),
00141                                          NIL,
00142                                          PointerGetDatum(NULL),
00143                                          1,
00144                                          0);
00145         }
00146 
00147         /*
00148          * Likewise for the anonymous block handler, if required; but we don't
00149          * care about its return type.
00150          */
00151         if (pltemplate->tmplinline)
00152         {
00153             funcname = SystemFuncName(pltemplate->tmplinline);
00154             funcargtypes[0] = INTERNALOID;
00155             inlineOid = LookupFuncName(funcname, 1, funcargtypes, true);
00156             if (!OidIsValid(inlineOid))
00157             {
00158                 inlineOid = ProcedureCreate(pltemplate->tmplinline,
00159                                             PG_CATALOG_NAMESPACE,
00160                                             false,      /* replace */
00161                                             false,      /* returnsSet */
00162                                             VOIDOID,
00163                                             BOOTSTRAP_SUPERUSERID,
00164                                             ClanguageId,
00165                                             F_FMGR_C_VALIDATOR,
00166                                             pltemplate->tmplinline,
00167                                             pltemplate->tmpllibrary,
00168                                             false,      /* isAgg */
00169                                             false,      /* isWindowFunc */
00170                                             false,      /* security_definer */
00171                                             false,      /* isLeakProof */
00172                                             true,       /* isStrict */
00173                                             PROVOLATILE_VOLATILE,
00174                                             buildoidvector(funcargtypes, 1),
00175                                             PointerGetDatum(NULL),
00176                                             PointerGetDatum(NULL),
00177                                             PointerGetDatum(NULL),
00178                                             NIL,
00179                                             PointerGetDatum(NULL),
00180                                             1,
00181                                             0);
00182             }
00183         }
00184         else
00185             inlineOid = InvalidOid;
00186 
00187         /*
00188          * Likewise for the validator, if required; but we don't care about
00189          * its return type.
00190          */
00191         if (pltemplate->tmplvalidator)
00192         {
00193             funcname = SystemFuncName(pltemplate->tmplvalidator);
00194             funcargtypes[0] = OIDOID;
00195             valOid = LookupFuncName(funcname, 1, funcargtypes, true);
00196             if (!OidIsValid(valOid))
00197             {
00198                 valOid = ProcedureCreate(pltemplate->tmplvalidator,
00199                                          PG_CATALOG_NAMESPACE,
00200                                          false, /* replace */
00201                                          false, /* returnsSet */
00202                                          VOIDOID,
00203                                          BOOTSTRAP_SUPERUSERID,
00204                                          ClanguageId,
00205                                          F_FMGR_C_VALIDATOR,
00206                                          pltemplate->tmplvalidator,
00207                                          pltemplate->tmpllibrary,
00208                                          false, /* isAgg */
00209                                          false, /* isWindowFunc */
00210                                          false, /* security_definer */
00211                                          false, /* isLeakProof */
00212                                          true,  /* isStrict */
00213                                          PROVOLATILE_VOLATILE,
00214                                          buildoidvector(funcargtypes, 1),
00215                                          PointerGetDatum(NULL),
00216                                          PointerGetDatum(NULL),
00217                                          PointerGetDatum(NULL),
00218                                          NIL,
00219                                          PointerGetDatum(NULL),
00220                                          1,
00221                                          0);
00222             }
00223         }
00224         else
00225             valOid = InvalidOid;
00226 
00227         /* ok, create it */
00228         return create_proc_lang(stmt->plname, stmt->replace, GetUserId(),
00229                                 handlerOid, inlineOid,
00230                                 valOid, pltemplate->tmpltrusted);
00231     }
00232     else
00233     {
00234         /*
00235          * No template, so use the provided information.  If there's no
00236          * handler clause, the user is trying to rely on a template that we
00237          * don't have, so complain accordingly.
00238          */
00239         if (!stmt->plhandler)
00240             ereport(ERROR,
00241                     (errcode(ERRCODE_UNDEFINED_OBJECT),
00242                      errmsg("unsupported language \"%s\"",
00243                             stmt->plname),
00244                      errhint("The supported languages are listed in the pg_pltemplate system catalog.")));
00245 
00246         /*
00247          * Check permission
00248          */
00249         if (!superuser())
00250             ereport(ERROR,
00251                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00252                      errmsg("must be superuser to create custom procedural language")));
00253 
00254         /*
00255          * Lookup the PL handler function and check that it is of the expected
00256          * return type
00257          */
00258         handlerOid = LookupFuncName(stmt->plhandler, 0, funcargtypes, false);
00259         funcrettype = get_func_rettype(handlerOid);
00260         if (funcrettype != LANGUAGE_HANDLEROID)
00261         {
00262             /*
00263              * We allow OPAQUE just so we can load old dump files.  When we
00264              * see a handler function declared OPAQUE, change it to
00265              * LANGUAGE_HANDLER.  (This is probably obsolete and removable?)
00266              */
00267             if (funcrettype == OPAQUEOID)
00268             {
00269                 ereport(WARNING,
00270                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
00271                          errmsg("changing return type of function %s from \"opaque\" to \"language_handler\"",
00272                                 NameListToString(stmt->plhandler))));
00273                 SetFunctionReturnType(handlerOid, LANGUAGE_HANDLEROID);
00274             }
00275             else
00276                 ereport(ERROR,
00277                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
00278                   errmsg("function %s must return type \"language_handler\"",
00279                          NameListToString(stmt->plhandler))));
00280         }
00281 
00282         /* validate the inline function */
00283         if (stmt->plinline)
00284         {
00285             funcargtypes[0] = INTERNALOID;
00286             inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes, false);
00287             /* return value is ignored, so we don't check the type */
00288         }
00289         else
00290             inlineOid = InvalidOid;
00291 
00292         /* validate the validator function */
00293         if (stmt->plvalidator)
00294         {
00295             funcargtypes[0] = OIDOID;
00296             valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false);
00297             /* return value is ignored, so we don't check the type */
00298         }
00299         else
00300             valOid = InvalidOid;
00301 
00302         /* ok, create it */
00303         return create_proc_lang(stmt->plname, stmt->replace, GetUserId(),
00304                                 handlerOid, inlineOid,
00305                                 valOid, stmt->pltrusted);
00306     }
00307 }
00308 
00309 /*
00310  * Guts of language creation.
00311  */
00312 static Oid
00313 create_proc_lang(const char *languageName, bool replace,
00314                  Oid languageOwner, Oid handlerOid, Oid inlineOid,
00315                  Oid valOid, bool trusted)
00316 {
00317     Relation    rel;
00318     TupleDesc   tupDesc;
00319     Datum       values[Natts_pg_language];
00320     bool        nulls[Natts_pg_language];
00321     bool        replaces[Natts_pg_language];
00322     NameData    langname;
00323     HeapTuple   oldtup;
00324     HeapTuple   tup;
00325     bool        is_update;
00326     ObjectAddress myself,
00327                 referenced;
00328 
00329     rel = heap_open(LanguageRelationId, RowExclusiveLock);
00330     tupDesc = RelationGetDescr(rel);
00331 
00332     /* Prepare data to be inserted */
00333     memset(values, 0, sizeof(values));
00334     memset(nulls, false, sizeof(nulls));
00335     memset(replaces, true, sizeof(replaces));
00336 
00337     namestrcpy(&langname, languageName);
00338     values[Anum_pg_language_lanname - 1] = NameGetDatum(&langname);
00339     values[Anum_pg_language_lanowner - 1] = ObjectIdGetDatum(languageOwner);
00340     values[Anum_pg_language_lanispl - 1] = BoolGetDatum(true);
00341     values[Anum_pg_language_lanpltrusted - 1] = BoolGetDatum(trusted);
00342     values[Anum_pg_language_lanplcallfoid - 1] = ObjectIdGetDatum(handlerOid);
00343     values[Anum_pg_language_laninline - 1] = ObjectIdGetDatum(inlineOid);
00344     values[Anum_pg_language_lanvalidator - 1] = ObjectIdGetDatum(valOid);
00345     nulls[Anum_pg_language_lanacl - 1] = true;
00346 
00347     /* Check for pre-existing definition */
00348     oldtup = SearchSysCache1(LANGNAME, PointerGetDatum(languageName));
00349 
00350     if (HeapTupleIsValid(oldtup))
00351     {
00352         /* There is one; okay to replace it? */
00353         if (!replace)
00354             ereport(ERROR,
00355                     (errcode(ERRCODE_DUPLICATE_OBJECT),
00356                      errmsg("language \"%s\" already exists", languageName)));
00357         if (!pg_language_ownercheck(HeapTupleGetOid(oldtup), languageOwner))
00358             aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE,
00359                            languageName);
00360 
00361         /*
00362          * Do not change existing ownership or permissions.  Note
00363          * dependency-update code below has to agree with this decision.
00364          */
00365         replaces[Anum_pg_language_lanowner - 1] = false;
00366         replaces[Anum_pg_language_lanacl - 1] = false;
00367 
00368         /* Okay, do it... */
00369         tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces);
00370         simple_heap_update(rel, &tup->t_self, tup);
00371 
00372         ReleaseSysCache(oldtup);
00373         is_update = true;
00374     }
00375     else
00376     {
00377         /* Creating a new language */
00378         tup = heap_form_tuple(tupDesc, values, nulls);
00379         simple_heap_insert(rel, tup);
00380         is_update = false;
00381     }
00382 
00383     /* Need to update indexes for either the insert or update case */
00384     CatalogUpdateIndexes(rel, tup);
00385 
00386     /*
00387      * Create dependencies for the new language.  If we are updating an
00388      * existing language, first delete any existing pg_depend entries.
00389      * (However, since we are not changing ownership or permissions, the
00390      * shared dependencies do *not* need to change, and we leave them alone.)
00391      */
00392     myself.classId = LanguageRelationId;
00393     myself.objectId = HeapTupleGetOid(tup);
00394     myself.objectSubId = 0;
00395 
00396     if (is_update)
00397         deleteDependencyRecordsFor(myself.classId, myself.objectId, true);
00398 
00399     /* dependency on owner of language */
00400     if (!is_update)
00401         recordDependencyOnOwner(myself.classId, myself.objectId,
00402                                 languageOwner);
00403 
00404     /* dependency on extension */
00405     recordDependencyOnCurrentExtension(&myself, is_update);
00406 
00407     /* dependency on the PL handler function */
00408     referenced.classId = ProcedureRelationId;
00409     referenced.objectId = handlerOid;
00410     referenced.objectSubId = 0;
00411     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00412 
00413     /* dependency on the inline handler function, if any */
00414     if (OidIsValid(inlineOid))
00415     {
00416         referenced.classId = ProcedureRelationId;
00417         referenced.objectId = inlineOid;
00418         referenced.objectSubId = 0;
00419         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00420     }
00421 
00422     /* dependency on the validator function, if any */
00423     if (OidIsValid(valOid))
00424     {
00425         referenced.classId = ProcedureRelationId;
00426         referenced.objectId = valOid;
00427         referenced.objectSubId = 0;
00428         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00429     }
00430 
00431     /* Post creation hook for new procedural language */
00432     InvokeObjectPostCreateHook(LanguageRelationId, myself.objectId, 0);
00433 
00434     heap_close(rel, RowExclusiveLock);
00435 
00436     return myself.objectId;
00437 }
00438 
00439 /*
00440  * Look to see if we have template information for the given language name.
00441  */
00442 static PLTemplate *
00443 find_language_template(const char *languageName)
00444 {
00445     PLTemplate *result;
00446     Relation    rel;
00447     SysScanDesc scan;
00448     ScanKeyData key;
00449     HeapTuple   tup;
00450 
00451     rel = heap_open(PLTemplateRelationId, AccessShareLock);
00452 
00453     ScanKeyInit(&key,
00454                 Anum_pg_pltemplate_tmplname,
00455                 BTEqualStrategyNumber, F_NAMEEQ,
00456                 NameGetDatum(languageName));
00457     scan = systable_beginscan(rel, PLTemplateNameIndexId, true,
00458                               SnapshotNow, 1, &key);
00459 
00460     tup = systable_getnext(scan);
00461     if (HeapTupleIsValid(tup))
00462     {
00463         Form_pg_pltemplate tmpl = (Form_pg_pltemplate) GETSTRUCT(tup);
00464         Datum       datum;
00465         bool        isnull;
00466 
00467         result = (PLTemplate *) palloc0(sizeof(PLTemplate));
00468         result->tmpltrusted = tmpl->tmpltrusted;
00469         result->tmpldbacreate = tmpl->tmpldbacreate;
00470 
00471         /* Remaining fields are variable-width so we need heap_getattr */
00472         datum = heap_getattr(tup, Anum_pg_pltemplate_tmplhandler,
00473                              RelationGetDescr(rel), &isnull);
00474         if (!isnull)
00475             result->tmplhandler = TextDatumGetCString(datum);
00476 
00477         datum = heap_getattr(tup, Anum_pg_pltemplate_tmplinline,
00478                              RelationGetDescr(rel), &isnull);
00479         if (!isnull)
00480             result->tmplinline = TextDatumGetCString(datum);
00481 
00482         datum = heap_getattr(tup, Anum_pg_pltemplate_tmplvalidator,
00483                              RelationGetDescr(rel), &isnull);
00484         if (!isnull)
00485             result->tmplvalidator = TextDatumGetCString(datum);
00486 
00487         datum = heap_getattr(tup, Anum_pg_pltemplate_tmpllibrary,
00488                              RelationGetDescr(rel), &isnull);
00489         if (!isnull)
00490             result->tmpllibrary = TextDatumGetCString(datum);
00491 
00492         /* Ignore template if handler or library info is missing */
00493         if (!result->tmplhandler || !result->tmpllibrary)
00494             result = NULL;
00495     }
00496     else
00497         result = NULL;
00498 
00499     systable_endscan(scan);
00500 
00501     heap_close(rel, AccessShareLock);
00502 
00503     return result;
00504 }
00505 
00506 
00507 /*
00508  * This just returns TRUE if we have a valid template for a given language
00509  */
00510 bool
00511 PLTemplateExists(const char *languageName)
00512 {
00513     return (find_language_template(languageName) != NULL);
00514 }
00515 
00516 /*
00517  * Guts of language dropping.
00518  */
00519 void
00520 DropProceduralLanguageById(Oid langOid)
00521 {
00522     Relation    rel;
00523     HeapTuple   langTup;
00524 
00525     rel = heap_open(LanguageRelationId, RowExclusiveLock);
00526 
00527     langTup = SearchSysCache1(LANGOID, ObjectIdGetDatum(langOid));
00528     if (!HeapTupleIsValid(langTup))     /* should not happen */
00529         elog(ERROR, "cache lookup failed for language %u", langOid);
00530 
00531     simple_heap_delete(rel, &langTup->t_self);
00532 
00533     ReleaseSysCache(langTup);
00534 
00535     heap_close(rel, RowExclusiveLock);
00536 }
00537 
00538 /*
00539  * get_language_oid - given a language name, look up the OID
00540  *
00541  * If missing_ok is false, throw an error if language name not found.  If
00542  * true, just return InvalidOid.
00543  */
00544 Oid
00545 get_language_oid(const char *langname, bool missing_ok)
00546 {
00547     Oid         oid;
00548 
00549     oid = GetSysCacheOid1(LANGNAME, CStringGetDatum(langname));
00550     if (!OidIsValid(oid) && !missing_ok)
00551         ereport(ERROR,
00552                 (errcode(ERRCODE_UNDEFINED_OBJECT),
00553                  errmsg("language \"%s\" does not exist", langname)));
00554     return oid;
00555 }