~vcs-imports/mammoth-replicator/trunk

« back to all changes in this revision

Viewing changes to src/backend/catalog/pg_proc.c

  • Committer: alvherre
  • Date: 2005-12-16 21:24:52 UTC
  • Revision ID: svn-v4:db760fc0-0f08-0410-9d63-cc6633f64896:trunk:1
Initial import of the REL8_0_3 sources from the Pgsql CVS repository.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-------------------------------------------------------------------------
 
2
 *
 
3
 * pg_proc.c
 
4
 *        routines to support manipulation of the pg_proc relation
 
5
 *
 
6
 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
 
7
 * Portions Copyright (c) 1994, Regents of the University of California
 
8
 *
 
9
 *
 
10
 * IDENTIFICATION
 
11
 *        $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.122.4.1 2005-05-03 16:51:22 tgl Exp $
 
12
 *
 
13
 *-------------------------------------------------------------------------
 
14
 */
 
15
#include "postgres.h"
 
16
 
 
17
#include "access/heapam.h"
 
18
#include "catalog/catname.h"
 
19
#include "catalog/dependency.h"
 
20
#include "catalog/indexing.h"
 
21
#include "catalog/pg_proc.h"
 
22
#include "catalog/pg_type.h"
 
23
#include "executor/functions.h"
 
24
#include "miscadmin.h"
 
25
#include "mb/pg_wchar.h"
 
26
#include "parser/parse_type.h"
 
27
#include "tcop/pquery.h"
 
28
#include "tcop/tcopprot.h"
 
29
#include "utils/acl.h"
 
30
#include "utils/builtins.h"
 
31
#include "utils/lsyscache.h"
 
32
#include "utils/syscache.h"
 
33
 
 
34
 
 
35
/* GUC parameter */
 
36
bool            check_function_bodies = true;
 
37
 
 
38
 
 
39
Datum           fmgr_internal_validator(PG_FUNCTION_ARGS);
 
40
Datum           fmgr_c_validator(PG_FUNCTION_ARGS);
 
41
Datum           fmgr_sql_validator(PG_FUNCTION_ARGS);
 
42
 
 
43
static Datum create_parameternames_array(int parameterCount,
 
44
                                                        const char *parameterNames[]);
 
45
static void sql_function_parse_error_callback(void *arg);
 
46
static int match_prosrc_to_query(const char *prosrc, const char *queryText,
 
47
                                          int cursorpos);
 
48
static bool match_prosrc_to_literal(const char *prosrc, const char *literal,
 
49
                                                int cursorpos, int *newcursorpos);
 
50
 
 
51
 
 
52
/* ----------------------------------------------------------------
 
53
 *              ProcedureCreate
 
54
 * ----------------------------------------------------------------
 
55
 */
 
56
Oid
 
57
ProcedureCreate(const char *procedureName,
 
58
                                Oid procNamespace,
 
59
                                bool replace,
 
60
                                bool returnsSet,
 
61
                                Oid returnType,
 
62
                                Oid languageObjectId,
 
63
                                Oid languageValidator,
 
64
                                const char *prosrc,
 
65
                                const char *probin,
 
66
                                bool isAgg,
 
67
                                bool security_definer,
 
68
                                bool isStrict,
 
69
                                char volatility,
 
70
                                int parameterCount,
 
71
                                const Oid *parameterTypes,
 
72
                                const char *parameterNames[])
 
