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