Age Owner TLA 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-2023, 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 "common/keywords.h"
21 : #include "fe_utils/string_utils.h"
22 :
23 : static PQExpBuffer defaultGetLocalPQExpBuffer(void);
24 :
25 : /* Globals exported by this file */
26 : int quote_all_identifiers = 0;
27 : PQExpBuffer (*getLocalPQExpBuffer) (void) = defaultGetLocalPQExpBuffer;
28 :
29 :
30 : /*
31 : * Returns a temporary PQExpBuffer, valid until the next call to the function.
32 : * This is used by fmtId and fmtQualifiedId.
33 : *
34 : * Non-reentrant and non-thread-safe but reduces memory leakage. You can
35 : * replace this with a custom version by setting the getLocalPQExpBuffer
36 : * function pointer.
37 : */
38 : static PQExpBuffer
2572 tgl 39 CBC 217278 : defaultGetLocalPQExpBuffer(void)
40 : {
41 : static PQExpBuffer id_return = NULL;
42 :
43 217278 : if (id_return) /* first time through? */
44 : {
45 : /* same buffer, just wipe contents */
46 216992 : resetPQExpBuffer(id_return);
47 : }
48 : else
49 : {
50 : /* new buffer */
51 286 : id_return = createPQExpBuffer();
52 : }
53 :
54 217278 : return id_return;
55 : }
56 :
57 : /*
58 : * Quotes input string if it's not a legitimate SQL identifier as-is.
59 : *
60 : * Note that the returned string must be used before calling fmtId again,
61 : * since we re-use the same return buffer each time.
62 : */
63 : const char *
64 184100 : fmtId(const char *rawid)
65 : {
66 184100 : PQExpBuffer id_return = getLocalPQExpBuffer();
67 :
68 : const char *cp;
69 184100 : bool need_quotes = false;
70 :
71 : /*
72 : * These checks need to match the identifier production in scan.l. Don't
73 : * use islower() etc.
74 : */
75 184100 : if (quote_all_identifiers)
76 13970 : need_quotes = true;
77 : /* slightly different rules for first character */
78 170130 : else if (!((rawid[0] >= 'a' && rawid[0] <= 'z') || rawid[0] == '_'))
79 293 : need_quotes = true;
80 : else
81 : {
82 : /* otherwise check the entire string */
83 1814641 : for (cp = rawid; *cp; cp++)
84 : {
85 1645013 : if (!((*cp >= 'a' && *cp <= 'z')
86 241833 : || (*cp >= '0' && *cp <= '9')
87 144628 : || (*cp == '_')))
88 : {
89 209 : need_quotes = true;
90 209 : break;
91 : }
92 : }
93 : }
94 :
95 184100 : if (!need_quotes)
96 : {
97 : /*
98 : * Check for keyword. We quote keywords except for unreserved ones.
99 : * (In some cases we could avoid quoting a col_name or type_func_name
100 : * keyword, but it seems much harder than it's worth to tell that.)
101 : *
102 : * Note: ScanKeywordLookup() does case-insensitive comparison, but
103 : * that's fine, since we already know we have all-lower-case.
104 : */
1554 105 169628 : int kwnum = ScanKeywordLookup(rawid, &ScanKeywords);
106 :
107 169628 : if (kwnum >= 0 && ScanKeywordCategories[kwnum] != UNRESERVED_KEYWORD)
2572 108 344 : need_quotes = true;
109 : }
110 :
111 184100 : if (!need_quotes)
112 : {
113 : /* no quoting needed */
114 169284 : appendPQExpBufferStr(id_return, rawid);
115 : }
116 : else
117 : {
118 14816 : appendPQExpBufferChar(id_return, '"');
119 150139 : for (cp = rawid; *cp; cp++)
120 : {
121 : /*
122 : * Did we find a double-quote in the string? Then make this a
123 : * double double-quote per SQL99. Before, we put in a
124 : * backslash/double-quote pair. - thomas 2000-08-05
125 : */
126 135323 : if (*cp == '"')
127 163 : appendPQExpBufferChar(id_return, '"');
128 135323 : appendPQExpBufferChar(id_return, *cp);
129 : }
130 14816 : appendPQExpBufferChar(id_return, '"');
131 : }
132 :
133 184100 : return id_return->data;
134 : }
135 :
136 : /*
137 : * fmtQualifiedId - construct a schema-qualified name, with quoting as needed.
138 : *
139 : * Like fmtId, use the result before calling again.
140 : *
141 : * Since we call fmtId and it also uses getLocalPQExpBuffer() we cannot
142 : * use that buffer until we're finished with calling fmtId().
143 : */
144 : const char *
1696 145 33178 : fmtQualifiedId(const char *schema, const char *id)
146 : {
147 : PQExpBuffer id_return;
2572 148 33178 : PQExpBuffer lcl_pqexp = createPQExpBuffer();
149 :
150 : /* Some callers might fail to provide a schema name */
1696 151 33178 : if (schema && *schema)
152 : {
2572 153 33178 : appendPQExpBuffer(lcl_pqexp, "%s.", fmtId(schema));
154 : }
155 33178 : appendPQExpBufferStr(lcl_pqexp, fmtId(id));
156 :
157 33178 : id_return = getLocalPQExpBuffer();
158 :
159 33178 : appendPQExpBufferStr(id_return, lcl_pqexp->data);
160 33178 : destroyPQExpBuffer(lcl_pqexp);
161 :
162 33178 : return id_return->data;
163 : }
164 :
165 :
166 : /*
167 : * Format a Postgres version number (in the PG_VERSION_NUM integer format
168 : * returned by PQserverVersion()) as a string. This exists mainly to
169 : * encapsulate knowledge about two-part vs. three-part version numbers.
170 : *
171 : * For reentrancy, caller must supply the buffer the string is put in.
172 : * Recommended size of the buffer is 32 bytes.
173 : *
174 : * Returns address of 'buf', as a notational convenience.
175 : */
176 : char *
2427 tgl 177 UBC 0 : formatPGVersionNumber(int version_number, bool include_minor,
178 : char *buf, size_t buflen)
179 : {
180 0 : if (version_number >= 100000)
181 : {
182 : /* New two-part style */
183 0 : if (include_minor)
184 0 : snprintf(buf, buflen, "%d.%d", version_number / 10000,
185 : version_number % 10000);
186 : else
187 0 : snprintf(buf, buflen, "%d", version_number / 10000);
188 : }
189 : else
190 : {
191 : /* Old three-part style */
192 0 : if (include_minor)
193 0 : snprintf(buf, buflen, "%d.%d.%d", version_number / 10000,
194 0 : (version_number / 100) % 100,
195 : version_number % 100);
196 : else
197 0 : snprintf(buf, buflen, "%d.%d", version_number / 10000,
198 0 : (version_number / 100) % 100);
199 : }
200 0 : return buf;
201 : }
202 :
203 :
204 : /*
205 : * Convert a string value to an SQL string literal and append it to
206 : * the given buffer. We assume the specified client_encoding and
207 : * standard_conforming_strings settings.
208 : *
209 : * This is essentially equivalent to libpq's PQescapeStringInternal,
210 : * except for the output buffer structure. We need it in situations
211 : * where we do not have a PGconn available. Where we do,
212 : * appendStringLiteralConn is a better choice.
213 : */
214 : void
2572 tgl 215 CBC 7711 : appendStringLiteral(PQExpBuffer buf, const char *str,
216 : int encoding, bool std_strings)
217 : {
218 7711 : size_t length = strlen(str);
219 7711 : const char *source = str;
220 : char *target;
221 :
222 7711 : if (!enlargePQExpBuffer(buf, 2 * length + 2))
2572 tgl 223 UBC 0 : return;
224 :
2572 tgl 225 CBC 7711 : target = buf->data + buf->len;
226 7711 : *target++ = '\'';
227 :
228 106251 : while (*source != '\0')
229 : {
230 98540 : char c = *source;
231 : int len;
232 : int i;
233 :
234 : /* Fast path for plain ASCII */
235 98540 : if (!IS_HIGHBIT_SET(c))
236 : {
237 : /* Apply quoting if needed */
238 98386 : if (SQL_STR_DOUBLE(c, !std_strings))
239 1958 : *target++ = c;
240 : /* Copy the character */
241 98386 : *target++ = c;
242 98386 : source++;
243 98386 : continue;
244 : }
245 :
246 : /* Slow path for possible multibyte characters */
247 154 : len = PQmblen(source, encoding);
248 :
249 : /* Copy the character */
250 308 : for (i = 0; i < len; i++)
251 : {
252 154 : if (*source == '\0')
2572 tgl 253 UBC 0 : break;
2572 tgl 254 CBC 154 : *target++ = *source++;
255 : }
256 :
257 : /*
258 : * If we hit premature end of string (ie, incomplete multibyte
259 : * character), try to pad out to the correct length with spaces. We
260 : * may not be able to pad completely, but we will always be able to
261 : * insert at least one pad space (since we'd not have quoted a
262 : * multibyte character). This should be enough to make a string that
263 : * the server will error out on.
264 : */
265 154 : if (i < len)
266 : {
2572 tgl 267 UBC 0 : char *stop = buf->data + buf->maxlen - 2;
268 :
269 0 : for (; i < len; i++)
270 : {
271 0 : if (target >= stop)
272 0 : break;
273 0 : *target++ = ' ';
274 : }
275 0 : break;
276 : }
277 : }
278 :
279 : /* Write the terminating quote and NUL character. */
2572 tgl 280 CBC 7711 : *target++ = '\'';
281 7711 : *target = '\0';
282 :
283 7711 : buf->len = target - buf->data;
284 : }
285 :
286 :
287 : /*
288 : * Convert a string value to an SQL string literal and append it to
289 : * the given buffer. Encoding and string syntax rules are as indicated
290 : * by current settings of the PGconn.
291 : */
292 : void
293 4006 : appendStringLiteralConn(PQExpBuffer buf, const char *str, PGconn *conn)
294 : {
295 4006 : size_t length = strlen(str);
296 :
297 : /*
298 : * XXX This is a kluge to silence escape_string_warning in our utility
299 : * programs. It should go away someday.
300 : */
301 4006 : if (strchr(str, '\\') != NULL && PQserverVersion(conn) >= 80100)
302 : {
303 : /* ensure we are not adjacent to an identifier */
304 716 : if (buf->len > 0 && buf->data[buf->len - 1] != ' ')
2572 tgl 305 UBC 0 : appendPQExpBufferChar(buf, ' ');
2572 tgl 306 CBC 716 : appendPQExpBufferChar(buf, ESCAPE_STRING_SYNTAX);
307 716 : appendStringLiteral(buf, str, PQclientEncoding(conn), false);
308 716 : return;
309 : }
310 : /* XXX end kluge */
311 :
312 3290 : if (!enlargePQExpBuffer(buf, 2 * length + 2))
2572 tgl 313 UBC 0 : return;
2572 tgl 314 CBC 3290 : appendPQExpBufferChar(buf, '\'');
315 3290 : buf->len += PQescapeStringConn(conn, buf->data + buf->len,
316 : str, length, NULL);
317 3290 : appendPQExpBufferChar(buf, '\'');
318 : }
319 :
320 :
321 : /*
322 : * Convert a string value to a dollar quoted literal and append it to
323 : * the given buffer. If the dqprefix parameter is not NULL then the
324 : * dollar quote delimiter will begin with that (after the opening $).
325 : *
326 : * No escaping is done at all on str, in compliance with the rules
327 : * for parsing dollar quoted strings. Also, we need not worry about
328 : * encoding issues.
329 : */
330 : void
331 1520 : appendStringLiteralDQ(PQExpBuffer buf, const char *str, const char *dqprefix)
332 : {
333 : static const char suffixes[] = "_XXXXXXX";
334 1520 : int nextchar = 0;
335 1520 : PQExpBuffer delimBuf = createPQExpBuffer();
336 :
337 : /* start with $ + dqprefix if not NULL */
338 1520 : appendPQExpBufferChar(delimBuf, '$');
339 1520 : if (dqprefix)
2572 tgl 340 UBC 0 : appendPQExpBufferStr(delimBuf, dqprefix);
341 :
342 : /*
343 : * Make sure we choose a delimiter which (without the trailing $) is not
344 : * present in the string being quoted. We don't check with the trailing $
345 : * because a string ending in $foo must not be quoted with $foo$.
346 : */
2572 tgl 347 CBC 2010 : while (strstr(str, delimBuf->data) != NULL)
348 : {
349 490 : appendPQExpBufferChar(delimBuf, suffixes[nextchar++]);
350 490 : nextchar %= sizeof(suffixes) - 1;
351 : }
352 :
353 : /* add trailing $ */
354 1520 : appendPQExpBufferChar(delimBuf, '$');
355 :
356 : /* quote it and we are all done */
357 1520 : appendPQExpBufferStr(buf, delimBuf->data);
358 1520 : appendPQExpBufferStr(buf, str);
359 1520 : appendPQExpBufferStr(buf, delimBuf->data);
360 :
361 1520 : destroyPQExpBuffer(delimBuf);
362 1520 : }
363 :
364 :
365 : /*
366 : * Convert a bytea value (presented as raw bytes) to an SQL string literal
367 : * and append it to the given buffer. We assume the specified
368 : * standard_conforming_strings setting.
369 : *
370 : * This is needed in situations where we do not have a PGconn available.
371 : * Where we do, PQescapeByteaConn is a better choice.
372 : */
373 : void
374 42 : appendByteaLiteral(PQExpBuffer buf, const unsigned char *str, size_t length,
375 : bool std_strings)
376 : {
377 42 : const unsigned char *source = str;
378 : char *target;
379 :
380 : static const char hextbl[] = "0123456789abcdef";
381 :
382 : /*
383 : * This implementation is hard-wired to produce hex-format output. We do
384 : * not know the server version the output will be loaded into, so making
385 : * an intelligent format choice is impossible. It might be better to
386 : * always use the old escaped format.
387 : */
388 42 : if (!enlargePQExpBuffer(buf, 2 * length + 5))
2572 tgl 389 UBC 0 : return;
390 :
2572 tgl 391 CBC 42 : target = buf->data + buf->len;
392 42 : *target++ = '\'';
393 42 : if (!std_strings)
2572 tgl 394 UBC 0 : *target++ = '\\';
2572 tgl 395 CBC 42 : *target++ = '\\';
396 42 : *target++ = 'x';
397 :
398 4062 : while (length-- > 0)
399 : {
400 4020 : unsigned char c = *source++;
401 :
402 4020 : *target++ = hextbl[(c >> 4) & 0xF];
403 4020 : *target++ = hextbl[c & 0xF];
404 : }
405 :
406 : /* Write the terminating quote and NUL character. */
407 42 : *target++ = '\'';
408 42 : *target = '\0';
409 :
410 42 : buf->len = target - buf->data;
411 : }
412 :
413 :
414 : /*
415 : * Append the given string to the shell command being built in the buffer,
416 : * with shell-style quoting as needed to create exactly one argument.
417 : *
418 : * Forbid LF or CR characters, which have scant practical use beyond designing
419 : * security breaches. The Windows command shell is unusable as a conduit for
420 : * arguments containing LF or CR characters. A future major release should
421 : * reject those characters in CREATE ROLE and CREATE DATABASE, because use
422 : * there eventually leads to errors here.
423 : *
424 : * appendShellString() simply prints an error and dies if LF or CR appears.
425 : * appendShellStringNoError() omits those characters from the result, and
426 : * returns false if there were any.
427 : */
428 : void
2435 noah 429 769 : appendShellString(PQExpBuffer buf, const char *str)
430 : {
2199 tgl 431 769 : if (!appendShellStringNoError(buf, str))
432 : {
433 1 : fprintf(stderr,
434 1 : _("shell command argument contains a newline or carriage return: \"%s\"\n"),
435 : str);
436 1 : exit(EXIT_FAILURE);
437 : }
438 768 : }
439 :
440 : bool
441 769 : appendShellStringNoError(PQExpBuffer buf, const char *str)
442 : {
443 : #ifdef WIN32
444 : int backslash_run_length = 0;
445 : #endif
446 769 : bool ok = true;
447 : const char *p;
448 :
449 : /*
450 : * Don't bother with adding quotes if the string is nonempty and clearly
451 : * contains only safe characters.
452 : */
2423 453 769 : if (*str != '\0' &&
2406 454 769 : strspn(str, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_./:") == strlen(str))
455 : {
2423 456 722 : appendPQExpBufferStr(buf, str);
2199 457 722 : return ok;
458 : }
459 :
460 : #ifndef WIN32
2435 noah 461 47 : appendPQExpBufferChar(buf, '\'');
462 2545 : for (p = str; *p; p++)
463 : {
464 2498 : if (*p == '\n' || *p == '\r')
465 : {
2199 tgl 466 2 : ok = false;
467 2 : continue;
468 : }
469 :
2435 noah 470 2496 : if (*p == '\'')
471 74 : appendPQExpBufferStr(buf, "'\"'\"'");
472 : else
473 2422 : appendPQExpBufferChar(buf, *p);
474 : }
475 47 : appendPQExpBufferChar(buf, '\'');
476 : #else /* WIN32 */
477 :
478 : /*
479 : * A Windows system() argument experiences two layers of interpretation.
480 : * First, cmd.exe interprets the string. Its behavior is undocumented,
481 : * but a caret escapes any byte except LF or CR that would otherwise have
482 : * special meaning. Handling of a caret before LF or CR differs between
483 : * "cmd.exe /c" and other modes, and it is unusable here.
484 : *
485 : * Second, the new process parses its command line to construct argv (see
486 : * https://msdn.microsoft.com/en-us/library/17w5ykft.aspx). This treats
487 : * backslash-double quote sequences specially.
488 : */
489 : appendPQExpBufferStr(buf, "^\"");
490 : for (p = str; *p; p++)
491 : {
492 : if (*p == '\n' || *p == '\r')
493 : {
494 : ok = false;
495 : continue;
496 : }
497 :
498 : /* Change N backslashes before a double quote to 2N+1 backslashes. */
499 : if (*p == '"')
500 : {
501 : while (backslash_run_length)
502 : {
503 : appendPQExpBufferStr(buf, "^\\");
504 : backslash_run_length--;
505 : }
506 : appendPQExpBufferStr(buf, "^\\");
507 : }
508 : else if (*p == '\\')
509 : backslash_run_length++;
510 : else
511 : backslash_run_length = 0;
512 :
513 : /*
514 : * Decline to caret-escape the most mundane characters, to ease
515 : * debugging and lest we approach the command length limit.
516 : */
517 : if (!((*p >= 'a' && *p <= 'z') ||
518 : (*p >= 'A' && *p <= 'Z') ||
519 : (*p >= '0' && *p <= '9')))
520 : appendPQExpBufferChar(buf, '^');
521 : appendPQExpBufferChar(buf, *p);
522 : }
523 :
524 : /*
525 : * Change N backslashes at end of argument to 2N backslashes, because they
526 : * precede the double quote that terminates the argument.
527 : */
528 : while (backslash_run_length)
529 : {
530 : appendPQExpBufferStr(buf, "^\\");
531 : backslash_run_length--;
532 : }
533 : appendPQExpBufferStr(buf, "^\"");
534 : #endif /* WIN32 */
535 :
2199 tgl 536 47 : return ok;
537 : }
538 :
539 :
540 : /*
541 : * Append the given string to the buffer, with suitable quoting for passing
542 : * the string as a value in a keyword/value pair in a libpq connection string.
543 : */
544 : void
2435 noah 545 483 : appendConnStrVal(PQExpBuffer buf, const char *str)
546 : {
547 : const char *s;
548 : bool needquotes;
549 :
550 : /*
551 : * If the string is one or more plain ASCII characters, no need to quote
552 : * it. This is quite conservative, but better safe than sorry.
553 : */
554 483 : needquotes = true;
555 3284 : for (s = str; *s; s++)
556 : {
557 2980 : if (!((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') ||
558 442 : (*s >= '0' && *s <= '9') || *s == '_' || *s == '.'))
559 : {
560 179 : needquotes = true;
561 179 : break;
562 : }
563 2801 : needquotes = false;
564 : }
565 :
566 483 : if (needquotes)
567 : {
568 179 : appendPQExpBufferChar(buf, '\'');
569 5276 : while (*str)
570 : {
571 : /* ' and \ must be escaped by to \' and \\ */
572 5097 : if (*str == '\'' || *str == '\\')
573 142 : appendPQExpBufferChar(buf, '\\');
574 :
575 5097 : appendPQExpBufferChar(buf, *str);
576 5097 : str++;
577 : }
578 179 : appendPQExpBufferChar(buf, '\'');
579 : }
580 : else
581 304 : appendPQExpBufferStr(buf, str);
582 483 : }
583 :
584 :
585 : /*
586 : * Append a psql meta-command that connects to the given database with the
587 : * then-current connection's user, host and port.
588 : */
589 : void
590 31 : appendPsqlMetaConnect(PQExpBuffer buf, const char *dbname)
591 : {
592 : const char *s;
593 : bool complex;
594 :
595 : /*
596 : * If the name is plain ASCII characters, emit a trivial "\connect "foo"".
597 : * For other names, even many not technically requiring it, skip to the
598 : * general case. No database has a zero-length name.
599 : */
600 31 : complex = false;
601 :
602 859 : for (s = dbname; *s; s++)
603 : {
604 828 : if (*s == '\n' || *s == '\r')
605 : {
2435 noah 606 UBC 0 : fprintf(stderr,
607 0 : _("database name contains a newline or carriage return: \"%s\"\n"),
608 : dbname);
609 0 : exit(EXIT_FAILURE);
610 : }
611 :
2435 noah 612 CBC 828 : if (!((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') ||
613 381 : (*s >= '0' && *s <= '9') || *s == '_' || *s == '.'))
614 : {
615 327 : complex = true;
616 : }
617 : }
618 :
619 31 : appendPQExpBufferStr(buf, "\\connect ");
620 31 : if (complex)
621 : {
622 : PQExpBufferData connstr;
623 :
624 10 : initPQExpBuffer(&connstr);
1375 drowley 625 10 : appendPQExpBufferStr(&connstr, "dbname=");
2435 noah 626 10 : appendConnStrVal(&connstr, dbname);
627 :
1375 drowley 628 10 : appendPQExpBufferStr(buf, "-reuse-previous=on ");
629 :
630 : /*
631 : * As long as the name does not contain a newline, SQL identifier
632 : * quoting satisfies the psql meta-command parser. Prefer not to
633 : * involve psql-interpreted single quotes, which behaved differently
634 : * before PostgreSQL 9.2.
635 : */
2435 noah 636 10 : appendPQExpBufferStr(buf, fmtId(connstr.data));
637 :
638 10 : termPQExpBuffer(&connstr);
639 : }
640 : else
641 21 : appendPQExpBufferStr(buf, fmtId(dbname));
642 31 : appendPQExpBufferChar(buf, '\n');
643 31 : }
644 :
645 :
646 : /*
647 : * Deconstruct the text representation of a 1-dimensional Postgres array
648 : * into individual items.
649 : *
650 : * On success, returns true and sets *itemarray and *nitems to describe
651 : * an array of individual strings. On parse failure, returns false;
652 : * *itemarray may exist or be NULL.
653 : *
654 : * NOTE: free'ing itemarray is sufficient to deallocate the working storage.
655 : */
656 : bool
2572 tgl 657 36717 : parsePGArray(const char *atext, char ***itemarray, int *nitems)
658 : {
659 : int inputlen;
660 : char **items;
661 : char *strings;
662 : int curitem;
663 :
664 : /*
665 : * We expect input in the form of "{item,item,item}" where any item is
666 : * either raw data, or surrounded by double quotes (in which case embedded
667 : * characters including backslashes and quotes are backslashed).
668 : *
669 : * We build the result as an array of pointers followed by the actual
670 : * string data, all in one malloc block for convenience of deallocation.
671 : * The worst-case storage need is not more than one pointer and one
672 : * character for each input character (consider "{,,,,,,,,,,}").
673 : */
674 36717 : *itemarray = NULL;
675 36717 : *nitems = 0;
676 36717 : inputlen = strlen(atext);
677 36717 : if (inputlen < 2 || atext[0] != '{' || atext[inputlen - 1] != '}')
2572 tgl 678 UBC 0 : return false; /* bad input */
2572 tgl 679 CBC 36717 : items = (char **) malloc(inputlen * (sizeof(char *) + sizeof(char)));
680 36717 : if (items == NULL)
2572 tgl 681 UBC 0 : return false; /* out of memory */
2572 tgl 682 CBC 36717 : *itemarray = items;
683 36717 : strings = (char *) (items + inputlen);
684 :
685 36717 : atext++; /* advance over initial '{' */
686 36717 : curitem = 0;
687 99949 : while (*atext != '}')
688 : {
689 63232 : if (*atext == '\0')
2572 tgl 690 UBC 0 : return false; /* premature end of string */
2572 tgl 691 CBC 63232 : items[curitem] = strings;
692 1124550 : while (*atext != '}' && *atext != ',')
693 : {
694 1061318 : if (*atext == '\0')
2572 tgl 695 UBC 0 : return false; /* premature end of string */
2572 tgl 696 CBC 1061318 : if (*atext != '"')
697 1061152 : *strings++ = *atext++; /* copy unquoted data */
698 : else
699 : {
700 : /* process quoted substring */
701 166 : atext++;
702 5612 : while (*atext != '"')
703 : {
704 5446 : if (*atext == '\0')
2572 tgl 705 UBC 0 : return false; /* premature end of string */
2572 tgl 706 CBC 5446 : if (*atext == '\\')
707 : {
708 850 : atext++;
709 850 : if (*atext == '\0')
2118 tgl 710 UBC 0 : return false; /* premature end of string */
711 : }
2118 tgl 712 CBC 5446 : *strings++ = *atext++; /* copy quoted data */
713 : }
2572 714 166 : atext++;
715 : }
716 : }
717 63232 : *strings++ = '\0';
718 63232 : if (*atext == ',')
719 27856 : atext++;
720 63232 : curitem++;
721 : }
722 36717 : if (atext[1] != '\0')
2572 tgl 723 UBC 0 : return false; /* bogus syntax (embedded '}') */
2572 tgl 724 CBC 36717 : *nitems = curitem;
725 36717 : return true;
726 : }
727 :
728 :
729 : /*
730 : * Append one element to the text representation of a 1-dimensional Postgres
731 : * array.
732 : *
733 : * The caller must provide the initial '{' and closing '}' of the array.
734 : * This function handles all else, including insertion of commas and
735 : * quoting of values.
736 : *
737 : * We assume that typdelim is ','.
738 : */
739 : void
489 740 230 : appendPGArray(PQExpBuffer buffer, const char *value)
741 : {
742 : bool needquote;
743 : const char *tmp;
744 :
745 230 : if (buffer->data[buffer->len - 1] != '{')
746 115 : appendPQExpBufferChar(buffer, ',');
747 :
748 : /* Decide if we need quotes; this should match array_out()'s choices. */
749 230 : if (value[0] == '\0')
489 tgl 750 UBC 0 : needquote = true; /* force quotes for empty string */
489 tgl 751 CBC 230 : else if (pg_strcasecmp(value, "NULL") == 0)
489 tgl 752 UBC 0 : needquote = true; /* force quotes for literal NULL */
753 : else
489 tgl 754 CBC 230 : needquote = false;
755 :
756 230 : if (!needquote)
757 : {
758 4150 : for (tmp = value; *tmp; tmp++)
759 : {
760 4020 : char ch = *tmp;
761 :
762 4020 : if (ch == '"' || ch == '\\' ||
763 3920 : ch == '{' || ch == '}' || ch == ',' ||
764 : /* these match array_isspace(): */
765 3920 : ch == ' ' || ch == '\t' || ch == '\n' ||
766 3920 : ch == '\r' || ch == '\v' || ch == '\f')
767 : {
768 100 : needquote = true;
769 100 : break;
770 : }
771 : }
772 : }
773 :
774 230 : if (needquote)
775 : {
776 100 : appendPQExpBufferChar(buffer, '"');
777 4350 : for (tmp = value; *tmp; tmp++)
778 : {
779 4250 : char ch = *tmp;
780 :
781 4250 : if (ch == '"' || ch == '\\')
782 750 : appendPQExpBufferChar(buffer, '\\');
783 4250 : appendPQExpBufferChar(buffer, ch);
784 : }
785 100 : appendPQExpBufferChar(buffer, '"');
786 : }
787 : else
788 130 : appendPQExpBufferStr(buffer, value);
789 230 : }
790 :
791 :
792 : /*
793 : * Format a reloptions array and append it to the given buffer.
794 : *
795 : * "prefix" is prepended to the option names; typically it's "" or "toast.".
796 : *
797 : * Returns false if the reloptions array could not be parsed (in which case
798 : * nothing will have been appended to the buffer), or true on success.
799 : *
800 : * Note: this logic should generally match the backend's flatten_reloptions()
801 : * (in adt/ruleutils.c).
802 : */
803 : bool
2529 dean.a.rasheed 804 207 : appendReloptionsArray(PQExpBuffer buffer, const char *reloptions,
805 : const char *prefix, int encoding, bool std_strings)
806 : {
807 : char **options;
808 : int noptions;
809 : int i;
810 :
811 207 : if (!parsePGArray(reloptions, &options, &noptions))
812 : {
297 peter 813 UNC 0 : free(options);
2529 dean.a.rasheed 814 UIC 0 : return false;
815 : }
2529 dean.a.rasheed 816 ECB :
2529 dean.a.rasheed 817 GIC 475 : for (i = 0; i < noptions; i++)
2529 dean.a.rasheed 818 ECB : {
2529 dean.a.rasheed 819 GIC 268 : char *option = options[i];
820 : char *name;
821 : char *separator;
822 : char *value;
823 :
824 : /*
825 : * Each array element should have the form name=value. If the "=" is
826 : * missing for some reason, treat it like an empty value.
2529 dean.a.rasheed 827 ECB : */
2529 dean.a.rasheed 828 CBC 268 : name = option;
829 268 : separator = strchr(option, '=');
2529 dean.a.rasheed 830 GIC 268 : if (separator)
2529 dean.a.rasheed 831 ECB : {
2529 dean.a.rasheed 832 CBC 268 : *separator = '\0';
2529 dean.a.rasheed 833 GIC 268 : value = separator + 1;
834 : }
2529 dean.a.rasheed 835 EUB : else
2529 dean.a.rasheed 836 UIC 0 : value = "";
2529 dean.a.rasheed 837 ECB :
2529 dean.a.rasheed 838 CBC 268 : if (i > 0)
839 61 : appendPQExpBufferStr(buffer, ", ");
2529 dean.a.rasheed 840 GIC 268 : appendPQExpBuffer(buffer, "%s%s=", prefix, fmtId(name));
841 :
842 : /*
843 : * In general we need to quote the value; but to avoid unnecessary
844 : * clutter, do not quote if it is an identifier that would not need
845 : * quoting. (We could also allow numbers, but that is a bit trickier
846 : * than it looks --- for example, are leading zeroes significant? We
847 : * don't want to assume very much here about what custom reloptions
848 : * might mean.)
2529 dean.a.rasheed 849 ECB : */
2529 dean.a.rasheed 850 CBC 268 : if (strcmp(fmtId(value), value) == 0)
2529 dean.a.rasheed 851 GIC 32 : appendPQExpBufferStr(buffer, value);
2529 dean.a.rasheed 852 ECB : else
2529 dean.a.rasheed 853 GIC 236 : appendStringLiteral(buffer, value, encoding, std_strings);
854 : }
2529 dean.a.rasheed 855 ECB :
297 peter 856 GNC 207 : free(options);
857 :
2529 dean.a.rasheed 858 GIC 207 : return true;
859 : }
860 :
861 :
862 : /*
863 : * processSQLNamePattern
864 : *
865 : * Scan a wildcard-pattern string and generate appropriate WHERE clauses
866 : * to limit the set of objects returned. The WHERE clauses are appended
867 : * to the already-partially-constructed query in buf. Returns whether
868 : * any clause was added.
869 : *
870 : * conn: connection query will be sent to (consulted for escaping rules).
871 : * buf: output parameter.
872 : * pattern: user-specified pattern option, or NULL if none ("*" is implied).
873 : * have_where: true if caller already emitted "WHERE" (clauses will be ANDed
874 : * onto the existing WHERE clause).
875 : * force_escape: always quote regexp special characters, even outside
876 : * double quotes (else they are quoted only between double quotes).
877 : * schemavar: name of query variable to match against a schema-name pattern.
878 : * Can be NULL if no schema.
879 : * namevar: name of query variable to match against an object-name pattern.
880 : * altnamevar: NULL, or name of an alternative variable to match against name.
881 : * visibilityrule: clause to use if we want to restrict to visible objects
882 : * (for example, "pg_catalog.pg_table_is_visible(p.oid)"). Can be NULL.
883 : * dbnamebuf: output parameter receiving the database name portion of the
884 : * pattern, if any. Can be NULL.
885 : * dotcnt: how many separators were parsed from the pattern, by reference.
886 : *
887 : * Formatting note: the text already present in buf should end with a newline.
888 : * The appended text, if any, will end with one too.
2572 tgl 889 ECB : */
890 : bool
2572 tgl 891 GIC 3108 : processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern,
892 : bool have_where, bool force_escape,
893 : const char *schemavar, const char *namevar,
894 : const char *altnamevar, const char *visibilityrule,
895 : PQExpBuffer dbnamebuf, int *dotcnt)
896 : {
2572 tgl 897 ECB : PQExpBufferData schemabuf;
898 : PQExpBufferData namebuf;
2572 tgl 899 GIC 3108 : bool added_clause = false;
900 : int dcnt;
901 :
902 : #define WHEREAND() \
903 : (appendPQExpBufferStr(buf, have_where ? " AND " : "WHERE "), \
2572 tgl 904 ECB : have_where = true, added_clause = true)
905 :
354 rhaas 906 CBC 3108 : if (dotcnt == NULL)
907 6 : dotcnt = &dcnt;
354 rhaas 908 GIC 3108 : *dotcnt = 0;
2572 tgl 909 3108 : if (pattern == NULL)
2572 tgl 910 ECB : {
911 : /* Default: select all visible objects */
2572 tgl 912 CBC 240 : if (visibilityrule)
2572 tgl 913 ECB : {
2572 tgl 914 GIC 57 : WHEREAND();
2572 tgl 915 CBC 57 : appendPQExpBuffer(buf, "%s\n", visibilityrule);
916 : }
2572 tgl 917 GIC 240 : return added_clause;
2572 tgl 918 ECB : }
919 :
2572 tgl 920 GIC 2868 : initPQExpBuffer(&schemabuf);
921 2868 : initPQExpBuffer(&namebuf);
922 :
923 : /*
924 : * Convert shell-style 'pattern' into the regular expression(s) we want to
925 : * execute. Quoting/escaping into SQL literal format will be done below
926 : * using appendStringLiteralConn().
927 : *
928 : * If the caller provided a schemavar, we want to split the pattern on
367 tgl 929 ECB : * ".", otherwise not.
930 : */
354 rhaas 931 GIC 2868 : patternToSQLRegex(PQclientEncoding(conn),
932 : (schemavar ? dbnamebuf : NULL),
933 : (schemavar ? &schemabuf : NULL),
934 : &namebuf,
935 : pattern, force_escape, true, dotcnt);
936 :
937 : /*
938 : * Now decide what we need to emit. We may run under a hostile
939 : * search_path, so qualify EVERY name. Note there will be a leading "^("
940 : * in the patterns in any case.
941 : *
942 : * We want the regex matches to use the database's default collation where
943 : * collation-sensitive behavior is required (for example, which characters
944 : * match '\w'). That happened by default before PG v12, but if the server
945 : * is >= v12 then we need to force it through explicit COLLATE clauses,
1465 tgl 946 ECB : * otherwise the "C" collation attached to "name" catalog columns wins.
947 : */
354 rhaas 948 GIC 2868 : if (namevar && namebuf.len > 2)
949 : {
950 : /* We have a name pattern, so constrain the namevar(s) */
2572 tgl 951 ECB :
952 : /* Optimize away a "*" pattern */
2572 tgl 953 CBC 2868 : if (strcmp(namebuf.data, "^(.*)$") != 0)
2572 tgl 954 ECB : {
2572 tgl 955 GIC 2826 : WHEREAND();
2572 tgl 956 CBC 2826 : if (altnamevar)
957 : {
1868 noah 958 108 : appendPQExpBuffer(buf,
1868 noah 959 ECB : "(%s OPERATOR(pg_catalog.~) ", namevar);
2572 tgl 960 CBC 108 : appendStringLiteralConn(buf, namebuf.data, conn);
1465 961 108 : if (PQserverVersion(conn) >= 120000)
1465 tgl 962 GIC 108 : appendPQExpBufferStr(buf, " COLLATE pg_catalog.default");
1868 noah 963 108 : appendPQExpBuffer(buf,
1868 noah 964 ECB : "\n OR %s OPERATOR(pg_catalog.~) ",
965 : altnamevar);
2572 tgl 966 CBC 108 : appendStringLiteralConn(buf, namebuf.data, conn);
1465 967 108 : if (PQserverVersion(conn) >= 120000)
1465 tgl 968 GIC 108 : appendPQExpBufferStr(buf, " COLLATE pg_catalog.default");
2572 969 108 : appendPQExpBufferStr(buf, ")\n");
970 : }
2572 tgl 971 ECB : else
972 : {
1868 noah 973 CBC 2718 : appendPQExpBuffer(buf, "%s OPERATOR(pg_catalog.~) ", namevar);
2572 tgl 974 2718 : appendStringLiteralConn(buf, namebuf.data, conn);
1465 975 2718 : if (PQserverVersion(conn) >= 120000)
1465 tgl 976 GIC 2718 : appendPQExpBufferStr(buf, " COLLATE pg_catalog.default");
2572 977 2718 : appendPQExpBufferChar(buf, '\n');
978 : }
979 : }
2572 tgl 980 ECB : }
981 :
354 rhaas 982 GIC 2868 : if (schemavar && schemabuf.len > 2)
983 : {
984 : /* We have a schema pattern, so constrain the schemavar */
2572 tgl 985 ECB :
986 : /* Optimize away a "*" pattern */
2572 tgl 987 CBC 704 : if (strcmp(schemabuf.data, "^(.*)$") != 0 && schemavar)
2572 tgl 988 ECB : {
2572 tgl 989 CBC 701 : WHEREAND();
1868 noah 990 701 : appendPQExpBuffer(buf, "%s OPERATOR(pg_catalog.~) ", schemavar);
2572 tgl 991 701 : appendStringLiteralConn(buf, schemabuf.data, conn);
1465 992 701 : if (PQserverVersion(conn) >= 120000)
1465 tgl 993 GIC 701 : appendPQExpBufferStr(buf, " COLLATE pg_catalog.default");
2572 994 701 : appendPQExpBufferChar(buf, '\n');
995 : }
996 : }
997 : else
2572 tgl 998 ECB : {
999 : /* No schema pattern given, so select only visible objects */
2572 tgl 1000 CBC 2164 : if (visibilityrule)
2572 tgl 1001 ECB : {
2572 tgl 1002 GIC 1691 : WHEREAND();
1003 1691 : appendPQExpBuffer(buf, "%s\n", visibilityrule);
1004 : }
2572 tgl 1005 ECB : }
1006 :
2572 tgl 1007 GIC 2868 : termPQExpBuffer(&schemabuf);
2572 tgl 1008 CBC 2868 : termPQExpBuffer(&namebuf);
1009 :
2572 tgl 1010 GIC 2868 : return added_clause;
1011 : #undef WHEREAND
1012 : }
1013 :
1014 : /*
1015 : * Transform a possibly qualified shell-style object name pattern into up to
1016 : * three SQL-style regular expressions, converting quotes, lower-casing
1017 : * unquoted letters, and adjusting shell-style wildcard characters into regexp
1018 : * notation.
1019 : *
1020 : * If the dbnamebuf and schemabuf arguments are non-NULL, and the pattern
1021 : * contains two or more dbname/schema/name separators, we parse the portions of
1022 : * the pattern prior to the first and second separators into dbnamebuf and
1023 : * schemabuf, and the rest into namebuf.
1024 : *
1025 : * If dbnamebuf is NULL and schemabuf is non-NULL, and the pattern contains at
1026 : * least one separator, we parse the first portion into schemabuf and the rest
1027 : * into namebuf.
1028 : *
1029 : * Otherwise, we parse all the pattern into namebuf.
1030 : *
1031 : * If the pattern contains more dotted parts than buffers to parse into, the
1032 : * extra dots will be treated as literal characters and written into the
1033 : * namebuf, though they will be counted. Callers should always check the value
1034 : * returned by reference in dotcnt and handle this error case appropriately.
1035 : *
1036 : * We surround the regexps with "^(...)$" to force them to match whole strings,
1037 : * as per SQL practice. We have to have parens in case strings contain "|",
1038 : * else the "^" and "$" will be bound into the first and last alternatives
1039 : * which is not what we want. Whether this is done for dbnamebuf is controlled
1040 : * by the want_literal_dbname parameter.
1041 : *
1042 : * The regexps we parse into the buffers are appended to the data (if any)
1043 : * already present. If we parse fewer fields than the number of buffers we
1044 : * were given, the extra buffers are unaltered.
1045 : *
1046 : * encoding: the character encoding for the given pattern
1047 : * dbnamebuf: output parameter receiving the database name portion of the
1048 : * pattern, if any. Can be NULL.
1049 : * schemabuf: output parameter receiving the schema name portion of the
1050 : * pattern, if any. Can be NULL.
1051 : * namebuf: output parameter receiving the database name portion of the
1052 : * pattern, if any. Can be NULL.
1053 : * pattern: user-specified pattern option, or NULL if none ("*" is implied).
1054 : * force_escape: always quote regexp special characters, even outside
1055 : * double quotes (else they are quoted only between double quotes).
1056 : * want_literal_dbname: if true, regexp special characters within the database
1057 : * name portion of the pattern will not be escaped, nor will the dbname be
1058 : * converted into a regular expression.
1059 : * dotcnt: output parameter receiving the number of separators parsed from the
1060 : * pattern.
795 rhaas 1061 ECB : */
1062 : void
795 rhaas 1063 GIC 2959 : patternToSQLRegex(int encoding, PQExpBuffer dbnamebuf, PQExpBuffer schemabuf,
1064 : PQExpBuffer namebuf, const char *pattern, bool force_escape,
1065 : bool want_literal_dbname, int *dotcnt)
1066 : {
1067 : PQExpBufferData buf[3];
1068 : PQExpBufferData left_literal;
1069 : PQExpBuffer curbuf;
1070 : PQExpBuffer maxbuf;
1071 : int i;
1072 : bool inquotes;
1073 : bool left;
795 rhaas 1074 ECB : const char *cp;
1075 :
795 rhaas 1076 GIC 2959 : Assert(pattern != NULL);
1077 2959 : Assert(namebuf != NULL);
795 rhaas 1078 ECB :
1079 : /* callers should never expect "dbname.relname" format */
795 rhaas 1080 GIC 2959 : Assert(dbnamebuf == NULL || schemabuf != NULL);
354 rhaas 1081 CBC 2959 : Assert(dotcnt != NULL);
795 rhaas 1082 ECB :
354 rhaas 1083 CBC 2959 : *dotcnt = 0;
795 rhaas 1084 GIC 2959 : inquotes = false;
795 rhaas 1085 CBC 2959 : cp = pattern;
795 rhaas 1086 ECB :
795 rhaas 1087 CBC 2959 : if (dbnamebuf != NULL)
1088 2437 : maxbuf = &buf[2];
795 rhaas 1089 GIC 522 : else if (schemabuf != NULL)
795 rhaas 1090 CBC 26 : maxbuf = &buf[1];
1091 : else
1092 496 : maxbuf = &buf[0];
795 rhaas 1093 ECB :
795 rhaas 1094 GIC 2959 : curbuf = &buf[0];
354 rhaas 1095 CBC 2959 : if (want_literal_dbname)
354 rhaas 1096 ECB : {
354 rhaas 1097 GIC 2868 : left = true;
1098 2868 : initPQExpBuffer(&left_literal);
354 rhaas 1099 ECB : }
1100 : else
354 rhaas 1101 CBC 91 : left = false;
795 1102 2959 : initPQExpBuffer(curbuf);
795 rhaas 1103 GIC 2959 : appendPQExpBufferStr(curbuf, "^(");
795 rhaas 1104 CBC 55138 : while (*cp)
1105 : {
1106 52179 : char ch = *cp;
1107 :
1108 52179 : if (ch == '"')
1109 : {
795 rhaas 1110 GIC 1593 : if (inquotes && cp[1] == '"')
795 rhaas 1111 ECB : {
1112 : /* emit one quote, stay in inquotes mode */
795 rhaas 1113 CBC 3 : appendPQExpBufferChar(curbuf, '"');
354 1114 3 : if (left)
354 rhaas 1115 GIC 3 : appendPQExpBufferChar(&left_literal, '"');
795 1116 3 : cp++;
795 rhaas 1117 ECB : }
1118 : else
795 rhaas 1119 GIC 1590 : inquotes = !inquotes;
795 rhaas 1120 CBC 1593 : cp++;
1121 : }
1122 50586 : else if (!inquotes && isupper((unsigned char) ch))
795 rhaas 1123 ECB : {
795 rhaas 1124 CBC 75 : appendPQExpBufferChar(curbuf,
1125 75 : pg_tolower((unsigned char) ch));
354 1126 75 : if (left)
1127 30 : appendPQExpBufferChar(&left_literal,
354 rhaas 1128 GIC 30 : pg_tolower((unsigned char) ch));
795 rhaas 1129 CBC 75 : cp++;
1130 : }
1131 50511 : else if (!inquotes && ch == '*')
795 rhaas 1132 ECB : {
795 rhaas 1133 CBC 170 : appendPQExpBufferStr(curbuf, ".*");
354 1134 170 : if (left)
354 rhaas 1135 GIC 118 : appendPQExpBufferChar(&left_literal, '*');
795 rhaas 1136 CBC 170 : cp++;
1137 : }
1138 50341 : else if (!inquotes && ch == '?')
795 rhaas 1139 ECB : {
795 rhaas 1140 CBC 3 : appendPQExpBufferChar(curbuf, '.');
354 1141 3 : if (left)
354 rhaas 1142 GIC 3 : appendPQExpBufferChar(&left_literal, '?');
795 rhaas 1143 CBC 3 : cp++;
1144 : }
354 1145 50338 : else if (!inquotes && ch == '.')
795 rhaas 1146 ECB : {
354 rhaas 1147 CBC 1397 : left = false;
1148 1397 : if (dotcnt)
354 rhaas 1149 GIC 1397 : (*dotcnt)++;
354 rhaas 1150 CBC 1397 : if (curbuf < maxbuf)
354 rhaas 1151 ECB : {
354 rhaas 1152 CBC 1105 : appendPQExpBufferStr(curbuf, ")$");
1153 1105 : curbuf++;
1154 1105 : initPQExpBuffer(curbuf);
354 rhaas 1155 GIC 1105 : appendPQExpBufferStr(curbuf, "^(");
1156 1105 : cp++;
354 rhaas 1157 ECB : }
1158 : else
354 rhaas 1159 CBC 292 : appendPQExpBufferChar(curbuf, *cp++);
1160 : }
795 rhaas 1161 GIC 48941 : else if (ch == '$')
1162 : {
1163 : /*
1164 : * Dollar is always quoted, whether inside quotes or not. The
1165 : * reason is that it's allowed in SQL identifiers, so there's a
1166 : * significant use-case for treating it literally, while because
1167 : * we anchor the pattern automatically there is no use-case for
795 rhaas 1168 ECB : * having it possess its regexp meaning.
1169 : */
795 rhaas 1170 CBC 6 : appendPQExpBufferStr(curbuf, "\\$");
354 1171 6 : if (left)
354 rhaas 1172 GIC 6 : appendPQExpBufferChar(&left_literal, '$');
795 1173 6 : cp++;
1174 : }
1175 : else
1176 : {
1177 : /*
1178 : * Ordinary data character, transfer to pattern
1179 : *
1180 : * Inside double quotes, or at all times if force_escape is true,
1181 : * quote regexp special characters with a backslash to avoid
1182 : * regexp errors. Outside quotes, however, let them pass through
1183 : * as-is; this lets knowledgeable users build regexp expressions
1184 : * that are more powerful than shell-style patterns.
1185 : *
1186 : * As an exception to that, though, always quote "[]", as that's
1187 : * much more likely to be an attempt to write an array type name
732 tgl 1188 ECB : * than it is to be the start of a regexp bracket expression.
795 rhaas 1189 : */
795 rhaas 1190 CBC 48935 : if ((inquotes || force_escape) &&
1191 14933 : strchr("|*+?()[]{}.^$\\", ch))
1192 1937 : appendPQExpBufferChar(curbuf, '\\');
732 tgl 1193 46998 : else if (ch == '[' && cp[1] == ']')
1194 3 : appendPQExpBufferChar(curbuf, '\\');
671 tgl 1195 GIC 48935 : i = PQmblenBounded(cp, encoding);
671 tgl 1196 CBC 97870 : while (i--)
354 rhaas 1197 ECB : {
354 rhaas 1198 CBC 48935 : if (left)
354 rhaas 1199 GIC 32384 : appendPQExpBufferChar(&left_literal, *cp);
671 tgl 1200 48935 : appendPQExpBufferChar(curbuf, *cp++);
1201 : }
795 rhaas 1202 ECB : }
1203 : }
795 rhaas 1204 CBC 2959 : appendPQExpBufferStr(curbuf, ")$");
1205 :
354 1206 2959 : if (namebuf)
795 rhaas 1207 ECB : {
354 rhaas 1208 CBC 2959 : appendPQExpBufferStr(namebuf, curbuf->data);
354 rhaas 1209 GIC 2959 : termPQExpBuffer(curbuf);
795 1210 2959 : curbuf--;
354 rhaas 1211 ECB : }
1212 :
354 rhaas 1213 CBC 2959 : if (schemabuf && curbuf >= buf)
354 rhaas 1214 ECB : {
795 rhaas 1215 CBC 729 : appendPQExpBufferStr(schemabuf, curbuf->data);
795 rhaas 1216 GIC 729 : termPQExpBuffer(curbuf);
354 1217 729 : curbuf--;
354 rhaas 1218 ECB : }
1219 :
354 rhaas 1220 CBC 2959 : if (dbnamebuf && curbuf >= buf)
354 rhaas 1221 ECB : {
354 rhaas 1222 GIC 376 : if (want_literal_dbname)
354 rhaas 1223 CBC 360 : appendPQExpBufferStr(dbnamebuf, left_literal.data);
354 rhaas 1224 ECB : else
795 rhaas 1225 GIC 16 : appendPQExpBufferStr(dbnamebuf, curbuf->data);
354 1226 376 : termPQExpBuffer(curbuf);
795 rhaas 1227 ECB : }
261 tgl 1228 :
261 tgl 1229 CBC 2959 : if (want_literal_dbname)
261 tgl 1230 GIC 2868 : termPQExpBuffer(&left_literal);
795 rhaas 1231 2959 : }
|