73
{
 
74
        int                     i;
 
75
        bool            genericParam = false;
 
76
        bool            internalParam = false;
 
77
        Relation        rel;
 
78
        HeapTuple       tup;
 
79
        HeapTuple       oldtup;
 
80
        char            nulls[Natts_pg_proc];
 
81
        Datum           values[Natts_pg_proc];
 
82
        char            replaces[Natts_pg_proc];
 
83
        Oid                     typev[FUNC_MAX_ARGS];
 
84
        Datum           namesarray;
 
85
        Oid                     relid;
 
86
        NameData        procname;
 
87
        TupleDesc       tupDesc;
 
88
        Oid                     retval;
 
89
        bool            is_update;
 
90
        ObjectAddress myself,
 
91
                                referenced;
 
92
 
 
93
        /*
 
94
         * sanity checks
 
95
         */
 
96
        Assert(PointerIsValid(prosrc));
 
97
        Assert(PointerIsValid(probin));
 
98
 
 
99
        if (parameterCount < 0 || parameterCount > FUNC_MAX_ARGS)
 
100
                ereport(ERROR,
 
101
                                (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
 
102
                                 errmsg("functions cannot have more than %d arguments",
 
103
                                                FUNC_MAX_ARGS)));
 
104
 
 
105
        /*
 
106
         * Do not allow return type ANYARRAY or ANYELEMENT unless at least one
 
107
         * input argument is ANYARRAY or ANYELEMENT.  Also, do not allow
 
108
         * return type INTERNAL unless at least one input argument is INTERNAL.
 
109
         */
 
110
        for (i = 0; i < parameterCount; i++)
 
111
        {
 
112
                switch (parameterTypes[i])
 
113
                {
 
114
                        case ANYARRAYOID:
 
115
                        case ANYELEMENTOID:
 
116
                                genericParam = true;
 
117
                                break;
 
118
                        case INTERNALOID:
 
119
                                internalParam = true;
 
120
                                break;
 
121
                }
 
122
        }
 
123
 
 
124
        if ((returnType == ANYARRAYOID || returnType == ANYELEMENTOID)
 
125
                && !genericParam)
 
126
                ereport(ERROR,
 
127
                                (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
 
128
                                 errmsg("cannot determine result data type"),
 
129
                                 errdetail("A function returning \"anyarray\" or \"anyelement\" must have at least one argument of either type.")));
 
130
 
 
131
        if (returnType == INTERNALOID && !internalParam)
 
132
                ereport(ERROR,
 
133
                                (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
 
134
                                 errmsg("unsafe use of INTERNAL pseudo-type"),
 
135
                                 errdetail("A function returning \"internal\" must have at least one \"internal\" argument.")));
 
136
 
 
137
        /* Make sure we have a zero-padded param type array */
 
138
        MemSet(typev, 0, FUNC_MAX_ARGS * sizeof(Oid));
 
139
        if (parameterCount > 0)
 
140
                memcpy(typev, parameterTypes, parameterCount * sizeof(Oid));
 
141
 
 
142
        /* Process param names, if given */
 
143
        namesarray = create_parameternames_array(parameterCount, parameterNames);
 
144
 
 
145
        /*
 
146
         * don't allow functions of complex types that have the same name as
 
147
         * existing attributes of the type
 
148
         */
 
149
        if (parameterCount == 1 && OidIsValid(typev[0]) &&
 
150
                (relid = typeidTypeRelid(typev[0])) != InvalidOid &&
 
151
                get_attnum(relid, procedureName) != InvalidAttrNumber)
 
152
                ereport(ERROR,
 
153
                                (errcode(ERRCODE_DUPLICATE_COLUMN),
 
154
                                 errmsg("\"%s\" is already an attribute of type %s",
 
155
                                                procedureName, format_type_be(typev[0]))));
 
156
 
 
157
        /*
 
158
         * All seems OK; prepare the data to be inserted into pg_proc.
 
159
         */
 
160
 
 
161
        for (i = 0; i < Natts_pg_proc; ++i)
 
162
        {
 
163
                nulls[i] = ' ';
 
164
                values[i] = (Datum) NULL;
 
165
                replaces[i] = 'r';
 
166
        }
 
167
 
 
168
        i = 0;
 
169
        namestrcpy(&procname, procedureName);
 
170
        values[i++] = NameGetDatum(&procname);          /* proname */
 
171
        values[i++] = ObjectIdGetDatum(procNamespace);          /* pronamespace */
 
172
        values[i++] = Int32GetDatum(GetUserId());       /* proowner */
 
173
        values[i++] = ObjectIdGetDatum(languageObjectId);       /* prolang */
 
174
        values[i++] = BoolGetDatum(isAgg);      /* proisagg */
 
175
        values[i++] = BoolGetDatum(security_definer);           /* prosecdef */
 
176
        values[i++] = BoolGetDatum(isStrict);           /* proisstrict */
 
177
        values[i++] = BoolGetDatum(returnsSet);         /* proretset */
 
178
        values[i++] = CharGetDatum(volatility);         /* provolatile */
 
179
        values[i++] = UInt16GetDatum(parameterCount);           /* pronargs */
 
180
        values[i++] = ObjectIdGetDatum(returnType); /* prorettype */
 
181
        values[i++] = PointerGetDatum(typev);           /* proargtypes */
 
182
        values[i++] = namesarray;       /* proargnames */
 
183
        if (namesarray == PointerGetDatum(NULL))
 
184
                nulls[Anum_pg_proc_proargnames - 1] = 'n';
 
185
        values[i++] = DirectFunctionCall1(textin,       /* prosrc */
 
186
                                                                          CStringGetDatum(prosrc));
 
187
        values[i++] = DirectFunctionCall1(textin,       /* probin */
 
188
                                                                          CStringGetDatum(probin));
 
189
        /* proacl will be handled below */
 
190
 
 
191
        rel = heap_openr(ProcedureRelationName, RowExclusiveLock);
 
192
        tupDesc = rel->rd_att;
 
193
 
 
194
        /* Check for pre-existing definition */
 
195
        oldtup = SearchSysCache(PROCNAMENSP,
 
196
                                                        PointerGetDatum(procedureName),
 
197
                                                        UInt16GetDatum(parameterCount),
 
198
                                                        PointerGetDatum(typev),
 
199
                                                        ObjectIdGetDatum(procNamespace));
 
200
 
 
201
        if (HeapTupleIsValid(oldtup))
 
202
        {
 
203
                /* There is one; okay to replace it? */
 
204
                Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup);
 
205
 
 
206
                if (!replace)
 
207
                        ereport(ERROR,
 
208
                                        (errcode(ERRCODE_DUPLICATE_FUNCTION),
 
209
                                         errmsg("function \"%s\" already exists with same argument types",
 
210
                                                        procedureName)));
 
211
                if (GetUserId() != oldproc->proowner && !superuser())
 
212
                        aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
 
213
                                                   procedureName);
 
