Line data Source code
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-2017, 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
41 2 : defaultGetLocalPQExpBuffer(void)
42 : {
43 : static PQExpBuffer id_return = NULL;
44 :
45 2 : if (id_return) /* first time through? */
46 : {
47 : /* same buffer, just wipe contents */
48 1 : resetPQExpBuffer(id_return);
49 : }
50 : else
51 : {
52 : /* new buffer */
53 1 : id_return = createPQExpBuffer();
54 : }
55 :
56 2 : 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 *
66 2 : fmtId(const char *rawid)
67 : {
68 2 : PQExpBuffer id_return = getLocalPQExpBuffer();
69 :
70 : const char *cp;
71 2 : 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 2 : if (quote_all_identifiers)
78 0 : need_quotes = true;
79 : /* slightly different rules for first character */
80 2 : else if (!((rawid[0] >= 'a' && rawid[0] <= 'z') || rawid[0] == '_'))
81 0 : need_quotes = true;
82 : else
83 : {
84 : /* otherwise check the entire string */
85 21 : for (cp = rawid; *cp; cp++)
86 : {
87 19 : if (!((*cp >= 'a' && *cp <= 'z')
88 1 : || (*cp >= '0' && *cp <= '9')
89 0 : || (*cp == '_')))
90 : {
91 0 : need_quotes = true;
92 0 : break;
93 : }
94 : }
95 : }
96 :
97 2 : 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 2 : const ScanKeyword *keyword = ScanKeywordLookup(rawid,
108 : ScanKeywords,
109 : NumScanKeywords);
110 :
111 2 : if (keyword != NULL && keyword->category != UNRESERVED_KEYWORD)
112 0 : need_quotes = true;
113 : }
114 :
115 2 : if (!need_quotes)
116 : {
117 : /* no quoting needed */
118 2 : appendPQExpBufferStr(id_return, rawid);
119 : }
120 : else
121 : {
122 0 : appendPQExpBufferChar(id_return, '"');
123 0 : 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 0 : if (*cp == '"')
131 0 : appendPQExpBufferChar(id_return, '"');
132 0 : appendPQExpBufferChar(id_return, *cp);
133 : }
134 0 : appendPQExpBufferChar(id_return, '"');
135 : }
136 :
137 2 : 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 *
150 0 : fmtQualifiedId(int remoteVersion, const char *schema, const char *id)
151 : {
152 : PQExpBuffer id_return;
153 0 : PQExpBuffer lcl_pqexp = createPQExpBuffer();
154 :
155 : /* Suppress schema name if fetching from pre-7.3 DB */
156 0 : if (remoteVersion >= 70300 && schema && *schema)
157 : {
158 0 : appendPQExpBuffer(lcl_pqexp, "%s.", fmtId(schema));
159 : }
160 0 : appendPQExpBufferStr(lcl_pqexp, fmtId(id));
161 :
162 0 : id_return = getLocalPQExpBuffer();
163 :
164 0 : appendPQExpBufferStr(id_return, lcl_pqexp->data);
165 0 : destroyPQExpBuffer(lcl_pqexp);
166 :
167 0 : 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 reentrancy, 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 *
182 0 : formatPGVersionNumber(int version_number, bool include_minor,
183 : char *buf, size_t buflen)
184 : {
185 0 : if (version_number >= 100000)
186 : {
187 : /* New two-part style */
188 0 : if (include_minor)
189 0 : snprintf(buf, buflen, "%d.%d", version_number / 10000,
190 : version_number % 10000);
191 : else
192 0 : snprintf(buf, buflen, "%d", version_number / 10000);
193 : }
194 : else
195 : {
196 : /* Old three-part style */
197 0 : if (include_minor)
198 0 : snprintf(buf, buflen, "%d.%d.%d", version_number / 10000,
199 0 : (version_number / 100) % 100,
200 : version_number % 100);
201 : else
202 0 : snprintf(buf, buflen, "%d.%d", version_number / 10000,
203 0 : (version_number / 100) % 100);
204 : }
205 0 : 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
220 0 : appendStringLiteral(PQExpBuffer buf, const char *str,
221 : int encoding, bool std_strings)
222 : {
223 0 : size_t length = strlen(str);
224 0 : const char *source = str;
225 : char *target;
226 :
227 0 : if (!enlargePQExpBuffer(buf, 2 * length + 2))
228 0 : return;
229 :
230 0 : target = buf->data + buf->len;
231 0 : *target++ = '\'';
232 :
233 0 : while (*source != '\0')
234 : {
235 0 : char c = *source;
236 : int len;
237 : int i;
238 :
239 : /* Fast path for plain ASCII */
240 0 : if (!IS_HIGHBIT_SET(c))
241 : {
242 : /* Apply quoting if needed */
243 0 : if (SQL_STR_DOUBLE(c, !std_strings))
244 0 : *target++ = c;
245 : /* Copy the character */
246 0 : *target++ = c;
247 0 : source++;
248 0 : continue;
249 : }
250 :
251 : /* Slow path for possible multibyte characters */
252 0 : len = PQmblen(source, encoding);
253 :
254 : /* Copy the character */
255 0 : for (i = 0; i < len; i++)
256 : {
257 0 : if (*source == '\0')
258 0 : break;
259 0 : *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 0 : if (i < len)
271 : {
272 0 : char *stop = buf->data + buf->maxlen - 2;
273 :
274 0 : for (; i < len; i++)
275 : {
276 0 : if (target >= stop)
277 0 : break;
278 0 : *target++ = ' ';
279 : }
280 0 : break;
281 : }
282 : }
283 :
284 : /* Write the terminating quote and NUL character. */
285 0 : *target++ = '\'';
286 0 : *target = '\0';
287 :
288 0 : 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
298 273 : appendStringLiteralConn(PQExpBuffer buf, const char *str, PGconn *conn)
299 : {
300 273 : 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 273 : if (strchr(str, '\\') != NULL && PQserverVersion(conn) >= 80100)
307 : {
308 : /* ensure we are not adjacent to an identifier */
309 0 : if (buf->len > 0 && buf->data[buf->len - 1] != ' ')
310 0 : appendPQExpBufferChar(buf, ' ');
311 0 : appendPQExpBufferChar(buf, ESCAPE_STRING_SYNTAX);
312 0 : appendStringLiteral(buf, str, PQclientEncoding(conn), false);
313 0 : return;
314 : }
315 : /* XXX end kluge */
316 :
317 273 : if (!enlargePQExpBuffer(buf, 2 * length + 2))
318 0 : return;
319 273 : appendPQExpBufferChar(buf, '\'');
320 273 : buf->len += PQescapeStringConn(conn, buf->data + buf->len,
321 : str, length, NULL);
322 273 : 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
336 0 : appendStringLiteralDQ(PQExpBuffer buf, const char *str, const char *dqprefix)
337 : {
338 : static const char suffixes[] = "_XXXXXXX";
339 0 : int nextchar = 0;
340 0 : PQExpBuffer delimBuf = createPQExpBuffer();
341 :
342 : /* start with $ + dqprefix if not NULL */
343 0 : appendPQExpBufferChar(delimBuf, '$');
344 0 : if (dqprefix)
345 0 : 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 0 : while (strstr(str, delimBuf->data) != NULL)
353 : {
354 0 : appendPQExpBufferChar(delimBuf, suffixes[nextchar++]);
355 0 : nextchar %= sizeof(suffixes) - 1;
356 : }
357 :
358 : /* add trailing $ */
359 0 : appendPQExpBufferChar(delimBuf, '$');
360 :
361 : /* quote it and we are all done */
362 0 : appendPQExpBufferStr(buf, delimBuf->data);
363 0 : appendPQExpBufferStr(buf, str);
364 0 : appendPQExpBufferStr(buf, delimBuf->data);
365 :
366 0 : destroyPQExpBuffer(delimBuf);
367 0 : }
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
379 0 : appendByteaLiteral(PQExpBuffer buf, const unsigned char *str, size_t length,
380 : bool std_strings)
381 : {
382 0 : 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 0 : if (!enlargePQExpBuffer(buf, 2 * length + 5))
394 0 : return;
395 :
396 0 : target = buf->data + buf->len;
397 0 : *target++ = '\'';
398 0 : if (!std_strings)
399 0 : *target++ = '\\';
400 0 : *target++ = '\\';
401 0 : *target++ = 'x';
402 :
403 0 : while (length-- > 0)
404 : {
405 0 : unsigned char c = *source++;
406 :
407 0 : *target++ = hextbl[(c >> 4) & 0xF];
408 0 : *target++ = hextbl[c & 0xF];
409 : }
410 :
411 : /* Write the terminating quote and NUL character. */
412 0 : *target++ = '\'';
413 0 : *target = '\0';
414 :
415 0 : 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 shell-style quoting as needed 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 : * appendShellString() simply prints an error and dies if LF or CR appears.
430 : * appendShellStringNoError() omits those characters from the result, and
431 : * returns false if there were any.
432 : */
433 : void
434 2 : appendShellString(PQExpBuffer buf, const char *str)
435 : {
436 2 : if (!appendShellStringNoError(buf, str))
437 : {
438 0 : fprintf(stderr,
439 : _("shell command argument contains a newline or carriage return: \"%s\"\n"),
440 : str);
441 0 : exit(EXIT_FAILURE);
442 : }
443 2 : }
444 :
445 : bool
446 2 : appendShellStringNoError(PQExpBuffer buf, const char *str)
447 : {
448 : #ifdef WIN32
449 : int backslash_run_length = 0;
450 : #endif
451 2 : bool ok = true;
452 : const char *p;
453 :
454 : /*
455 : * Don't bother with adding quotes if the string is nonempty and clearly
456 : * contains only safe characters.
457 : */
458 4 : if (*str != '\0' &&
459 2 : strspn(str, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_./:") == strlen(str))
460 : {
461 2 : appendPQExpBufferStr(buf, str);
462 2 : return ok;
463 : }
464 :
465 : #ifndef WIN32
466 0 : appendPQExpBufferChar(buf, '\'');
467 0 : for (p = str; *p; p++)
468 : {
469 0 : if (*p == '\n' || *p == '\r')
470 : {
471 0 : ok = false;
472 0 : continue;
473 : }
474 :
475 0 : if (*p == '\'')
476 0 : appendPQExpBufferStr(buf, "'\"'\"'");
477 : else
478 0 : appendPQExpBufferChar(buf, *p);
479 : }
480 0 : appendPQExpBufferChar(buf, '\'');
481 : #else /* WIN32 */
482 :
483 : /*
484 : * A Windows system() argument experiences two layers of interpretation.
485 : * First, cmd.exe interprets the string. Its behavior is undocumented,
486 : * but a caret escapes any byte except LF or CR that would otherwise have
487 : * special meaning. Handling of a caret before LF or CR differs between
488 : * "cmd.exe /c" and other modes, and it is unusable here.
489 : *
490 : * Second, the new process parses its command line to construct argv (see
491 : * https://msdn.microsoft.com/en-us/library/17w5ykft.aspx). This treats
492 : * backslash-double quote sequences specially.
493 : */
494 : appendPQExpBufferStr(buf, "^\"");
495 : for (p = str; *p; p++)
496 : {
497 : if (*p == '\n' || *p == '\r')
498 : {
499 : ok = false;
500 : continue;
501 : }
502 :
503 : /* Change N backslashes before a double quote to 2N+1 backslashes. */
504 : if (*p == '"')
505 : {
506 : while (backslash_run_length)
507 : {
508 : appendPQExpBufferStr(buf, "^\\");
509 : backslash_run_length--;
510 : }
511 : appendPQExpBufferStr(buf, "^\\");
512 : }
513 : else if (*p == '\\')
514 : backslash_run_length++;
515 : else
516 : backslash_run_length = 0;
517 :
518 : /*
519 : * Decline to caret-escape the most mundane characters, to ease
520 : * debugging and lest we approach the command length limit.
521 : */
522 : if (!((*p >= 'a' && *p <= 'z') ||
523 : (*p >= 'A' && *p <= 'Z') ||
524 : (*p >= '0' && *p <= '9')))
525 : appendPQExpBufferChar(buf, '^');
526 : appendPQExpBufferChar(buf, *p);
527 : }
528 :
529 : /*
530 : * Change N backslashes at end of argument to 2N backslashes, because they
531 : * precede the double quote that terminates the argument.
532 : */
533 : while (backslash_run_length)
534 : {
535 : appendPQExpBufferStr(buf, "^\\");
536 : backslash_run_length--;
537 : }
538 : appendPQExpBufferStr(buf, "^\"");
539 : #endif /* WIN32 */
540 :
541 0 : return ok;
542 : }
543 :
544 :
545 : /*
546 : * Append the given string to the buffer, with suitable quoting for passing
547 : * the string as a value, in a keyword/pair value in a libpq connection
548 : * string
549 : */
550 : void
551 33 : appendConnStrVal(PQExpBuffer buf, const char *str)
552 : {
553 : const char *s;
554 : bool needquotes;
555 :
556 : /*
557 : * If the string is one or more plain ASCII characters, no need to quote
558 : * it. This is quite conservative, but better safe than sorry.
559 : */
560 33 : needquotes = true;
561 363 : for (s = str; *s; s++)
562 : {
563 330 : if (!((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') ||
564 0 : (*s >= '0' && *s <= '9') || *s == '_' || *s == '.'))
565 : {
566 0 : needquotes = true;
567 0 : break;
568 : }
569 330 : needquotes = false;
570 : }
571 :
572 33 : if (needquotes)
573 : {
574 0 : appendPQExpBufferChar(buf, '\'');
575 0 : while (*str)
576 : {
577 : /* ' and \ must be escaped by to \' and \\ */
578 0 : if (*str == '\'' || *str == '\\')
579 0 : appendPQExpBufferChar(buf, '\\');
580 :
581 0 : appendPQExpBufferChar(buf, *str);
582 0 : str++;
583 : }
584 0 : appendPQExpBufferChar(buf, '\'');
585 : }
586 : else
587 33 : appendPQExpBufferStr(buf, str);
588 33 : }
589 :
590 :
591 : /*
592 : * Append a psql meta-command that connects to the given database with the
593 : * then-current connection's user, host and port.
594 : */
595 : void
596 0 : appendPsqlMetaConnect(PQExpBuffer buf, const char *dbname)
597 : {
598 : const char *s;
599 : bool complex;
600 :
601 : /*
602 : * If the name is plain ASCII characters, emit a trivial "\connect "foo"".
603 : * For other names, even many not technically requiring it, skip to the
604 : * general case. No database has a zero-length name.
605 : */
606 0 : complex = false;
607 :
608 0 : for (s = dbname; *s; s++)
609 : {
610 0 : if (*s == '\n' || *s == '\r')
611 : {
612 0 : fprintf(stderr,
613 : _("database name contains a newline or carriage return: \"%s\"\n"),
614 : dbname);
615 0 : exit(EXIT_FAILURE);
616 : }
617 :
618 0 : if (!((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') ||
619 0 : (*s >= '0' && *s <= '9') || *s == '_' || *s == '.'))
620 : {
621 0 : complex = true;
622 : }
623 : }
624 :
625 0 : appendPQExpBufferStr(buf, "\\connect ");
626 0 : if (complex)
627 : {
628 : PQExpBufferData connstr;
629 :
630 0 : initPQExpBuffer(&connstr);
631 0 : appendPQExpBuffer(&connstr, "dbname=");
632 0 : appendConnStrVal(&connstr, dbname);
633 :
634 0 : appendPQExpBuffer(buf, "-reuse-previous=on ");
635 :
636 : /*
637 : * As long as the name does not contain a newline, SQL identifier
638 : * quoting satisfies the psql meta-command parser. Prefer not to
639 : * involve psql-interpreted single quotes, which behaved differently
640 : * before PostgreSQL 9.2.
641 : */
642 0 : appendPQExpBufferStr(buf, fmtId(connstr.data));
643 :
644 0 : termPQExpBuffer(&connstr);
645 : }
646 : else
647 0 : appendPQExpBufferStr(buf, fmtId(dbname));
648 0 : appendPQExpBufferChar(buf, '\n');
649 0 : }
650 :
651 :
652 : /*
653 : * Deconstruct the text representation of a 1-dimensional Postgres array
654 : * into individual items.
655 : *
656 : * On success, returns true and sets *itemarray and *nitems to describe
657 : * an array of individual strings. On parse failure, returns false;
658 : * *itemarray may exist or be NULL.
659 : *
660 : * NOTE: free'ing itemarray is sufficient to deallocate the working storage.
661 : */
662 : bool
663 0 : parsePGArray(const char *atext, char ***itemarray, int *nitems)
664 : {
665 : int inputlen;
666 : char **items;
667 : char *strings;
668 : int curitem;
669 :
670 : /*
671 : * We expect input in the form of "{item,item,item}" where any item is
672 : * either raw data, or surrounded by double quotes (in which case embedded
673 : * characters including backslashes and quotes are backslashed).
674 : *
675 : * We build the result as an array of pointers followed by the actual
676 : * string data, all in one malloc block for convenience of deallocation.
677 : * The worst-case storage need is not more than one pointer and one
678 : * character for each input character (consider "{,,,,,,,,,,}").
679 : */
680 0 : *itemarray = NULL;
681 0 : *nitems = 0;
682 0 : inputlen = strlen(atext);
683 0 : if (inputlen < 2 || atext[0] != '{' || atext[inputlen - 1] != '}')
684 0 : return false; /* bad input */
685 0 : items = (char **) malloc(inputlen * (sizeof(char *) + sizeof(char)));
686 0 : if (items == NULL)
687 0 : return false; /* out of memory */
688 0 : *itemarray = items;
689 0 : strings = (char *) (items + inputlen);
690 :
691 0 : atext++; /* advance over initial '{' */
692 0 : curitem = 0;
693 0 : while (*atext != '}')
694 : {
695 0 : if (*atext == '\0')
696 0 : return false; /* premature end of string */
697 0 : items[curitem] = strings;
698 0 : while (*atext != '}' && *atext != ',')
699 : {
700 0 : if (*atext == '\0')
701 0 : return false; /* premature end of string */
702 0 : if (*atext != '"')
703 0 : *strings++ = *atext++; /* copy unquoted data */
704 : else
705 : {
706 : /* process quoted substring */
707 0 : atext++;
708 0 : while (*atext != '"')
709 : {
710 0 : if (*atext == '\0')
711 0 : return false; /* premature end of string */
712 0 : if (*atext == '\\')
713 : {
714 0 : atext++;
715 0 : if (*atext == '\0')
716 0 : return false; /* premature end of string */
717 : }
718 0 : *strings++ = *atext++; /* copy quoted data */
719 : }
720 0 : atext++;
721 : }
722 : }
723 0 : *strings++ = '\0';
724 0 : if (*atext == ',')
725 0 : atext++;
726 0 : curitem++;
727 : }
728 0 : if (atext[1] != '\0')
729 0 : return false; /* bogus syntax (embedded '}') */
730 0 : *nitems = curitem;
731 0 : return true;
732 : }
733 :
734 :
735 : /*
736 : * Format a reloptions array and append it to the given buffer.
737 : *
738 : * "prefix" is prepended to the option names; typically it's "" or "toast.".
739 : *
740 : * Returns false if the reloptions array could not be parsed (in which case
741 : * nothing will have been appended to the buffer), or true on success.
742 : *
743 : * Note: this logic should generally match the backend's flatten_reloptions()
744 : * (in adt/ruleutils.c).
745 : */
746 : bool
747 0 : appendReloptionsArray(PQExpBuffer buffer, const char *reloptions,
748 : const char *prefix, int encoding, bool std_strings)
749 : {
750 : char **options;
751 : int noptions;
752 : int i;
753 :
754 0 : if (!parsePGArray(reloptions, &options, &noptions))
755 : {
756 0 : if (options)
757 0 : free(options);
758 0 : return false;
759 : }
760 :
761 0 : for (i = 0; i < noptions; i++)
762 : {
763 0 : char *option = options[i];
764 : char *name;
765 : char *separator;
766 : char *value;
767 :
768 : /*
769 : * Each array element should have the form name=value. If the "=" is
770 : * missing for some reason, treat it like an empty value.
771 : */
772 0 : name = option;
773 0 : separator = strchr(option, '=');
774 0 : if (separator)
775 : {
776 0 : *separator = '\0';
777 0 : value = separator + 1;
778 : }
779 : else
780 0 : value = "";
781 :
782 0 : if (i > 0)
783 0 : appendPQExpBufferStr(buffer, ", ");
784 0 : appendPQExpBuffer(buffer, "%s%s=", prefix, fmtId(name));
785 :
786 : /*
787 : * In general we need to quote the value; but to avoid unnecessary
788 : * clutter, do not quote if it is an identifier that would not need
789 : * quoting. (We could also allow numbers, but that is a bit trickier
790 : * than it looks --- for example, are leading zeroes significant? We
791 : * don't want to assume very much here about what custom reloptions
792 : * might mean.)
793 : */
794 0 : if (strcmp(fmtId(value), value) == 0)
795 0 : appendPQExpBufferStr(buffer, value);
796 : else
797 0 : appendStringLiteral(buffer, value, encoding, std_strings);
798 : }
799 :
800 0 : if (options)
801 0 : free(options);
802 :
803 0 : return true;
804 : }
805 :
806 :
807 : /*
808 : * processSQLNamePattern
809 : *
810 : * Scan a wildcard-pattern string and generate appropriate WHERE clauses
811 : * to limit the set of objects returned. The WHERE clauses are appended
812 : * to the already-partially-constructed query in buf. Returns whether
813 : * any clause was added.
814 : *
815 : * conn: connection query will be sent to (consulted for escaping rules).
816 : * buf: output parameter.
817 : * pattern: user-specified pattern option, or NULL if none ("*" is implied).
818 : * have_where: true if caller already emitted "WHERE" (clauses will be ANDed
819 : * onto the existing WHERE clause).
820 : * force_escape: always quote regexp special characters, even outside
821 : * double quotes (else they are quoted only between double quotes).
822 : * schemavar: name of query variable to match against a schema-name pattern.
823 : * Can be NULL if no schema.
824 : * namevar: name of query variable to match against an object-name pattern.
825 : * altnamevar: NULL, or name of an alternative variable to match against name.
826 : * visibilityrule: clause to use if we want to restrict to visible objects
827 : * (for example, "pg_catalog.pg_table_is_visible(p.oid)"). Can be NULL.
828 : *
829 : * Formatting note: the text already present in buf should end with a newline.
830 : * The appended text, if any, will end with one too.
831 : */
832 : bool
833 303 : processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern,
834 : bool have_where, bool force_escape,
835 : const char *schemavar, const char *namevar,
836 : const char *altnamevar, const char *visibilityrule)
837 : {
838 : PQExpBufferData schemabuf;
839 : PQExpBufferData namebuf;
840 303 : int encoding = PQclientEncoding(conn);
841 : bool inquotes;
842 : const char *cp;
843 : int i;
844 303 : bool added_clause = false;
845 :
846 : #define WHEREAND() \
847 : (appendPQExpBufferStr(buf, have_where ? " AND " : "WHERE "), \
848 : have_where = true, added_clause = true)
849 :
850 303 : if (pattern == NULL)
851 : {
852 : /* Default: select all visible objects */
853 46 : if (visibilityrule)
854 : {
855 2 : WHEREAND();
856 2 : appendPQExpBuffer(buf, "%s\n", visibilityrule);
857 : }
858 46 : return added_clause;
859 : }
860 :
861 257 : initPQExpBuffer(&schemabuf);
862 257 : initPQExpBuffer(&namebuf);
863 :
864 : /*
865 : * Parse the pattern, converting quotes and lower-casing unquoted letters.
866 : * Also, adjust shell-style wildcard characters into regexp notation.
867 : *
868 : * We surround the pattern with "^(...)$" to force it to match the whole
869 : * string, as per SQL practice. We have to have parens in case the string
870 : * contains "|", else the "^" and "$" will be bound into the first and
871 : * last alternatives which is not what we want.
872 : *
873 : * Note: the result of this pass is the actual regexp pattern(s) we want
874 : * to execute. Quoting/escaping into SQL literal format will be done
875 : * below using appendStringLiteralConn().
876 : */
877 257 : appendPQExpBufferStr(&namebuf, "^(");
878 :
879 257 : inquotes = false;
880 257 : cp = pattern;
881 :
882 3472 : while (*cp)
883 : {
884 2958 : char ch = *cp;
885 :
886 2958 : if (ch == '"')
887 : {
888 0 : if (inquotes && cp[1] == '"')
889 : {
890 : /* emit one quote, stay in inquotes mode */
891 0 : appendPQExpBufferChar(&namebuf, '"');
892 0 : cp++;
893 : }
894 : else
895 0 : inquotes = !inquotes;
896 0 : cp++;
897 : }
898 2958 : else if (!inquotes && isupper((unsigned char) ch))
899 : {
900 0 : appendPQExpBufferChar(&namebuf,
901 0 : pg_tolower((unsigned char) ch));
902 0 : cp++;
903 : }
904 2958 : else if (!inquotes && ch == '*')
905 : {
906 1 : appendPQExpBufferStr(&namebuf, ".*");
907 1 : cp++;
908 : }
909 2957 : else if (!inquotes && ch == '?')
910 : {
911 0 : appendPQExpBufferChar(&namebuf, '.');
912 0 : cp++;
913 : }
914 2957 : else if (!inquotes && ch == '.')
915 : {
916 : /* Found schema/name separator, move current pattern to schema */
917 15 : resetPQExpBuffer(&schemabuf);
918 15 : appendPQExpBufferStr(&schemabuf, namebuf.data);
919 15 : resetPQExpBuffer(&namebuf);
920 15 : appendPQExpBufferStr(&namebuf, "^(");
921 15 : cp++;
922 : }
923 2942 : else if (ch == '$')
924 : {
925 : /*
926 : * Dollar is always quoted, whether inside quotes or not. The
927 : * reason is that it's allowed in SQL identifiers, so there's a
928 : * significant use-case for treating it literally, while because
929 : * we anchor the pattern automatically there is no use-case for
930 : * having it possess its regexp meaning.
931 : */
932 0 : appendPQExpBufferStr(&namebuf, "\\$");
933 0 : cp++;
934 : }
935 : else
936 : {
937 : /*
938 : * Ordinary data character, transfer to pattern
939 : *
940 : * Inside double quotes, or at all times if force_escape is true,
941 : * quote regexp special characters with a backslash to avoid
942 : * regexp errors. Outside quotes, however, let them pass through
943 : * as-is; this lets knowledgeable users build regexp expressions
944 : * that are more powerful than shell-style patterns.
945 : */
946 2942 : if ((inquotes || force_escape) &&
947 0 : strchr("|*+?()[]{}.^$\\", ch))
948 0 : appendPQExpBufferChar(&namebuf, '\\');
949 2942 : i = PQmblen(cp, encoding);
950 8826 : while (i-- && *cp)
951 : {
952 2942 : appendPQExpBufferChar(&namebuf, *cp);
953 2942 : cp++;
954 : }
955 : }
956 : }
957 :
958 : /*
959 : * Now decide what we need to emit. Note there will be a leading "^(" in
960 : * the patterns in any case.
961 : */
962 257 : if (namebuf.len > 2)
963 : {
964 : /* We have a name pattern, so constrain the namevar(s) */
965 :
966 257 : appendPQExpBufferStr(&namebuf, ")$");
967 : /* Optimize away a "*" pattern */
968 257 : if (strcmp(namebuf.data, "^(.*)$") != 0)
969 : {
970 257 : WHEREAND();
971 257 : if (altnamevar)
972 : {
973 0 : appendPQExpBuffer(buf, "(%s ~ ", namevar);
974 0 : appendStringLiteralConn(buf, namebuf.data, conn);
975 0 : appendPQExpBuffer(buf, "\n OR %s ~ ", altnamevar);
976 0 : appendStringLiteralConn(buf, namebuf.data, conn);
977 0 : appendPQExpBufferStr(buf, ")\n");
978 : }
979 : else
980 : {
981 257 : appendPQExpBuffer(buf, "%s ~ ", namevar);
982 257 : appendStringLiteralConn(buf, namebuf.data, conn);
983 257 : appendPQExpBufferChar(buf, '\n');
984 : }
985 : }
986 : }
987 :
988 257 : if (schemabuf.len > 2)
989 : {
990 : /* We have a schema pattern, so constrain the schemavar */
991 :
992 15 : appendPQExpBufferStr(&schemabuf, ")$");
993 : /* Optimize away a "*" pattern */
994 15 : if (strcmp(schemabuf.data, "^(.*)$") != 0 && schemavar)
995 : {
996 15 : WHEREAND();
997 15 : appendPQExpBuffer(buf, "%s ~ ", schemavar);
998 15 : appendStringLiteralConn(buf, schemabuf.data, conn);
999 15 : appendPQExpBufferChar(buf, '\n');
1000 : }
1001 : }
1002 : else
1003 : {
1004 : /* No schema pattern given, so select only visible objects */
1005 242 : if (visibilityrule)
1006 : {
1007 234 : WHEREAND();
1008 234 : appendPQExpBuffer(buf, "%s\n", visibilityrule);
1009 : }
1010 : }
1011 :
1012 257 : termPQExpBuffer(&schemabuf);
1013 257 : termPQExpBuffer(&namebuf);
1014 :
1015 257 : return added_clause;
1016 : #undef WHEREAND
1017 : }
|