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

Generated by: LCOV version 1.14