214
 
 
215
                /*
 
216
                 * Not okay to change the return type of the existing proc, since
 
217
                 * existing rules, views, etc may depend on the return type.
 
218
                 */
 
219
                if (returnType != oldproc->prorettype ||
 
220
                        returnsSet != oldproc->proretset)
 
221
                        ereport(ERROR,
 
222
                                        (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
 
223
                                errmsg("cannot change return type of existing function"),
 
224
                                         errhint("Use DROP FUNCTION first.")));
 
225
 
 
226
                /* Can't change aggregate status, either */
 
227
                if (oldproc->proisagg != isAgg)
 
228
                {
 
229
                        if (oldproc->proisagg)
 
230
                                ereport(ERROR,
 
231
                                                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
 
232
                                                 errmsg("function \"%s\" is an aggregate",
 
233
                                                                procedureName)));
 
234
                        else
 
235
                                ereport(ERROR,
 
236
                                                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
 
237
                                                 errmsg("function \"%s\" is not an aggregate",
 
238
                                                                procedureName)));
 
239
                }
 
240
 
 
241
                /* do not change existing ownership or permissions, either */
 
242
                replaces[Anum_pg_proc_proowner - 1] = ' ';
 
243
                replaces[Anum_pg_proc_proacl - 1] = ' ';
 
244
 
 
245
                /* Okay, do it... */
 
246
                tup = heap_modifytuple(oldtup, rel, values, nulls, replaces);
 
247
                simple_heap_update(rel, &tup->t_self, tup);
 
248
 
 
249
                ReleaseSysCache(oldtup);
 
250
                is_update = true;
 
251
        }
 
252
        else
 
