Coverage Report

Created: 2026-08-14 06:37

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