LCOV - code coverage report
Current view: top level - src/backend/commands - proclang.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 126 162 77.8 %
Date: 2017-09-29 15:12:54 Functions: 5 6 83.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * proclang.c
       4             :  *    PostgreSQL PROCEDURAL LANGUAGE support code.
       5             :  *
       6             :  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  * IDENTIFICATION
      10             :  *    src/backend/commands/proclang.c
      11             :  *
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : #include "postgres.h"
      15             : 
      16             : #include "access/genam.h"
      17             : #include "access/heapam.h"
      18             : #include "access/htup_details.h"
      19             : #include "catalog/dependency.h"
      20             : #include "catalog/indexing.h"
      21             : #include "catalog/objectaccess.h"
      22             : #include "catalog/pg_authid.h"
      23             : #include "catalog/pg_language.h"
      24             : #include "catalog/pg_namespace.h"
      25             : #include "catalog/pg_pltemplate.h"
      26             : #include "catalog/pg_proc.h"
      27             : #include "catalog/pg_proc_fn.h"
      28             : #include "catalog/pg_type.h"
      29             : #include "commands/dbcommands.h"
      30             : #include "commands/defrem.h"
      31             : #include "commands/proclang.h"
      32             : #include "miscadmin.h"
      33             : #include "parser/parse_func.h"
      34             : #include "parser/parser.h"
      35             : #include "utils/acl.h"
      36             : #include "utils/builtins.h"
      37             : #include "utils/fmgroids.h"
      38             : #include "utils/lsyscache.h"
      39             : #include "utils/rel.h"
      40             : #include "utils/syscache.h"
      41             : #include "utils/tqual.h"
      42             : 
      43             : 
      44             : typedef struct
      45             : {
      46             :     bool        tmpltrusted;    /* trusted? */
      47             :     bool        tmpldbacreate;  /* db owner allowed to create? */
      48             :     char       *tmplhandler;    /* name of handler function */
      49             :     char       *tmplinline;     /* name of anonymous-block handler, or NULL */
      50             :     char       *tmplvalidator;  /* name of validator function, or NULL */
      51             :     char       *tmpllibrary;    /* path of shared library */
      52             : } PLTemplate;
      53             : 
      54             : static ObjectAddress create_proc_lang(const char *languageName, bool replace,
      55             :                  Oid languageOwner, Oid handlerOid, Oid inlineOid,
      56             :                  Oid valOid, bool trusted);
      57             : static PLTemplate *find_language_template(const char *languageName);
      58             : 
      59             : /* ---------------------------------------------------------------------
      60             :  * CREATE PROCEDURAL LANGUAGE
      61             :  * ---------------------------------------------------------------------
      62             :  */
      63             : ObjectAddress
      64           3 : CreateProceduralLanguage(CreatePLangStmt *stmt)
      65             : {
      66             :     PLTemplate *pltemplate;
      67             :     ObjectAddress tmpAddr;
      68             :     Oid         handlerOid,
      69             :                 inlineOid,
      70             :                 valOid;
      71             :     Oid         funcrettype;
      72             :     Oid         funcargtypes[1];
      73             : 
      74             :     /*
      75             :      * If we have template information for the language, ignore the supplied
      76             :      * parameters (if any) and use the template information.
      77             :      */
      78           3 :     if ((pltemplate = find_language_template(stmt->plname)) != NULL)
      79             :     {
      80             :         List       *funcname;
      81             : 
      82             :         /*
      83             :          * Give a notice if we are ignoring supplied parameters.
      84             :          */
      85           1 :         if (stmt->plhandler)
      86           0 :             ereport(NOTICE,
      87             :                     (errmsg("using pg_pltemplate information instead of CREATE LANGUAGE parameters")));
      88             : 
      89             :         /*
      90             :          * Check permission
      91             :          */
      92           1 :         if (!superuser())
      93             :         {
      94           0 :             if (!pltemplate->tmpldbacreate)
      95           0 :                 ereport(ERROR,
      96             :                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
      97             :                          errmsg("must be superuser to create procedural language \"%s\"",
      98             :                                 stmt->plname)));
      99           0 :             if (!pg_database_ownercheck(MyDatabaseId, GetUserId()))
     100           0 :                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
     101           0 :                                get_database_name(MyDatabaseId));
     102             :         }
     103             : 
     104             :         /*
     105             :          * Find or create the handler function, which we force to be in the
     106             :          * pg_catalog schema.  If already present, it must have the correct
     107             :          * return type.
     108             :          */
     109           1 :         funcname = SystemFuncName(pltemplate->tmplhandler);
     110           1 :         handlerOid = LookupFuncName(funcname, 0, funcargtypes, true);
     111           1 :         if (OidIsValid(handlerOid))
     112             :         {
     113           0 :             funcrettype = get_func_rettype(handlerOid);
     114           0 :             if (funcrettype != LANGUAGE_HANDLEROID)
     115           0 :                 ereport(ERROR,
     116             :                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     117             :                          errmsg("function %s must return type %s",
     118             :                                 NameListToString(funcname), "language_handler")));
     119             :         }
     120             :         else
     121             :         {
     122           2 :             tmpAddr = ProcedureCreate(pltemplate->tmplhandler,
     123             :                                       PG_CATALOG_NAMESPACE,
     124             :                                       false,    /* replace */
     125             :                                       false,    /* returnsSet */
     126             :                                       LANGUAGE_HANDLEROID,
     127             :                                       BOOTSTRAP_SUPERUSERID,
     128             :                                       ClanguageId,
     129             :                                       F_FMGR_C_VALIDATOR,
     130           1 :                                       pltemplate->tmplhandler,
     131           1 :                                       pltemplate->tmpllibrary,
     132             :                                       false,    /* isAgg */
     133             :                                       false,    /* isWindowFunc */
     134             :                                       false,    /* security_definer */
     135             :                                       false,    /* isLeakProof */
     136             :                                       false,    /* isStrict */
     137             :                                       PROVOLATILE_VOLATILE,
     138             :                                       PROPARALLEL_UNSAFE,
     139             :                                       buildoidvector(funcargtypes, 0),
     140             :                                       PointerGetDatum(NULL),
     141             :                                       PointerGetDatum(NULL),
     142             :                                       PointerGetDatum(NULL),
     143             :                                       NIL,
     144             :                                       PointerGetDatum(NULL),
     145             :                                       PointerGetDatum(NULL),
     146             :                                       1,
     147             :                                       0);
     148           1 :             handlerOid = tmpAddr.objectId;
     149             :         }
     150             : 
     151             :         /*
     152             :          * Likewise for the anonymous block handler, if required; but we don't
     153             :          * care about its return type.
     154             :          */
     155           1 :         if (pltemplate->tmplinline)
     156             :         {
     157           1 :             funcname = SystemFuncName(pltemplate->tmplinline);
     158           1 :             funcargtypes[0] = INTERNALOID;
     159           1 :             inlineOid = LookupFuncName(funcname, 1, funcargtypes, true);
     160           1 :             if (!OidIsValid(inlineOid))
     161             :             {
     162           2 :                 tmpAddr = ProcedureCreate(pltemplate->tmplinline,
     163             :                                           PG_CATALOG_NAMESPACE,
     164             :                                           false,    /* replace */
     165             :                                           false,    /* returnsSet */
     166             :                                           VOIDOID,
     167             :                                           BOOTSTRAP_SUPERUSERID,
     168             :                                           ClanguageId,
     169             :                                           F_FMGR_C_VALIDATOR,
     170           1 :                                           pltemplate->tmplinline,
     171           1 :                                           pltemplate->tmpllibrary,
     172             :                                           false,    /* isAgg */
     173             :                                           false,    /* isWindowFunc */
     174             :                                           false,    /* security_definer */
     175             :                                           false,    /* isLeakProof */
     176             :                                           true, /* isStrict */
     177             :                                           PROVOLATILE_VOLATILE,
     178             :                                           PROPARALLEL_UNSAFE,
     179             :                                           buildoidvector(funcargtypes, 1),
     180             :                                           PointerGetDatum(NULL),
     181             :                                           PointerGetDatum(NULL),
     182             :                                           PointerGetDatum(NULL),
     183             :                                           NIL,
     184             :                                           PointerGetDatum(NULL),
     185             :                                           PointerGetDatum(NULL),
     186             :                                           1,
     187             :                                           0);
     188           1 :                 inlineOid = tmpAddr.objectId;
     189             :             }
     190             :         }
     191             :         else
     192           0 :             inlineOid = InvalidOid;
     193             : 
     194             :         /*
     195             :          * Likewise for the validator, if required; but we don't care about
     196             :          * its return type.
     197             :          */
     198           1 :         if (pltemplate->tmplvalidator)
     199             :         {
     200           1 :             funcname = SystemFuncName(pltemplate->tmplvalidator);
     201           1 :             funcargtypes[0] = OIDOID;
     202           1 :             valOid = LookupFuncName(funcname, 1, funcargtypes, true);
     203           1 :             if (!OidIsValid(valOid))
     204             :             {
     205           2 :                 tmpAddr = ProcedureCreate(pltemplate->tmplvalidator,
     206             :                                           PG_CATALOG_NAMESPACE,
     207             :                                           false,    /* replace */
     208             :                                           false,    /* returnsSet */
     209             :                                           VOIDOID,
     210             :                                           BOOTSTRAP_SUPERUSERID,
     211             :                                           ClanguageId,
     212             :                                           F_FMGR_C_VALIDATOR,
     213           1 :                                           pltemplate->tmplvalidator,
     214           1 :                                           pltemplate->tmpllibrary,
     215             :                                           false,    /* isAgg */
     216             :                                           false,    /* isWindowFunc */
     217             :                                           false,    /* security_definer */
     218             :                                           false,    /* isLeakProof */
     219             :                                           true, /* isStrict */
     220             :                                           PROVOLATILE_VOLATILE,
     221             :                                           PROPARALLEL_UNSAFE,
     222             :                                           buildoidvector(funcargtypes, 1),
     223             :                                           PointerGetDatum(NULL),
     224             :                                           PointerGetDatum(NULL),
     225             :                                           PointerGetDatum(NULL),
     226             :                                           NIL,
     227             :                                           PointerGetDatum(NULL),
     228             :                                           PointerGetDatum(NULL),
     229             :                                           1,
     230             :                                           0);
     231           1 :                 valOid = tmpAddr.objectId;
     232             :             }
     233             :         }
     234             :         else
     235           0 :             valOid = InvalidOid;
     236             : 
     237             :         /* ok, create it */
     238           1 :         return create_proc_lang(stmt->plname, stmt->replace, GetUserId(),
     239             :                                 handlerOid, inlineOid,
     240           1 :                                 valOid, pltemplate->tmpltrusted);
     241             :     }
     242             :     else
     243             :     {
     244             :         /*
     245             :          * No template, so use the provided information.  If there's no
     246             :          * handler clause, the user is trying to rely on a template that we
     247             :          * don't have, so complain accordingly.
     248             :          */
     249           2 :         if (!stmt->plhandler)
     250           0 :             ereport(ERROR,
     251             :                     (errcode(ERRCODE_UNDEFINED_OBJECT),
     252             :                      errmsg("unsupported language \"%s\"",
     253             :                             stmt->plname),
     254             :                      errhint("The supported languages are listed in the pg_pltemplate system catalog.")));
     255             : 
     256             :         /*
     257             :          * Check permission
     258             :          */
     259           2 :         if (!superuser())
     260           0 :             ereport(ERROR,
     261             :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     262             :                      errmsg("must be superuser to create custom procedural language")));
     263             : 
     264             :         /*
     265             :          * Lookup the PL handler function and check that it is of the expected
     266             :          * return type
     267             :          */
     268           2 :         handlerOid = LookupFuncName(stmt->plhandler, 0, funcargtypes, false);
     269           2 :         funcrettype = get_func_rettype(handlerOid);
     270           2 :         if (funcrettype != LANGUAGE_HANDLEROID)
     271             :         {
     272             :             /*
     273             :              * We allow OPAQUE just so we can load old dump files.  When we
     274             :              * see a handler function declared OPAQUE, change it to
     275             :              * LANGUAGE_HANDLER.  (This is probably obsolete and removable?)
     276             :              */
     277           0 :             if (funcrettype == OPAQUEOID)
     278             :             {
     279           0 :                 ereport(WARNING,
     280             :                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     281             :                          errmsg("changing return type of function %s from %s to %s",
     282             :                                 NameListToString(stmt->plhandler),
     283             :                                 "opaque", "language_handler")));
     284           0 :                 SetFunctionReturnType(handlerOid, LANGUAGE_HANDLEROID);
     285             :             }
     286             :             else
     287           0 :                 ereport(ERROR,
     288             :                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     289             :                          errmsg("function %s must return type %s",
     290             :                                 NameListToString(stmt->plhandler), "language_handler")));
     291             :         }
     292             : 
     293             :         /* validate the inline function */
     294           2 :         if (stmt->plinline)
     295             :         {
     296           0 :             funcargtypes[0] = INTERNALOID;
     297           0 :             inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes, false);
     298             :             /* return value is ignored, so we don't check the type */
     299             :         }
     300             :         else
     301           2 :             inlineOid = InvalidOid;
     302             : 
     303             :         /* validate the validator function */
     304           2 :         if (stmt->plvalidator)
     305             :         {
     306           0 :             funcargtypes[0] = OIDOID;
     307           0 :             valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false);
     308             :             /* return value is ignored, so we don't check the type */
     309             :         }
     310             :         else
     311           2 :             valOid = InvalidOid;
     312             : 
     313             :         /* ok, create it */
     314           2 :         return create_proc_lang(stmt->plname, stmt->replace, GetUserId(),
     315             :                                 handlerOid, inlineOid,
     316           2 :                                 valOid, stmt->pltrusted);
     317             :     }
     318             : }
     319             : 
     320             : /*
     321             :  * Guts of language creation.
     322             :  */
     323             : static ObjectAddress
     324           3 : create_proc_lang(const char *languageName, bool replace,
     325             :                  Oid languageOwner, Oid handlerOid, Oid inlineOid,
     326             :                  Oid valOid, bool trusted)
     327             : {
     328             :     Relation    rel;
     329             :     TupleDesc   tupDesc;
     330             :     Datum       values[Natts_pg_language];
     331             :     bool        nulls[Natts_pg_language];
     332             :     bool        replaces[Natts_pg_language];
     333             :     NameData    langname;
     334             :     HeapTuple   oldtup;
     335             :     HeapTuple   tup;
     336             :     bool        is_update;
     337             :     ObjectAddress myself,
     338             :                 referenced;
     339             : 
     340           3 :     rel = heap_open(LanguageRelationId, RowExclusiveLock);
     341           3 :     tupDesc = RelationGetDescr(rel);
     342             : 
     343             :     /* Prepare data to be inserted */
     344           3 :     memset(values, 0, sizeof(values));
     345           3 :     memset(nulls, false, sizeof(nulls));
     346           3 :     memset(replaces, true, sizeof(replaces));
     347             : 
     348           3 :     namestrcpy(&langname, languageName);
     349           3 :     values[Anum_pg_language_lanname - 1] = NameGetDatum(&langname);
     350           3 :     values[Anum_pg_language_lanowner - 1] = ObjectIdGetDatum(languageOwner);
     351           3 :     values[Anum_pg_language_lanispl - 1] = BoolGetDatum(true);
     352           3 :     values[Anum_pg_language_lanpltrusted - 1] = BoolGetDatum(trusted);
     353           3 :     values[Anum_pg_language_lanplcallfoid - 1] = ObjectIdGetDatum(handlerOid);
     354           3 :     values[Anum_pg_language_laninline - 1] = ObjectIdGetDatum(inlineOid);
     355           3 :     values[Anum_pg_language_lanvalidator - 1] = ObjectIdGetDatum(valOid);
     356           3 :     nulls[Anum_pg_language_lanacl - 1] = true;
     357             : 
     358             :     /* Check for pre-existing definition */
     359           3 :     oldtup = SearchSysCache1(LANGNAME, PointerGetDatum(languageName));
     360             : 
     361           3 :     if (HeapTupleIsValid(oldtup))
     362             :     {
     363             :         /* There is one; okay to replace it? */
     364           0 :         if (!replace)
     365           0 :             ereport(ERROR,
     366             :                     (errcode(ERRCODE_DUPLICATE_OBJECT),
     367             :                      errmsg("language \"%s\" already exists", languageName)));
     368           0 :         if (!pg_language_ownercheck(HeapTupleGetOid(oldtup), languageOwner))
     369           0 :             aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE,
     370             :                            languageName);
     371             : 
     372             :         /*
     373             :          * Do not change existing ownership or permissions.  Note
     374             :          * dependency-update code below has to agree with this decision.
     375             :          */
     376           0 :         replaces[Anum_pg_language_lanowner - 1] = false;
     377           0 :         replaces[Anum_pg_language_lanacl - 1] = false;
     378             : 
     379             :         /* Okay, do it... */
     380           0 :         tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces);
     381           0 :         CatalogTupleUpdate(rel, &tup->t_self, tup);
     382             : 
     383           0 :         ReleaseSysCache(oldtup);
     384           0 :         is_update = true;
     385             :     }
     386             :     else
     387             :     {
     388             :         /* Creating a new language */
     389           3 :         tup = heap_form_tuple(tupDesc, values, nulls);
     390           3 :         CatalogTupleInsert(rel, tup);
     391           3 :         is_update = false;
     392             :     }
     393             : 
     394             :     /*
     395             :      * Create dependencies for the new language.  If we are updating an
     396             :      * existing language, first delete any existing pg_depend entries.
     397             :      * (However, since we are not changing ownership or permissions, the
     398             :      * shared dependencies do *not* need to change, and we leave them alone.)
     399             :      */
     400           3 :     myself.classId = LanguageRelationId;
     401           3 :     myself.objectId = HeapTupleGetOid(tup);
     402           3 :     myself.objectSubId = 0;
     403             : 
     404           3 :     if (is_update)
     405           0 :         deleteDependencyRecordsFor(myself.classId, myself.objectId, true);
     406             : 
     407             :     /* dependency on owner of language */
     408           3 :     if (!is_update)
     409           3 :         recordDependencyOnOwner(myself.classId, myself.objectId,
     410             :                                 languageOwner);
     411             : 
     412             :     /* dependency on extension */
     413           3 :     recordDependencyOnCurrentExtension(&myself, is_update);
     414             : 
     415             :     /* dependency on the PL handler function */
     416           3 :     referenced.classId = ProcedureRelationId;
     417           3 :     referenced.objectId = handlerOid;
     418           3 :     referenced.objectSubId = 0;
     419           3 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     420             : 
     421             :     /* dependency on the inline handler function, if any */
     422           3 :     if (OidIsValid(inlineOid))
     423             :     {
     424           1 :         referenced.classId = ProcedureRelationId;
     425           1 :         referenced.objectId = inlineOid;
     426           1 :         referenced.objectSubId = 0;
     427           1 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     428             :     }
     429             : 
     430             :     /* dependency on the validator function, if any */
     431           3 :     if (OidIsValid(valOid))
     432             :     {
     433           1 :         referenced.classId = ProcedureRelationId;
     434           1 :         referenced.objectId = valOid;
     435           1 :         referenced.objectSubId = 0;
     436           1 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     437             :     }
     438             : 
     439             :     /* Post creation hook for new procedural language */
     440           3 :     InvokeObjectPostCreateHook(LanguageRelationId, myself.objectId, 0);
     441             : 
     442           3 :     heap_close(rel, RowExclusiveLock);
     443             : 
     444           3 :     return myself;
     445             : }
     446             : 
     447             : /*
     448             :  * Look to see if we have template information for the given language name.
     449             :  */
     450             : static PLTemplate *
     451           3 : find_language_template(const char *languageName)
     452             : {
     453             :     PLTemplate *result;
     454             :     Relation    rel;
     455             :     SysScanDesc scan;
     456             :     ScanKeyData key;
     457             :     HeapTuple   tup;
     458             : 
     459           3 :     rel = heap_open(PLTemplateRelationId, AccessShareLock);
     460             : 
     461           3 :     ScanKeyInit(&key,
     462             :                 Anum_pg_pltemplate_tmplname,
     463             :                 BTEqualStrategyNumber, F_NAMEEQ,
     464             :                 CStringGetDatum(languageName));
     465           3 :     scan = systable_beginscan(rel, PLTemplateNameIndexId, true,
     466             :                               NULL, 1, &key);
     467             : 
     468           3 :     tup = systable_getnext(scan);
     469           3 :     if (HeapTupleIsValid(tup))
     470             :     {
     471           1 :         Form_pg_pltemplate tmpl = (Form_pg_pltemplate) GETSTRUCT(tup);
     472             :         Datum       datum;
     473             :         bool        isnull;
     474             : 
     475           1 :         result = (PLTemplate *) palloc0(sizeof(PLTemplate));
     476           1 :         result->tmpltrusted = tmpl->tmpltrusted;
     477           1 :         result->tmpldbacreate = tmpl->tmpldbacreate;
     478             : 
     479             :         /* Remaining fields are variable-width so we need heap_getattr */
     480           1 :         datum = heap_getattr(tup, Anum_pg_pltemplate_tmplhandler,
     481             :                              RelationGetDescr(rel), &isnull);
     482           1 :         if (!isnull)
     483           1 :             result->tmplhandler = TextDatumGetCString(datum);
     484             : 
     485           1 :         datum = heap_getattr(tup, Anum_pg_pltemplate_tmplinline,
     486             :                              RelationGetDescr(rel), &isnull);
     487           1 :         if (!isnull)
     488           1 :             result->tmplinline = TextDatumGetCString(datum);
     489             : 
     490           1 :         datum = heap_getattr(tup, Anum_pg_pltemplate_tmplvalidator,
     491             :                              RelationGetDescr(rel), &isnull);
     492           1 :         if (!isnull)
     493           1 :             result->tmplvalidator = TextDatumGetCString(datum);
     494             : 
     495           1 :         datum = heap_getattr(tup, Anum_pg_pltemplate_tmpllibrary,
     496             :                              RelationGetDescr(rel), &isnull);
     497           1 :         if (!isnull)
     498           1 :             result->tmpllibrary = TextDatumGetCString(datum);
     499             : 
     500             :         /* Ignore template if handler or library info is missing */
     501           1 :         if (!result->tmplhandler || !result->tmpllibrary)
     502           0 :             result = NULL;
     503             :     }
     504             :     else
     505           2 :         result = NULL;
     506             : 
     507           3 :     systable_endscan(scan);
     508             : 
     509           3 :     heap_close(rel, AccessShareLock);
     510             : 
     511           3 :     return result;
     512             : }
     513             : 
     514             : 
     515             : /*
     516             :  * This just returns TRUE if we have a valid template for a given language
     517             :  */
     518             : bool
     519           0 : PLTemplateExists(const char *languageName)
     520             : {
     521           0 :     return (find_language_template(languageName) != NULL);
     522             : }
     523             : 
     524             : /*
     525             :  * Guts of language dropping.
     526             :  */
     527             : void
     528           2 : DropProceduralLanguageById(Oid langOid)
     529             : {
     530             :     Relation    rel;
     531             :     HeapTuple   langTup;
     532             : 
     533           2 :     rel = heap_open(LanguageRelationId, RowExclusiveLock);
     534             : 
     535           2 :     langTup = SearchSysCache1(LANGOID, ObjectIdGetDatum(langOid));
     536           2 :     if (!HeapTupleIsValid(langTup)) /* should not happen */
     537           0 :         elog(ERROR, "cache lookup failed for language %u", langOid);
     538             : 
     539           2 :     CatalogTupleDelete(rel, &langTup->t_self);
     540             : 
     541           2 :     ReleaseSysCache(langTup);
     542             : 
     543           2 :     heap_close(rel, RowExclusiveLock);
     544           2 : }
     545             : 
     546             : /*
     547             :  * get_language_oid - given a language name, look up the OID
     548             :  *
     549             :  * If missing_ok is false, throw an error if language name not found.  If
     550             :  * true, just return InvalidOid.
     551             :  */
     552             : Oid
     553          26 : get_language_oid(const char *langname, bool missing_ok)
     554             : {
     555             :     Oid         oid;
     556             : 
     557          26 :     oid = GetSysCacheOid1(LANGNAME, CStringGetDatum(langname));
     558          26 :     if (!OidIsValid(oid) && !missing_ok)
     559           2 :         ereport(ERROR,
     560             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     561             :                  errmsg("language \"%s\" does not exist", langname)));
     562          24 :     return oid;
     563             : }

Generated by: LCOV version 1.11