253
        {
 
254
                /* Creating a new procedure */
 
255
 
 
256
                /* start out with empty permissions */
 
257
                nulls[Anum_pg_proc_proacl - 1] = 'n';
 
258
 
 
259
                tup = heap_formtuple(tupDesc, values, nulls);
 
260
                simple_heap_insert(rel, tup);
 
261
                is_update = false;
 
262
        }
 
263
 
 
264
        /* Need to update indexes for either the insert or update case */
 
265
        CatalogUpdateIndexes(rel, tup);
 
266
 
 
267
        retval = HeapTupleGetOid(tup);
 
268
 
 
269
        /*
 
270
         * Create dependencies for the new function.  If we are updating an
 
271
         * existing function, first delete any existing pg_depend entries.
 
272
         */
 
273
        if (is_update)
 
274
                deleteDependencyRecordsFor(RelOid_pg_proc, retval);
 
275
 
 
276
        myself.classId = RelOid_pg_proc;
 
277
        myself.objectId = retval;
 
278
        myself.objectSubId = 0;
 
279
 
 
280
        /* dependency on namespace */
 
281
        referenced.classId = get_system_catalog_relid(NamespaceRelationName);
 
282
        referenced.objectId = procNamespace;
 
283
        referenced.objectSubId = 0;
 
284
        recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
285
 
 
286
        /* dependency on implementation language */
 
287
        referenced.classId = get_system_catalog_relid(LanguageRelationName);
 
288
        referenced.objectId = languageObjectId;
 
289
        referenced.objectSubId = 0;
 
290
        recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
291
 
 
292
        /* dependency on return type */
 
293
        referenced.classId = RelOid_pg_type;
 
294
        referenced.objectId = returnType;
 
295
        referenced.objectSubId = 0;
 
296
        recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
297
 
 
298
        /* dependency on input types */
 
299
        for (i = 0; i < parameterCount; i++)
 
300
        {
 
301
                referenced.classId = RelOid_pg_type;
 
302
                referenced.objectId = typev[i];
 
303
                referenced.objectSubId = 0;
 
304
                recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
305
        }
 
306
 
 
307
        heap_freetuple(tup);
 
308
 
 
309
        heap_close(rel, RowExclusiveLock);
 
310
 
 
311
        /* Verify function body */
 
312
        if (OidIsValid(languageValidator))
 
313
        {
 
314
                /* Advance command counter so new tuple can be seen by validator */
 
315
                CommandCounterIncrement();
 
316
                OidFunctionCall1(languageValidator, ObjectIdGetDatum(retval));
 
317
        }
 
318
 
 
319
        return retval;
 
320
}
 
321
 
 
322
 
 
323
/*
 
324
 * create_parameternames_array - build proargnames value from an array
 
325
 * of C strings.  Returns a NULL pointer if no names provided.
 
326
 */
 
327
static Datum
 
328
create_parameternames_array(int parameterCount, const char *parameterNames[])
 
329
{
 
330
        Datum           elems[FUNC_MAX_ARGS];
 
331
        bool            found = false;
 
332
        ArrayType  *names;
 
333
        int                     i;
 
334
 
 
335
        if (!parameterNames)
 
336
                return PointerGetDatum(NULL);
 
337
 
 
338
        for (i = 0; i < parameterCount; i++)
 
339
        {
 
340
                const char *s = parameterNames[i];
 
341
 
 
342
                if (s && *s)
 
343
                        found = true;
 
344
                else
 
345
                        s = "";
 
346
 
 
347
                elems[i] = DirectFunctionCall1(textin, CStringGetDatum(s));
 
348
        }
 
349
 
 
350
        if (!found)
 
351
                return PointerGetDatum(NULL);
 
352
 
 
353
        names = construct_array(elems, parameterCount, TEXTOID, -1, false, 'i');
 
354
 
 
355
        return PointerGetDatum(names);
 
356
}
 
357
 
 
358
 
 
359
 
 
360
/*
 
361
 * Validator for internal functions
 
362
 *
 
363
 * Check that the given internal function name (the "prosrc" value) is
 
364
 * a known builtin function.
 
365
 */
 
366
Datum
 
367
fmgr_internal_validator(PG_FUNCTION_ARGS)
 
