1 /*-------------------------------------------------------------------------
2 *
3 * String-processing utility routines for frontend code
4 *
5 * Assorted utility functions that are useful in constructing SQL queries
6 * and interpreting backend output.
7 *
8 *
9 * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
10 * Portions Copyright (c) 1994, Regents of the University of California
11 *
12 * src/fe_utils/string_utils.c
13 *
14 *-------------------------------------------------------------------------
15 */
16 #include "postgres_fe.h"
17
18 #include <ctype.h>
19
20 #include "fe_utils/string_utils.h"
21
22 #include "common/keywords.h"
23
24
25 static PQExpBuffer defaultGetLocalPQExpBuffer(void);
26
27 /* Globals exported by this file */
28 int quote_all_identifiers = 0;
29 PQExpBuffer (*getLocalPQExpBuffer) (void) = defaultGetLocalPQExpBuffer;
30
31
32 /*
33 * Returns a temporary PQExpBuffer, valid until the next call to the function.
34 * This is used by fmtId and fmtQualifiedId.
35 *
36 * Non-reentrant and non-thread-safe but reduces memory leakage. You can
37 * replace this with a custom version by setting the getLocalPQExpBuffer
38 * function pointer.
39 */
40 static PQExpBuffer
defaultGetLocalPQExpBuffer(void)41 defaultGetLocalPQExpBuffer(void)
42 {
43 static PQExpBuffer id_return = NULL;
44
45 if (id_return) /* first time through? */
46 {
47 /* same buffer, just wipe contents */
48 resetPQExpBuffer(id_return);
49 }
50 else
51 {
52 /* new buffer */
53 id_return = createPQExpBuffer();
54 }
55
56 return id_return;
57 }
58
59 /*
60 * Quotes input string if it's not a legitimate SQL identifier as-is.
61 *
62 * Note that the returned string must be used before calling fmtId again,
63 * since we re-use the same return buffer each time.
64 */
65 const char *
fmtId(const char * rawid)66 fmtId(const char *rawid)
67 {
68 PQExpBuffer id_return = getLocalPQExpBuffer();
69
70 const char *cp;
71 bool need_quotes = false;
72
73 /*
74 * These checks need to match the identifier production in scan.l. Don't
75 * use islower() etc.
76 */
77 if (quote_all_identifiers)
78 need_quotes = true;
79 /* slightly different rules for first character */
80 else if (!((rawid[0] >= 'a' && rawid[0] <= 'z') || rawid[0] == '_'))
81 need_quotes = true;
82 else
83 {
84 /* otherwise check the entire string */
85 for (cp = rawid; *cp; cp++)
86 {
87 if (!((*cp >= 'a' && *cp <= 'z')
88 || (*cp >= '0' && *cp <= '9')
89 || (*cp == '_')))
90 {
91 need_quotes = true;
92 break;
93 }
94 }
95 }
96
97 if (!need_quotes)
98 {
99 /*
100 * Check for keyword. We quote keywords except for unreserved ones.
101 * (In some cases we could avoid quoting a col_name or type_func_name
102 * keyword, but it seems much harder than it's worth to tell that.)
103 *
104 * Note: ScanKeywordLookup() does case-insensitive comparison, but
105 * that's fine, since we already know we have all-lower-case.
106 */
107 const ScanKeyword *keyword = ScanKeywordLookup(rawid,
108 ScanKeywords,
109 NumScanKeywords);
110
111 if (keyword != NULL && keyword->category != UNRESERVED_KEYWORD)
112 need_quotes = true;
113 }
114
115 if (!need_quotes)
116 {
117 /* no quoting needed */
118 appendPQExpBufferStr(id_return, rawid);
119 }
120 else
121 {
122 appendPQExpBufferChar(id_return, '"');
123 for (cp = rawid; *cp; cp++)
124 {
125 /*
126 * Did we find a double-quote in the string? Then make this a
127 * double double-quote per SQL99. Before, we put in a
128 * backslash/double-quote pair. - thomas 2000-08-05
129 */
130 if (*cp == '"')
131 appendPQExpBufferChar(id_return, '"');
132 appendPQExpBufferChar(id_return, *cp);
133 }
134 appendPQExpBufferChar(id_return, '"');
135 }
136
137 return id_return->data;
138 }
139
140 /*
141 * fmtQualifiedId - convert a qualified name to the proper format for
142 * the source database.
143 *
144 * Like fmtId, use the result before calling again.
145 *
146 * Since we call fmtId and it also uses getLocalPQExpBuffer() we cannot
147 * use that buffer until we're finished with calling fmtId().
148 */
149 const char *
fmtQualifiedId(int remoteVersion,const char * schema,const char * id)150 fmtQualifiedId(int remoteVersion, const char *schema, const char *id)
151 {
152 PQExpBuffer id_return;
153 PQExpBuffer lcl_pqexp = createPQExpBuffer();
154
155 /* Suppress schema name if fetching from pre-7.3 DB */
156 if (remoteVersion >= 70300 && schema && *schema)
157 {
158 appendPQExpBuffer(lcl_pqexp, "%s.", fmtId(schema));
159 }
160 appendPQExpBufferStr(lcl_pqexp, fmtId(id));
161
162 id_return = getLocalPQExpBuffer();
163
164 appendPQExpBufferStr(id_return, lcl_pqexp->data);
165 destroyPQExpBuffer(lcl_pqexp);
166
167 return id_return->data;
168 }
169
170
171 /*
172 * Format a Postgres version number (in the PG_VERSION_NUM integer format
173 * returned by PQserverVersion()) as a string. This exists mainly to
174 * encapsulate knowledge about two-part vs. three-part version numbers.
175 *
176 * For re-entrancy, caller must supply the buffer the string is put in.
177 * Recommended size of the buffer is 32 bytes.
178 *
179 * Returns address of 'buf', as a notational convenience.
180 */
181 char *
formatPGVersionNumber(int version_number,bool include_minor,char * buf,size_t buflen)182 formatPGVersionNumber(int version_number, bool include_minor,
183 char *buf, size_t buflen)
184 {
185 if (version_number >= 100000)
186 {
187 /* New two-part style */
188 if (include_minor)
189 snprintf(buf, buflen, "%d.%d", version_number / 10000,
190 version_number % 10000);
191 else
192 snprintf(buf, buflen, "%d", version_number / 10000);
193 }
194 else
195 {
196 /* Old three-part style */
197 if (include_minor)
198 snprintf(buf, buflen, "%d.%d.%d", version_number / 10000,
199 (version_number / 100) % 100,
200 version_number % 100);
201 else
202 snprintf(buf, buflen, "%d.%d", version_number / 10000,
203 (version_number / 100) % 100);
204 }
205 return buf;
206 }
207
208
209 /*
210 * Convert a string value to an SQL string literal and append it to
211 * the given buffer. We assume the specified client_encoding and
212 * standard_conforming_strings settings.
213 *
214 * This is essentially equivalent to libpq's PQescapeStringInternal,
215 * except for the output buffer structure. We need it in situations
216 * where we do not have a PGconn available. Where we do,
217 * appendStringLiteralConn is a better choice.
218 */
219 void
appendStringLiteral(PQExpBuffer buf,const char * str,int encoding,bool std_strings)220 appendStringLiteral(PQExpBuffer buf, const char *str,
221 int encoding, bool std_strings)
222 {
223 size_t length = strlen(str);
224 const char *source = str;
225 char *target;
226
227 if (!enlargePQExpBuffer(buf, 2 * length + 2))
228 return;
229
230 target = buf->data + buf->len;
231 *target++ = '\'';
232
233 while (*source != '\0')
234 {
235 char c = *source;
236 int len;
237 int i;
238
239 /* Fast path for plain ASCII */
240 if (!IS_HIGHBIT_SET(c))
241 {
242 /* Apply quoting if needed */
243 if (SQL_STR_DOUBLE(c, !std_strings))
244 *target++ = c;
245 /* Copy the character */
246 *target++ = c;
247 source++;
248 continue;
249 }
250
251 /* Slow path for possible multibyte characters */
252 len = PQmblen(source, encoding);
253
254 /* Copy the character */
255 for (i = 0; i < len; i++)
256 {
257 if (*source == '\0')
258 break;
259 *target++ = *source++;
260 }
261
262 /*
263 * If we hit premature end of string (ie, incomplete multibyte
264 * character), try to pad out to the correct length with spaces. We
265 * may not be able to pad completely, but we will always be able to
266 * insert at least one pad space (since we'd not have quoted a
267 * multibyte character). This should be enough to make a string that
268 * the server will error out on.
269 */
270 if (i < len)
271 {
272 char *stop = buf->data + buf->maxlen - 2;
273
274 for (; i < len; i++)
275 {
276 if (target >= stop)
277 break;
278 *target++ = ' ';
279 }
280 break;
281 }
282 }
283
284 /* Write the terminating quote and NUL character. */
285 *target++ = '\'';
286 *target = '\0';
287
288 buf->len = target - buf->data;
289 }
290
291
292 /*
293 * Convert a string value to an SQL string literal and append it to
294 * the given buffer. Encoding and string syntax rules are as indicated
295 * by current settings of the PGconn.
296 */
297 void
appendStringLiteralConn(PQExpBuffer buf,const char * str,PGconn * conn)298 appendStringLiteralConn(PQExpBuffer buf, const char *str, PGconn *conn)
299 {
300 size_t length = strlen(str);
301
302 /*
303 * XXX This is a kluge to silence escape_string_warning in our utility
304 * programs. It should go away someday.
305 */
306 if (strchr(str, '\\') != NULL && PQserverVersion(conn) >= 80100)
307 {
308 /* ensure we are not adjacent to an identifier */
309 if (buf->len > 0 && buf->data[buf->len - 1] != ' ')
310 appendPQExpBufferChar(buf, ' ');
311 appendPQExpBufferChar(buf, ESCAPE_STRING_SYNTAX);
312 appendStringLiteral(buf, str, PQclientEncoding(conn), false);
313 return;
314 }
315 /* XXX end kluge */
316
317 if (!enlargePQExpBuffer(buf, 2 * length + 2))
318 return;
319 appendPQExpBufferChar(buf, '\'');
320 buf->len += PQescapeStringConn(conn, buf->data + buf->len,
321 str, length, NULL);
322 appendPQExpBufferChar(buf, '\'');
323 }
324
325
326 /*
327 * Convert a string value to a dollar quoted literal and append it to
328 * the given buffer. If the dqprefix parameter is not NULL then the
329 * dollar quote delimiter will begin with that (after the opening $).
330 *
331 * No escaping is done at all on str, in compliance with the rules
332 * for parsing dollar quoted strings. Also, we need not worry about
333 * encoding issues.
334 */
335 void
appendStringLiteralDQ(PQExpBuffer buf,const char * str,const char * dqprefix)336 appendStringLiteralDQ(PQExpBuffer buf, const char *str, const char *dqprefix)
337 {
338 static const char suffixes[] = "_XXXXXXX";
339 int nextchar = 0;
340 PQExpBuffer delimBuf = createPQExpBuffer();
341
342 /* start with $ + dqprefix if not NULL */
343 appendPQExpBufferChar(delimBuf, '$');
344 if (dqprefix)
345 appendPQExpBufferStr(delimBuf, dqprefix);
346
347 /*
348 * Make sure we choose a delimiter which (without the trailing $) is not
349 * present in the string being quoted. We don't check with the trailing $
350 * because a string ending in $foo must not be quoted with $foo$.
351 */
352 while (strstr(str, delimBuf->data) != NULL)
353 {
354 appendPQExpBufferChar(delimBuf, suffixes[nextchar++]);
355 nextchar %= sizeof(suffixes) - 1;
356 }
357
358 /* add trailing $ */
359 appendPQExpBufferChar(delimBuf, '$');
360
361 /* quote it and we are all done */
362 appendPQExpBufferStr(buf, delimBuf->data);
363 appendPQExpBufferStr(buf, str);
364 appendPQExpBufferStr(buf, delimBuf->data);
365
366 destroyPQExpBuffer(delimBuf);
367 }
368
369
370 /*
371 * Convert a bytea value (presented as raw bytes) to an SQL string literal
372 * and append it to the given buffer. We assume the specified
373 * standard_conforming_strings setting.
374 *
375 * This is needed in situations where we do not have a PGconn available.
376 * Where we do, PQescapeByteaConn is a better choice.
377 */
378 void
appendByteaLiteral(PQExpBuffer buf,const unsigned char * str,size_t length,bool std_strings)379 appendByteaLiteral(PQExpBuffer buf, const unsigned char *str, size_t length,
380 bool std_strings)
381 {
382 const unsigned char *source = str;
383 char *target;
384
385 static const char hextbl[] = "0123456789abcdef";
386
387 /*
388 * This implementation is hard-wired to produce hex-format output. We do
389 * not know the server version the output will be loaded into, so making
390 * an intelligent format choice is impossible. It might be better to
391 * always use the old escaped format.
392 */
393 if (!enlargePQExpBuffer(buf, 2 * length + 5))
394 return;
395
396 target = buf->data + buf->len;
397 *target++ = '\'';
398 if (!std_strings)
399 *target++ = '\\';
400 *target++ = '\\';
401 *target++ = 'x';
402
403 while (length-- > 0)
404 {
405 unsigned char c = *source++;
406
407 *target++ = hextbl[(c >> 4) & 0xF];
408 *target++ = hextbl[c & 0xF];
409 }
410
411 /* Write the terminating quote and NUL character. */
412 *target++ = '\'';
413 *target = '\0';
414
415 buf->len = target - buf->data;
416 }
417
418
419 /*
420 * Append the given string to the shell command being built in the buffer,
421 * with suitable shell-style quoting to create exactly one argument.
422 *
423 * Forbid LF or CR characters, which have scant practical use beyond designing
424 * security breaches. The Windows command shell is unusable as a conduit for
425 * arguments containing LF or CR characters. A future major release should
426 * reject those characters in CREATE ROLE and CREATE DATABASE, because use
427 * there eventually leads to errors here.
428 */
429 void
appendShellString(PQExpBuffer buf,const char * str)430 appendShellString(PQExpBuffer buf, const char *str)
431 {
432 const char *p;
433
434 #ifndef WIN32
435 appendPQExpBufferChar(buf, '\'');
436 for (p = str; *p; p++)
437 {
438 if (*p == '\n' || *p == '\r')
439 {
440 fprintf(stderr,
441 _("shell command argument contains a newline or carriage return: \"%s\"\n"),
442 str);
443 exit(EXIT_FAILURE);
444 }
445
446 if (*p == '\'')
447 appendPQExpBufferStr(buf, "'\"'\"'");
448 else
449 appendPQExpBufferChar(buf, *p);
450 }
451 appendPQExpBufferChar(buf, '\'');
452 #else /* WIN32 */
453 int backslash_run_length = 0;
454
455 /*
456 * A Windows system() argument experiences two layers of interpretation.
457 * First, cmd.exe interprets the string. Its behavior is undocumented,
458 * but a caret escapes any byte except LF or CR that would otherwise have
459 * special meaning. Handling of a caret before LF or CR differs between
460 * "cmd.exe /c" and other modes, and it is unusable here.
461 *
462 * Second, the new process parses its command line to construct argv (see
463 * https://msdn.microsoft.com/en-us/library/17w5ykft.aspx). This treats
464 * backslash-double quote sequences specially.
465 */
466 appendPQExpBufferStr(buf, "^\"");
467 for (p = str; *p; p++)
468 {
469 if (*p == '\n' || *p == '\r')
470 {
471 fprintf(stderr,
472 _("shell command argument contains a newline or carriage return: \"%s\"\n"),
473 str);
474 exit(EXIT_FAILURE);
475 }
476
477 /* Change N backslashes before a double quote to 2N+1 backslashes. */
478 if (*p == '"')
479 {
480 while (backslash_run_length)
481 {
482 appendPQExpBufferStr(buf, "^\\");
483 backslash_run_length--;
484 }
485 appendPQExpBufferStr(buf, "^\\");
486 }
487 else if (*p == '\\')
488 backslash_run_length++;
489 else
490 backslash_run_length = 0;
491
492 /*
493 * Decline to caret-escape the most mundane characters, to ease
494 * debugging and lest we approach the command length limit.
495 */
496 if (!((*p >= 'a' && *p <= 'z') ||
497 (*p >= 'A' && *p <= 'Z') ||
498 (*p >= '0' && *p <= '9')))
499 appendPQExpBufferChar(buf, '^');
500 appendPQExpBufferChar(buf, *p);
501 }
502
503 /*
504 * Change N backslashes at end of argument to 2N backslashes, because they
505 * precede the double quote that terminates the argument.
506 */
507 while (backslash_run_length)
508 {
509 appendPQExpBufferStr(buf, "^\\");
510 backslash_run_length--;
511 }
512 appendPQExpBufferStr(buf, "^\"");
513 #endif /* WIN32 */
514 }
515
516
517 /*
518 * Append the given string to the buffer, with suitable quoting for passing
519 * the string as a value, in a keyword/pair value in a libpq connection
520 * string
521 */
522 void
appendConnStrVal(PQExpBuffer buf,const char * str)523 appendConnStrVal(PQExpBuffer buf, const char *str)
524 {
525 const char *s;
526 bool needquotes;
527
528 /*
529 * If the string is one or more plain ASCII characters, no need to quote
530 * it. This is quite conservative, but better safe than sorry.
531 */
532 needquotes = true;
533 for (s = str; *s; s++)
534 {
535 if (!((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') ||
536 (*s >= '0' && *s <= '9') || *s == '_' || *s == '.'))
537 {
538 needquotes = true;
539 break;
540 }
541 needquotes = false;
542 }
543
544 if (needquotes)
545 {
546 appendPQExpBufferChar(buf, '\'');
547 while (*str)
548 {
549 /* ' and \ must be escaped by to \' and \\ */
550 if (*str == '\'' || *str == '\\')
551 appendPQExpBufferChar(buf, '\\');
552
553 appendPQExpBufferChar(buf, *str);
554 str++;
555 }
556 appendPQExpBufferChar(buf, '\'');
557 }
558 else
559 appendPQExpBufferStr(buf, str);
560 }
561
562
563 /*
564 * Append a psql meta-command that connects to the given database with the
565 * then-current connection's user, host and port.
566 */
567 void
appendPsqlMetaConnect(PQExpBuffer buf,const char * dbname)568 appendPsqlMetaConnect(PQExpBuffer buf, const char *dbname)
569 {
570 const char *s;
571 bool complex;
572
573 /*
574 * If the name is plain ASCII characters, emit a trivial "\connect "foo"".
575 * For other names, even many not technically requiring it, skip to the
576 * general case. No database has a zero-length name.
577 */
578 complex = false;
579 for (s = dbname; *s; s++)
580 {
581 if (*s == '\n' || *s == '\r')
582 {
583 fprintf(stderr,
584 _("database name contains a newline or carriage return: \"%s\"\n"),
585 dbname);
586 exit(EXIT_FAILURE);
587 }
588
589 if (!((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') ||
590 (*s >= '0' && *s <= '9') || *s == '_' || *s == '.'))
591 {
592 complex = true;
593 }
594 }
595
596 appendPQExpBufferStr(buf, "\\connect ");
597 if (complex)
598 {
599 PQExpBufferData connstr;
600
601 initPQExpBuffer(&connstr);
602 appendPQExpBuffer(&connstr, "dbname=");
603 appendConnStrVal(&connstr, dbname);
604
605 appendPQExpBuffer(buf, "-reuse-previous=on ");
606
607 /*
608 * As long as the name does not contain a newline, SQL identifier
609 * quoting satisfies the psql meta-command parser. Prefer not to
610 * involve psql-interpreted single quotes, which behaved differently
611 * before PostgreSQL 9.2.
612 */
613 appendPQExpBufferStr(buf, fmtId(connstr.data));
614
615 termPQExpBuffer(&connstr);
616 }
617 else
618 appendPQExpBufferStr(buf, fmtId(dbname));
619 appendPQExpBufferChar(buf, '\n');
620 }
621
622
623 /*
624 * Deconstruct the text representation of a 1-dimensional Postgres array
625 * into individual items.
626 *
627 * On success, returns true and sets *itemarray and *nitems to describe
628 * an array of individual strings. On parse failure, returns false;
629 * *itemarray may exist or be NULL.
630 *
631 * NOTE: free'ing itemarray is sufficient to deallocate the working storage.
632 */
633 bool
parsePGArray(const char * atext,char *** itemarray,int * nitems)634 parsePGArray(const char *atext, char ***itemarray, int *nitems)
635 {
636 int inputlen;
637 char **items;
638 char *strings;
639 int curitem;
640
641 /*
642 * We expect input in the form of "{item,item,item}" where any item is
643 * either raw data, or surrounded by double quotes (in which case embedded
644 * characters including backslashes and quotes are backslashed).
645 *
646 * We build the result as an array of pointers followed by the actual
647 * string data, all in one malloc block for convenience of deallocation.
648 * The worst-case storage need is not more than one pointer and one
649 * character for each input character (consider "{,,,,,,,,,,}").
650 */
651 *itemarray = NULL;
652 *nitems = 0;
653 inputlen = strlen(atext);
654 if (inputlen < 2 || atext[0] != '{' || atext[inputlen - 1] != '}')
655 return false; /* bad input */
656 items = (char **) malloc(inputlen * (sizeof(char *) + sizeof(char)));
657 if (items == NULL)
658 return false; /* out of memory */
659 *itemarray = items;
660 strings = (char *) (items + inputlen);
661
662 atext++; /* advance over initial '{' */
663 curitem = 0;
664 while (*atext != '}')
665 {
666 if (*atext == '\0')
667 return false; /* premature end of string */
668 items[curitem] = strings;
669 while (*atext != '}' && *atext != ',')
670 {
671 if (*atext == '\0')
672 return false; /* premature end of string */
673 if (*atext != '"')
674 *strings++ = *atext++; /* copy unquoted data */
675 else
676 {
677 /* process quoted substring */
678 atext++;
679 while (*atext != '"')
680 {
681 if (*atext == '\0')
682 return false; /* premature end of string */
683 if (*atext == '\\')
684 {
685 atext++;
686 if (*atext == '\0')
687 return false; /* premature end of string */
688 }
689 *strings++ = *atext++; /* copy quoted data */
690 }
691 atext++;
692 }
693 }
694 *strings++ = '\0';
695 if (*atext == ',')
696 atext++;
697 curitem++;
698 }
699 if (atext[1] != '\0')
700 return false; /* bogus syntax (embedded '}') */
701 *nitems = curitem;
702 return true;
703 }
704
705
706 /*
707 * Format a reloptions array and append it to the given buffer.
708 *
709 * "prefix" is prepended to the option names; typically it's "" or "toast.".
710 *
711 * Returns false if the reloptions array could not be parsed (in which case
712 * nothing will have been appended to the buffer), or true on success.
713 *
714 * Note: this logic should generally match the backend's flatten_reloptions()
715 * (in adt/ruleutils.c).
716 */
717 bool
appendReloptionsArray(PQExpBuffer buffer,const char * reloptions,const char * prefix,int encoding,bool std_strings)718 appendReloptionsArray(PQExpBuffer buffer, const char *reloptions,
719 const char *prefix, int encoding, bool std_strings)
720 {
721 char **options;
722 int noptions;
723 int i;
724
725 if (!parsePGArray(reloptions, &options, &noptions))
726 {
727 if (options)
728 free(options);
729 return false;
730 }
731
732 for (i = 0; i < noptions; i++)
733 {
734 char *option = options[i];
735 char *name;
736 char *separator;
737 char *value;
738
739 /*
740 * Each array element should have the form name=value. If the "=" is
741 * missing for some reason, treat it like an empty value.
742 */
743 name = option;
744 separator = strchr(option, '=');
745 if (separator)
746 {
747 *separator = '\0';
748 value = separator + 1;
749 }
750 else
751 value = "";
752
753 if (i > 0)
754 appendPQExpBufferStr(buffer, ", ");
755 appendPQExpBuffer(buffer, "%s%s=", prefix, fmtId(name));
756
757 /*
758 * In general we need to quote the value; but to avoid unnecessary
759 * clutter, do not quote if it is an identifier that would not need
760 * quoting. (We could also allow numbers, but that is a bit trickier
761 * than it looks --- for example, are leading zeroes significant? We
762 * don't want to assume very much here about what custom reloptions
763 * might mean.)
764 */
765 if (strcmp(fmtId(value), value) == 0)
766 appendPQExpBufferStr(buffer, value);
767 else
768 appendStringLiteral(buffer, value, encoding, std_strings);
769 }
770
771 if (options)
772 free(options);
773
774 return true;
775 }
776
777
778 /*
779 * processSQLNamePattern
780 *
781 * Scan a wildcard-pattern string and generate appropriate WHERE clauses
782 * to limit the set of objects returned. The WHERE clauses are appended
783 * to the already-partially-constructed query in buf. Returns whether
784 * any clause was added.
785 *
786 * conn: connection query will be sent to (consulted for escaping rules).
787 * buf: output parameter.
788 * pattern: user-specified pattern option, or NULL if none ("*" is implied).
789 * have_where: true if caller already emitted "WHERE" (clauses will be ANDed
790 * onto the existing WHERE clause).
791 * force_escape: always quote regexp special characters, even outside
792 * double quotes (else they are quoted only between double quotes).
793 * schemavar: name of query variable to match against a schema-name pattern.
794 * Can be NULL if no schema.
795 * namevar: name of query variable to match against an object-name pattern.
796 * altnamevar: NULL, or name of an alternative variable to match against name.
797 * visibilityrule: clause to use if we want to restrict to visible objects
798 * (for example, "pg_catalog.pg_table_is_visible(p.oid)"). Can be NULL.
799 *
800 * Formatting note: the text already present in buf should end with a newline.
801 * The appended text, if any, will end with one too.
802 */
803 bool
processSQLNamePattern(PGconn * conn,PQExpBuffer buf,const char * pattern,bool have_where,bool force_escape,const char * schemavar,const char * namevar,const char * altnamevar,const char * visibilityrule)804 processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern,
805 bool have_where, bool force_escape,
806 const char *schemavar, const char *namevar,
807 const char *altnamevar, const char *visibilityrule)
808 {
809 PQExpBufferData schemabuf;
810 PQExpBufferData namebuf;
811 int encoding = PQclientEncoding(conn);
812 bool inquotes;
813 const char *cp;
814 int i;
815 bool added_clause = false;
816
817 #define WHEREAND() \
818 (appendPQExpBufferStr(buf, have_where ? " AND " : "WHERE "), \
819 have_where = true, added_clause = true)
820
821 if (pattern == NULL)
822 {
823 /* Default: select all visible objects */
824 if (visibilityrule)
825 {
826 WHEREAND();
827 appendPQExpBuffer(buf, "%s\n", visibilityrule);
828 }
829 return added_clause;
830 }
831
832 initPQExpBuffer(&schemabuf);
833 initPQExpBuffer(&namebuf);
834
835 /*
836 * Parse the pattern, converting quotes and lower-casing unquoted letters.
837 * Also, adjust shell-style wildcard characters into regexp notation.
838 *
839 * We surround the pattern with "^(...)$" to force it to match the whole
840 * string, as per SQL practice. We have to have parens in case the string
841 * contains "|", else the "^" and "$" will be bound into the first and
842 * last alternatives which is not what we want.
843 *
844 * Note: the result of this pass is the actual regexp pattern(s) we want
845 * to execute. Quoting/escaping into SQL literal format will be done
846 * below using appendStringLiteralConn().
847 */
848 appendPQExpBufferStr(&namebuf, "^(");
849
850 inquotes = false;
851 cp = pattern;
852
853 while (*cp)
854 {
855 char ch = *cp;
856
857 if (ch == '"')
858 {
859 if (inquotes && cp[1] == '"')
860 {
861 /* emit one quote, stay in inquotes mode */
862 appendPQExpBufferChar(&namebuf, '"');
863 cp++;
864 }
865 else
866 inquotes = !inquotes;
867 cp++;
868 }
869 else if (!inquotes && isupper((unsigned char) ch))
870 {
871 appendPQExpBufferChar(&namebuf,
872 pg_tolower((unsigned char) ch));
873 cp++;
874 }
875 else if (!inquotes && ch == '*')
876 {
877 appendPQExpBufferStr(&namebuf, ".*");
878 cp++;
879 }
880 else if (!inquotes && ch == '?')
881 {
882 appendPQExpBufferChar(&namebuf, '.');
883 cp++;
884 }
885 else if (!inquotes && ch == '.')
886 {
887 /* Found schema/name separator, move current pattern to schema */
888 resetPQExpBuffer(&schemabuf);
889 appendPQExpBufferStr(&schemabuf, namebuf.data);
890 resetPQExpBuffer(&namebuf);
891 appendPQExpBufferStr(&namebuf, "^(");
892 cp++;
893 }
894 else if (ch == '$')
895 {
896 /*
897 * Dollar is always quoted, whether inside quotes or not. The
898 * reason is that it's allowed in SQL identifiers, so there's a
899 * significant use-case for treating it literally, while because
900 * we anchor the pattern automatically there is no use-case for
901 * having it possess its regexp meaning.
902 */
903 appendPQExpBufferStr(&namebuf, "\\$");
904 cp++;
905 }
906 else
907 {
908 /*
909 * Ordinary data character, transfer to pattern
910 *
911 * Inside double quotes, or at all times if force_escape is true,
912 * quote regexp special characters with a backslash to avoid
913 * regexp errors. Outside quotes, however, let them pass through
914 * as-is; this lets knowledgeable users build regexp expressions
915 * that are more powerful than shell-style patterns.
916 */
917 if ((inquotes || force_escape) &&
918 strchr("|*+?()[]{}.^$\\", ch))
919 appendPQExpBufferChar(&namebuf, '\\');
920 i = PQmblen(cp, encoding);
921 while (i-- && *cp)
922 {
923 appendPQExpBufferChar(&namebuf, *cp);
924 cp++;
925 }
926 }
927 }
928
929 /*
930 * Now decide what we need to emit. We may run under a hostile
931 * search_path, so qualify EVERY name. Note there will be a leading "^("
932 * in the patterns in any case.
933 */
934 if (namebuf.len > 2)
935 {
936 /* We have a name pattern, so constrain the namevar(s) */
937
938 appendPQExpBufferStr(&namebuf, ")$");
939 /* Optimize away a "*" pattern */
940 if (strcmp(namebuf.data, "^(.*)$") != 0)
941 {
942 WHEREAND();
943 if (altnamevar)
944 {
945 appendPQExpBuffer(buf,
946 "(%s OPERATOR(pg_catalog.~) ", namevar);
947 appendStringLiteralConn(buf, namebuf.data, conn);
948 appendPQExpBuffer(buf,
949 "\n OR %s OPERATOR(pg_catalog.~) ",
950 altnamevar);
951 appendStringLiteralConn(buf, namebuf.data, conn);
952 appendPQExpBufferStr(buf, ")\n");
953 }
954 else
955 {
956 appendPQExpBuffer(buf, "%s OPERATOR(pg_catalog.~) ", namevar);
957 appendStringLiteralConn(buf, namebuf.data, conn);
958 appendPQExpBufferChar(buf, '\n');
959 }
960 }
961 }
962
963 if (schemabuf.len > 2)
964 {
965 /* We have a schema pattern, so constrain the schemavar */
966
967 appendPQExpBufferStr(&schemabuf, ")$");
968 /* Optimize away a "*" pattern */
969 if (strcmp(schemabuf.data, "^(.*)$") != 0 && schemavar)
970 {
971 WHEREAND();
972 appendPQExpBuffer(buf, "%s OPERATOR(pg_catalog.~) ", schemavar);
973 appendStringLiteralConn(buf, schemabuf.data, conn);
974 appendPQExpBufferChar(buf, '\n');
975 }
976 }
977 else
978 {
979 /* No schema pattern given, so select only visible objects */
980 if (visibilityrule)
981 {
982 WHEREAND();
983 appendPQExpBuffer(buf, "%s\n", visibilityrule);
984 }
985 }
986
987 termPQExpBuffer(&schemabuf);
988 termPQExpBuffer(&namebuf);
989
990 return added_clause;
991 #undef WHEREAND
992 }
993