OSDN Git Service

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