368
{
 
369
        Oid                     funcoid = PG_GETARG_OID(0);
 
370
        HeapTuple       tuple;
 
371
        Form_pg_proc proc;
 
372
        bool            isnull;
 
373
        Datum           tmp;
 
374
        char       *prosrc;
 
375
 
 
376
        /*
 
377
         * We do not honor check_function_bodies since it's unlikely the
 
378
         * function name will be found later if it isn't there now.
 
379
         */
 
380
 
 
381
        tuple = SearchSysCache(PROCOID,
 
382
                                                   ObjectIdGetDatum(funcoid),
 
383
                                                   0, 0, 0);
 
384
        if (!HeapTupleIsValid(tuple))
 
385
                elog(ERROR, "cache lookup failed for function %u", funcoid);
 
386
        proc = (Form_pg_proc) GETSTRUCT(tuple);
 
387
 
 
388
        tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
 
389
        if (isnull)
 
390
                elog(ERROR, "null prosrc");
 
391
        prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
 
392
 
 
393
        if (fmgr_internal_function(prosrc) == InvalidOid)
 
394
                ereport(ERROR,
 
395
                                (errcode(ERRCODE_UNDEFINED_FUNCTION),
 
396
                                 errmsg("there is no built-in function named \"%s\"",
 
397
                                                prosrc)));
 
398
 
 
399
        ReleaseSysCache(tuple);
 
400
 
 
401
        PG_RETURN_VOID();
 
402
}
 
403
 
 
404
 
 
405
 
 
406
/*
 
407
 * Validator for C language functions
 
408
 *
 
409
 * Make sure that the library file exists, is loadable, and contains
 
410
 * the specified link symbol. Also check for a valid function
 
411
 * information record.
 
412
 */
 
413
Datum
 
414
fmgr_c_validator(PG_FUNCTION_ARGS)
 
415
{
 
416
        Oid                     funcoid = PG_GETARG_OID(0);
 
417
        void       *libraryhandle;
 
418
        HeapTuple       tuple;
 
419
        Form_pg_proc proc;
 
420
        bool            isnull;
 
421
        Datum           tmp;
 
422
        char       *prosrc;
 
423
        char       *probin;
 
424
 
 
425
        /*
 
426
         * It'd be most consistent to skip the check if
 
427
         * !check_function_bodies, but the purpose of that switch is to be
 
428
         * helpful for pg_dump loading, and for pg_dump loading it's much
 
429
         * better if we *do* check.
 
430
         */
 
431
 
 
432
        tuple = SearchSysCache(PROCOID,
 
433
                                                   ObjectIdGetDatum(funcoid),
 
434
                                                   0, 0, 0);
 
435
        if (!HeapTupleIsValid(tuple))
 
436
                elog(ERROR, "cache lookup failed for function %u", funcoid);
 
437
        proc = (Form_pg_proc) GETSTRUCT(tuple);
 
438
 
 
439
        tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
 
440
        if (isnull)
 
441
                elog(ERROR, "null prosrc");
 
442
        prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
 
443
 
 
444
        tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_probin, &isnull);
 
445
        if (isnull)
 
446
                elog(ERROR, "null probin");
 
447
        probin = DatumGetCString(DirectFunctionCall1(textout, tmp));
 
448
 
 
449
        (void) load_external_function(probin, prosrc, true, &libraryhandle);
 
450
        (void) fetch_finfo_record(libraryhandle, prosrc);
 
451
 
 
452
        ReleaseSysCache(tuple);
 
453
 
 
454
        PG_RETURN_VOID();
 
455
}
 
456
 
 
457
 
 
458
/*
 
459
 * Validator for SQL language functions
 
460
 *
 
461
 * Parse it here in order to be sure that it contains no syntax errors.
 
462
 */
 
463
Datum
 
464
fmgr_sql_validator(PG_FUNCTION_ARGS)
 
