LCOV - code coverage report
Current view: top level - src/backend/commands - proclang.c (source / functions) Hit Total Coverage
Test: PostgreSQL 16beta1 Lines: 62 76 81.6 %
Date: 2023-05-30 16:15:03 Functions: 2 2 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * proclang.c
       4             :  *    PostgreSQL LANGUAGE support code.
       5             :  *
       6             :  * Portions Copyright (c) 1996-2023, 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/table.h"
      17             : #include "catalog/catalog.h"
      18             : #include "catalog/dependency.h"
      19             : #include "catalog/indexing.h"
      20             : #include "catalog/objectaccess.h"
      21             : #include "catalog/pg_language.h"
      22             : #include "catalog/pg_namespace.h"
      23             : #include "catalog/pg_proc.h"
      24             : #include "catalog/pg_type.h"
      25             : #include "commands/defrem.h"
      26             : #include "commands/proclang.h"
      27             : #include "miscadmin.h"
      28             : #include "parser/parse_func.h"
      29             : #include "utils/builtins.h"
      30             : #include "utils/lsyscache.h"
      31             : #include "utils/rel.h"
      32             : #include "utils/syscache.h"
      33             : 
      34             : 
      35             : /*
      36             :  * CREATE LANGUAGE
      37             :  */
      38             : ObjectAddress
      39         648 : CreateProceduralLanguage(CreatePLangStmt *stmt)
      40             : {
      41         648 :     const char *languageName = stmt->plname;
      42         648 :     Oid         languageOwner = GetUserId();
      43             :     Oid         handlerOid,
      44             :                 inlineOid,
      45             :                 valOid;
      46             :     Oid         funcrettype;
      47             :     Oid         funcargtypes[1];
      48             :     Relation    rel;
      49             :     TupleDesc   tupDesc;
      50             :     Datum       values[Natts_pg_language];
      51             :     bool        nulls[Natts_pg_language];
      52             :     bool        replaces[Natts_pg_language];
      53             :     NameData    langname;
      54             :     HeapTuple   oldtup;
      55             :     HeapTuple   tup;
      56             :     Oid         langoid;
      57             :     bool        is_update;
      58             :     ObjectAddress myself,
      59             :                 referenced;
      60             :     ObjectAddresses *addrs;
      61             : 
      62             :     /*
      63             :      * Check permission
      64             :      */
      65         648 :     if (!superuser())
      66           0 :         ereport(ERROR,
      67             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
      68             :                  errmsg("must be superuser to create custom procedural language")));
      69             : 
      70             :     /*
      71             :      * Lookup the PL handler function and check that it is of the expected
      72             :      * return type
      73             :      */
      74             :     Assert(stmt->plhandler);
      75         648 :     handlerOid = LookupFuncName(stmt->plhandler, 0, NULL, false);
      76         648 :     funcrettype = get_func_rettype(handlerOid);
      77         648 :     if (funcrettype != LANGUAGE_HANDLEROID)
      78           0 :         ereport(ERROR,
      79             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
      80             :                  errmsg("function %s must return type %s",
      81             :                         NameListToString(stmt->plhandler), "language_handler")));
      82             : 
      83             :     /* validate the inline function */
      84         648 :     if (stmt->plinline)
      85             :     {
      86         630 :         funcargtypes[0] = INTERNALOID;
      87         630 :         inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes, false);
      88             :         /* return value is ignored, so we don't check the type */
      89             :     }
      90             :     else
      91          18 :         inlineOid = InvalidOid;
      92             : 
      93             :     /* validate the validator function */
      94         648 :     if (stmt->plvalidator)
      95             :     {
      96         630 :         funcargtypes[0] = OIDOID;
      97         630 :         valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false);
      98             :         /* return value is ignored, so we don't check the type */
      99             :     }
     100             :     else
     101          18 :         valOid = InvalidOid;
     102             : 
     103             :     /* ok to create it */
     104         648 :     rel = table_open(LanguageRelationId, RowExclusiveLock);
     105         648 :     tupDesc = RelationGetDescr(rel);
     106             : 
     107             :     /* Prepare data to be inserted */
     108         648 :     memset(values, 0, sizeof(values));
     109         648 :     memset(nulls, false, sizeof(nulls));
     110         648 :     memset(replaces, true, sizeof(replaces));
     111             : 
     112         648 :     namestrcpy(&langname, languageName);
     113         648 :     values[Anum_pg_language_lanname - 1] = NameGetDatum(&langname);
     114         648 :     values[Anum_pg_language_lanowner - 1] = ObjectIdGetDatum(languageOwner);
     115         648 :     values[Anum_pg_language_lanispl - 1] = BoolGetDatum(true);
     116         648 :     values[Anum_pg_language_lanpltrusted - 1] = BoolGetDatum(stmt->pltrusted);
     117         648 :     values[Anum_pg_language_lanplcallfoid - 1] = ObjectIdGetDatum(handlerOid);
     118         648 :     values[Anum_pg_language_laninline - 1] = ObjectIdGetDatum(inlineOid);
     119         648 :     values[Anum_pg_language_lanvalidator - 1] = ObjectIdGetDatum(valOid);
     120         648 :     nulls[Anum_pg_language_lanacl - 1] = true;
     121             : 
     122             :     /* Check for pre-existing definition */
     123         648 :     oldtup = SearchSysCache1(LANGNAME, PointerGetDatum(languageName));
     124             : 
     125         648 :     if (HeapTupleIsValid(oldtup))
     126             :     {
     127           0 :         Form_pg_language oldform = (Form_pg_language) GETSTRUCT(oldtup);
     128             : 
     129             :         /* There is one; okay to replace it? */
     130           0 :         if (!stmt->replace)
     131           0 :             ereport(ERROR,
     132             :                     (errcode(ERRCODE_DUPLICATE_OBJECT),
     133             :                      errmsg("language \"%s\" already exists", languageName)));
     134             : 
     135             :         /* This is currently pointless, since we already checked superuser */
     136             : #ifdef NOT_USED
     137             :         if (!object_ownercheck(LanguageRelationId, oldform->oid, languageOwner))
     138             :             aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_LANGUAGE,
     139             :                            languageName);
     140             : #endif
     141             : 
     142             :         /*
     143             :          * Do not change existing oid, ownership or permissions.  Note
     144             :          * dependency-update code below has to agree with this decision.
     145             :          */
     146           0 :         replaces[Anum_pg_language_oid - 1] = false;
     147           0 :         replaces[Anum_pg_language_lanowner - 1] = false;
     148           0 :         replaces[Anum_pg_language_lanacl - 1] = false;
     149             : 
     150             :         /* Okay, do it... */
     151           0 :         tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces);
     152           0 :         CatalogTupleUpdate(rel, &tup->t_self, tup);
     153             : 
     154           0 :         langoid = oldform->oid;
     155           0 :         ReleaseSysCache(oldtup);
     156           0 :         is_update = true;
     157             :     }
     158             :     else
     159             :     {
     160             :         /* Creating a new language */
     161         648 :         langoid = GetNewOidWithIndex(rel, LanguageOidIndexId,
     162             :                                      Anum_pg_language_oid);
     163         648 :         values[Anum_pg_language_oid - 1] = ObjectIdGetDatum(langoid);
     164         648 :         tup = heap_form_tuple(tupDesc, values, nulls);
     165         648 :         CatalogTupleInsert(rel, tup);
     166         648 :         is_update = false;
     167             :     }
     168             : 
     169             :     /*
     170             :      * Create dependencies for the new language.  If we are updating an
     171             :      * existing language, first delete any existing pg_depend entries.
     172             :      * (However, since we are not changing ownership or permissions, the
     173             :      * shared dependencies do *not* need to change, and we leave them alone.)
     174             :      */
     175         648 :     myself.classId = LanguageRelationId;
     176         648 :     myself.objectId = langoid;
     177         648 :     myself.objectSubId = 0;
     178             : 
     179         648 :     if (is_update)
     180           0 :         deleteDependencyRecordsFor(myself.classId, myself.objectId, true);
     181             : 
     182             :     /* dependency on owner of language */
     183         648 :     if (!is_update)
     184         648 :         recordDependencyOnOwner(myself.classId, myself.objectId,
     185             :                                 languageOwner);
     186             : 
     187             :     /* dependency on extension */
     188         648 :     recordDependencyOnCurrentExtension(&myself, is_update);
     189             : 
     190         648 :     addrs = new_object_addresses();
     191             : 
     192             :     /* dependency on the PL handler function */
     193         648 :     ObjectAddressSet(referenced, ProcedureRelationId, handlerOid);
     194         648 :     add_exact_object_address(&referenced, addrs);
     195             : 
     196             :     /* dependency on the inline handler function, if any */
     197         648 :     if (OidIsValid(inlineOid))
     198             :     {
     199         630 :         ObjectAddressSet(referenced, ProcedureRelationId, inlineOid);
     200         630 :         add_exact_object_address(&referenced, addrs);
     201             :     }
     202             : 
     203             :     /* dependency on the validator function, if any */
     204         648 :     if (OidIsValid(valOid))
     205             :     {
     206         630 :         ObjectAddressSet(referenced, ProcedureRelationId, valOid);
     207         630 :         add_exact_object_address(&referenced, addrs);
     208             :     }
     209             : 
     210         648 :     record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
     211         648 :     free_object_addresses(addrs);
     212             : 
     213             :     /* Post creation hook for new procedural language */
     214         648 :     InvokeObjectPostCreateHook(LanguageRelationId, myself.objectId, 0);
     215             : 
     216         648 :     table_close(rel, RowExclusiveLock);
     217             : 
     218         648 :     return myself;
     219             : }
     220             : 
     221             : /*
     222             :  * get_language_oid - given a language name, look up the OID
     223             :  *
     224             :  * If missing_ok is false, throw an error if language name not found.  If
     225             :  * true, just return InvalidOid.
     226             :  */
     227             : Oid
     228        1476 : get_language_oid(const char *langname, bool missing_ok)
     229             : {
     230             :     Oid         oid;
     231             : 
     232        1476 :     oid = GetSysCacheOid1(LANGNAME, Anum_pg_language_oid,
     233             :                           CStringGetDatum(langname));
     234        1476 :     if (!OidIsValid(oid) && !missing_ok)
     235          16 :         ereport(ERROR,
     236             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     237             :                  errmsg("language \"%s\" does not exist", langname)));
     238        1460 :     return oid;
     239             : }

Generated by: LCOV version 1.14