OSDN Git Service

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