465
{
 
466
        Oid                     funcoid = PG_GETARG_OID(0);
 
467
        HeapTuple       tuple;
 
468
        Form_pg_proc proc;
 
469
        List       *querytree_list;
 
470
        bool            isnull;
 
471
        Datum           tmp;
 
472
        char       *prosrc;
 
473
        ErrorContextCallback sqlerrcontext;
 
474
        char            functyptype;
 
475
        bool            haspolyarg;
 
476
        int                     i;
 
477
 
 
478
        tuple = SearchSysCache(PROCOID,
 
479
                                                   ObjectIdGetDatum(funcoid),
 
480
                                                   0, 0, 0);
 
481
        if (!HeapTupleIsValid(tuple))
 
482
                elog(ERROR, "cache lookup failed for function %u", funcoid);
 
483
        proc = (Form_pg_proc) GETSTRUCT(tuple);
 
484
 
 
485
        functyptype = get_typtype(proc->prorettype);
 
486
 
 
487
        /* Disallow pseudotype result */
 
488
        /* except for RECORD, VOID, ANYARRAY, or ANYELEMENT */
 
489
        if (functyptype == 'p' &&
 
490
                proc->prorettype != RECORDOID &&
 
491
                proc->prorettype != VOIDOID &&
 
492
                proc->prorettype != ANYARRAYOID &&
 
493
                proc->prorettype != ANYELEMENTOID)
 
494
                ereport(ERROR,
 
495
                                (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
 
496
                                 errmsg("SQL functions cannot return type %s",
 
497
                                                format_type_be(proc->prorettype))));
 
498
 
 
499
        /* Disallow pseudotypes in arguments */
 
500
        /* except for ANYARRAY or ANYELEMENT */
 
501
        haspolyarg = false;
 
502
        for (i = 0; i < proc->pronargs; i++)
 
503
        {
 
504
                if (get_typtype(proc->proargtypes[i]) == 'p')
 
505
                {
 
506
                        if (proc->proargtypes[i] == ANYARRAYOID ||
 
507
                                proc->proargtypes[i] == ANYELEMENTOID)
 
508
                                haspolyarg = true;
 
509
                        else
 
510
                                ereport(ERROR,
 
511
                                                (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
 
512
                                 errmsg("SQL functions cannot have arguments of type %s",
 
513
                                                format_type_be(proc->proargtypes[i]))));
 
514
                }
 
515
        }
 
516
 
 
517
        /* Postpone body checks if !check_function_bodies */
 
518
        if (check_function_bodies)
 
519
        {
 
520
                tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
 
521
                if (isnull)
 
522
                        elog(ERROR, "null prosrc");
 
523
 
 
524
                prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
 
525
 
 
526
                /*
 
527
                 * Setup error traceback support for ereport().
 
528
                 */
 
529
                sqlerrcontext.callback = sql_function_parse_error_callback;
 
530
                sqlerrcontext.arg = tuple;
 
531
                sqlerrcontext.previous = error_context_stack;
 
532
                error_context_stack = &sqlerrcontext;
 
533
 
 
534
                /*
 
535
                 * We can't do full prechecking of the function definition if
 
536
                 * there are any polymorphic input types, because actual datatypes
 
537
                 * of expression results will be unresolvable.  The check will be
 
538
                 * done at runtime instead.
 
539
                 *
 
540
                 * We can run the text through the raw parser though; this will at
 
541
                 * least catch silly syntactic errors.
 
542
                 */
 
543
                if (!haspolyarg)
 
544
                {
 
545
                        querytree_list = pg_parse_and_rewrite(prosrc,
 
546
                                                                                                  proc->proargtypes,
 
547
                                                                                                  proc->pronargs);
 
548
                        (void) check_sql_fn_retval(proc->prorettype, functyptype,
 
549
                                                                           querytree_list, NULL);
 
550
                }
 
551
                else
 
552
                        querytree_list = pg_parse_query(prosrc);
 
553
 
 
554
                error_context_stack = sqlerrcontext.previous;
 
555
        }
 
556
 
 
557
        ReleaseSysCache(tuple);
 
558
 
 
559
        PG_RETURN_VOID();
 
560
}
 
561
 
 
562
/*
 
563
 * Error context callback for handling errors in SQL function definitions
 
564
 */
 
565
static void
 
566
sql_function_parse_error_callback(void *arg)
 
567
{
 
568
        HeapTuple       tuple = (HeapTuple) arg;
 
569
        Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(tuple);
 
570
        bool            isnull;
 
571
        Datum           tmp;
 
572
        char       *prosrc;
 
573
 
 
574
        /* See if it's a syntax error; if so, transpose to CREATE FUNCTION */
 
575
        tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
 
576
        if (isnull)
 
577
                elog(ERROR, "null prosrc");
 
578
        prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
 
579
 
 
580
        if (!function_parse_error_transpose(prosrc))
 
581
        {
 
582
                /* If it's not a syntax error, push info onto context stack */
 
583
                errcontext("SQL function \"%s\"", NameStr(proc->proname));
 
584
        }
 
585
 
 
586
        pfree(prosrc);
 
587
}
 
588
 
 
589
/*
 
590
 * Adjust a syntax error occurring inside the function body of a CREATE
 
591
 * FUNCTION command.  This can be used by any function validator, not only
 
592
 * for SQL-language functions.  It is assumed that the syntax error position
 
593
 * is initially relative to the function body string (as passed in).  If
 
594
 * possible, we adjust the position to reference the original CREATE command;
 
595
 * if we can't manage that, we set up an "internal query" syntax error instead.
 
596
 *
 
597
 * Returns true if a syntax error was processed, false if not.
 
598
 */
 
599
bool
 
600
function_parse_error_transpose(const char *prosrc)
 
601
{
 
602
        int                     origerrposition;
 
603
        int                     newerrposition;
 
604
        const char *queryText;
 
605
 
 
606
        /*
 
607
         * Nothing to do unless we are dealing with a syntax error that has a
 
608
         * cursor position.
 
609
         *
 
610
         * Some PLs may prefer to report the error position as an internal error
 
611
         * to begin with, so check that too.
 
612
         */
 
613
        origerrposition = geterrposition();
 
614
        if (origerrposition <= 0)
 
615
        {
 
616
                origerrposition = getinternalerrposition();
 
617
                if (origerrposition <= 0)
 
618
                        return false;
 
619
        }
 
620
 
 
621
        /* We can get the original query text from the active portal (hack...) */
 
622
        Assert(ActivePortal && ActivePortal->status == PORTAL_ACTIVE);
 
623
        queryText = ActivePortal->sourceText;
 
624
 
 
625
        /* Try to locate the prosrc in the original text */
 
626
        newerrposition = match_prosrc_to_query(prosrc, queryText, origerrposition);
 
627
 
 
628
        if (newerrposition > 0)
 
629
        {
 
630
                /* Successful, so fix error position to reference original query */
 
631
                errposition(newerrposition);
 
632
                /* Get rid of any report of the error as an "internal query" */
 
633
                internalerrposition(0);
 
634
                internalerrquery(NULL);
 
635
        }
 
636
        else
 
637
        {
 
638
                /*
 
639
                 * If unsuccessful, convert the position to an internal position
 
640
                 * marker and give the function text as the internal query.
 
641
                 */
 
642
                errposition(0);
 
643
                internalerrposition(origerrposition);
 
644
                internalerrquery(prosrc);
 
645
        }
 
646
 
 
647
        return true;
 
648
}
 
649
 
 
650
/*
 
651
 * Try to locate the string literal containing the function body in the
 
652
 * given text of the CREATE FUNCTION command.  If successful, return the
 
653
 * character (not byte) index within the command corresponding to the
 
654
 * given character index within the literal.  If not successful, return 0.
 
655
 */
 
656
static int
 
657
match_prosrc_to_query(const char *prosrc, const char *queryText,
 
658
                                          int cursorpos)
 
659
{
 
660
        /*
 
661
         * Rather than fully parsing the CREATE FUNCTION command, we just scan
 
662
         * the command looking for $prosrc$ or 'prosrc'.  This could be fooled
 
663
         * (though not in any very probable scenarios), so fail if we find
 
664
         * more than one match.
 
665
         */
 
666
        int                     prosrclen = strlen(prosrc);
 
667
        int                     querylen = strlen(queryText);
 
668
        int                     matchpos = 0;
 
669
        int                     curpos;
 
670
        int                     newcursorpos;
 
671
 
 
672
        for (curpos = 0; curpos < querylen - prosrclen; curpos++)
 
673
        {
 
674
                if (queryText[curpos] == '$' &&
 
675
                        strncmp(prosrc, &queryText[curpos + 1], prosrclen) == 0 &&
 
676
                        queryText[curpos + 1 + prosrclen] == '$')
 
677
                {
 
678
                        /*
 
679
                         * Found a $foo$ match.  Since there are no embedded quoting
 
680
                         * characters in a dollar-quoted literal, we don't have to do
 
681
                         * any fancy arithmetic; just offset by the starting position.
 
682
                         */
 
683
                        if (matchpos)
 
684
                                return 0;               /* multiple matches, fail */
 
685
                        matchpos = pg_mbstrlen_with_len(queryText, curpos + 1)
 
686
                                + cursorpos;
 
687
                }
 
688
                else if (queryText[curpos] == '\'' &&
 
689
                                 match_prosrc_to_literal(prosrc, &queryText[curpos + 1],
 
690
                                                                                 cursorpos, &newcursorpos))
 
691
                {
 
692
                        /*
 
693
                         * Found a 'foo' match.  match_prosrc_to_literal() has
 
694
                         * adjusted for any quotes or backslashes embedded in the
 
695
                         * literal.
 
696
                         */
 
697
                        if (matchpos)
 
698
                                return 0;               /* multiple matches, fail */
 
699
                        matchpos = pg_mbstrlen_with_len(queryText, curpos + 1)
 
700
                                + newcursorpos;
 
701
                }
 
702
        }
 
703
 
 
704
        return matchpos;
 
705
}
 
706
 
 
707
/*
 
708
 * Try to match the given source text to a single-quoted literal.
 
709
 * If successful, adjust newcursorpos to correspond to the character
 
710
 * (not byte) index corresponding to cursorpos in the source text.
 
711
 *
 
712
 * At entry, literal points just past a ' character.  We must check for the
 
713
 * trailing quote.
 
714
 */
 
715
static bool
 
716
match_prosrc_to_literal(const char *prosrc, const char *literal,
 
717
                                                int cursorpos, int *newcursorpos)
 
718
{
 
719
        int                     newcp = cursorpos;
 
720
        int                     chlen;
 
721
 
 
722
        /*
 
723
         * This implementation handles backslashes and doubled quotes in the
 
724
         * string literal.      It does not handle the SQL syntax for literals
 
725
         * continued across line boundaries.
 
726
         *
 
727
         * We do the comparison a character at a time, not a byte at a time, so
 
728
         * that we can do the correct cursorpos math.
 
729
         */
 
730
        while (*prosrc)
 
731
        {
 
732
                cursorpos--;                    /* characters left before cursor */
 
733
 
 
734
                /*
 
735
                 * Check for backslashes and doubled quotes in the literal; adjust
 
736
                 * newcp when one is found before the cursor.
 
737
                 */
 
738
                if (*literal == '\\')
 
739
                {
 
740
                        literal++;
 
741
                        if (cursorpos > 0)
 
742
                                newcp++;
 
743
                }
 
744
                else if (*literal == '\'')
 
745
                {
 
746
                        if (literal[1] != '\'')
 
747
                                return false;
 
748
                        literal++;
 
749
                        if (cursorpos > 0)
 
750
                                newcp++;
 
751
                }
 
752
                chlen = pg_mblen(prosrc);
 
753
                if (strncmp(prosrc, literal, chlen) != 0)
 
754
                        return false;
 
755
                prosrc += chlen;
 
756
                literal += chlen;
 
757
        }
 
758
 
 
759
        *newcursorpos = newcp;
 
760
 
 
761
        if (*literal == '\'' && literal[1] != '\'')
 
762
                return true;
 
763
        return false;
 
764
}