Branch data 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-2026, 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 "common/logging.h"
22 : : #include "fe_utils/string_utils.h"
23 : : #include "mb/pg_wchar.h"
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 : : static int fmtIdEncoding = -1;
32 : :
33 : :
34 : : /*
35 : : * Returns a temporary PQExpBuffer, valid until the next call to the function.
36 : : * This is used by fmtId and fmtQualifiedId.
37 : : *
38 : : * Non-reentrant and non-thread-safe but reduces memory leakage. You can
39 : : * replace this with a custom version by setting the getLocalPQExpBuffer
40 : : * function pointer.
41 : : */
42 : : static PQExpBuffer
43 : 378857 : defaultGetLocalPQExpBuffer(void)
44 : : {
45 : : static PQExpBuffer id_return = NULL;
46 : :
47 [ + + ]: 378857 : if (id_return) /* first time through? */
48 : : {
49 : : /* same buffer, just wipe contents */
50 : 378383 : resetPQExpBuffer(id_return);
51 : : }
52 : : else
53 : : {
54 : : /* new buffer */
55 : 474 : id_return = createPQExpBuffer();
56 : : }
57 : :
58 : 378857 : return id_return;
59 : : }
60 : :
61 : : /*
62 : : * Set the encoding that fmtId() and fmtQualifiedId() use.
63 : : *
64 : : * This is not safe against multiple connections having different encodings,
65 : : * but there is no real other way to address the need to know the encoding for
66 : : * fmtId()/fmtQualifiedId() input for safe escaping. Eventually we should get
67 : : * rid of fmtId().
68 : : */
69 : : void
70 : 11348 : setFmtEncoding(int encoding)
71 : : {
72 : 11348 : fmtIdEncoding = encoding;
73 : 11348 : }
74 : :
75 : : /*
76 : : * Return the currently configured encoding for fmtId() and fmtQualifiedId().
77 : : */
78 : : static int
79 : 253902 : getFmtEncoding(void)
80 : : {
81 [ + - ]: 253902 : if (fmtIdEncoding != -1)
82 : 253902 : return fmtIdEncoding;
83 : :
84 : : /*
85 : : * In assertion builds it seems best to fail hard if the encoding was not
86 : : * set, to make it easier to find places with missing calls. But in
87 : : * production builds that seems like a bad idea, thus we instead just
88 : : * default to UTF-8.
89 : : */
90 : : Assert(fmtIdEncoding != -1);
91 : :
92 : 0 : return PG_UTF8;
93 : : }
94 : :
95 : : /*
96 : : * Quotes input string if it's not a legitimate SQL identifier as-is.
97 : : *
98 : : * Note that the returned string must be used before calling fmtIdEnc again,
99 : : * since we re-use the same return buffer each time.
100 : : */
101 : : const char *
102 : 319588 : fmtIdEnc(const char *rawid, int encoding)
103 : : {
104 : 319588 : PQExpBuffer id_return = getLocalPQExpBuffer();
105 : :
106 : : const char *cp;
107 : 319588 : bool need_quotes = false;
108 : 319588 : size_t remaining = strlen(rawid);
109 : :
110 : : /*
111 : : * These checks need to match the identifier production in scan.l. Don't
112 : : * use islower() etc.
113 : : */
114 [ + + ]: 319588 : if (quote_all_identifiers)
115 : 24751 : need_quotes = true;
116 : : /* slightly different rules for first character */
117 [ + + - + : 294837 : else if (!((rawid[0] >= 'a' && rawid[0] <= 'z') || rawid[0] == '_'))
+ + ]
118 : 742 : need_quotes = true;
119 : : else
120 : : {
121 : : /* otherwise check the entire string */
122 : 294095 : cp = rawid;
123 [ + + ]: 3219016 : for (size_t i = 0; i < remaining; i++, cp++)
124 : : {
125 [ + + - + ]: 2935981 : if (!((*cp >= 'a' && *cp <= 'z')
126 [ + + + + ]: 386518 : || (*cp >= '0' && *cp <= '9')
127 [ + + ]: 269676 : || (*cp == '_')))
128 : : {
129 : 11060 : need_quotes = true;
130 : 11060 : break;
131 : : }
132 : : }
133 : : }
134 : :
135 [ + + ]: 319588 : if (!need_quotes)
136 : : {
137 : : /*
138 : : * Check for keyword. We quote keywords except for unreserved ones.
139 : : * (In some cases we could avoid quoting a col_name or type_func_name
140 : : * keyword, but it seems much harder than it's worth to tell that.)
141 : : *
142 : : * Note: ScanKeywordLookup() does case-insensitive comparison, but
143 : : * that's fine, since we already know we have all-lower-case.
144 : : */
145 : 283035 : int kwnum = ScanKeywordLookup(rawid, &ScanKeywords);
146 : :
147 [ + + + + ]: 283035 : if (kwnum >= 0 && ScanKeywordCategories[kwnum] != UNRESERVED_KEYWORD)
148 : 738 : need_quotes = true;
149 : : }
150 : :
151 [ + + ]: 319588 : if (!need_quotes)
152 : : {
153 : : /* no quoting needed */
154 : 282297 : appendPQExpBufferStr(id_return, rawid);
155 : : }
156 : : else
157 : : {
158 : 37291 : appendPQExpBufferChar(id_return, '"');
159 : :
160 : 37291 : cp = &rawid[0];
161 [ + + ]: 445903 : while (remaining > 0)
162 : : {
163 : : int charlen;
164 : :
165 : : /* Fast path for plain ASCII */
166 [ + + ]: 408612 : if (!IS_HIGHBIT_SET(*cp))
167 : : {
168 : : /*
169 : : * Did we find a double-quote in the string? Then make this a
170 : : * double double-quote per SQL99. Before, we put in a
171 : : * backslash/double-quote pair. - thomas 2000-08-05
172 : : */
173 [ + + ]: 406126 : if (*cp == '"')
174 : 344 : appendPQExpBufferChar(id_return, '"');
175 : 406126 : appendPQExpBufferChar(id_return, *cp);
176 : 406126 : remaining--;
177 : 406126 : cp++;
178 : 406126 : continue;
179 : : }
180 : :
181 : : /* Slow path for possible multibyte characters */
182 : 2486 : charlen = pg_encoding_mblen(encoding, cp);
183 : :
184 [ + + + + ]: 4945 : if (remaining < charlen ||
185 : 2459 : pg_encoding_verifymbchar(encoding, cp, charlen) == -1)
186 : : {
187 : : /*
188 : : * Multibyte character is invalid. It's important to verify
189 : : * that as invalid multibyte characters could e.g. be used to
190 : : * "skip" over quote characters, e.g. when parsing
191 : : * character-by-character.
192 : : *
193 : : * Replace the character's first byte with an invalid
194 : : * sequence. The invalid sequence ensures that the escaped
195 : : * string will trigger an error on the server-side, even if we
196 : : * can't directly report an error here.
197 : : *
198 : : * It would be a bit faster to verify the whole string the
199 : : * first time we encounter a set highbit, but this way we can
200 : : * replace just the invalid data, which probably makes it
201 : : * easier for users to find the invalidly encoded portion of a
202 : : * larger string.
203 : : */
204 [ + - ]: 40 : if (enlargePQExpBuffer(id_return, 2))
205 : : {
206 : 40 : pg_encoding_set_invalid(encoding,
207 : 40 : id_return->data + id_return->len);
208 : 40 : id_return->len += 2;
209 : 40 : id_return->data[id_return->len] = '\0';
210 : : }
211 : :
212 : : /*
213 : : * Handle the following bytes as if this byte didn't exist.
214 : : * That's safer in case the subsequent bytes contain
215 : : * characters that are significant for the caller (e.g. '>' in
216 : : * html).
217 : : */
218 : 40 : remaining--;
219 : 40 : cp++;
220 : : }
221 : : else
222 : : {
223 [ + + ]: 4905 : for (int i = 0; i < charlen; i++)
224 : : {
225 : 2459 : appendPQExpBufferChar(id_return, *cp);
226 : 2459 : remaining--;
227 : 2459 : cp++;
228 : : }
229 : : }
230 : : }
231 : :
232 : 37291 : appendPQExpBufferChar(id_return, '"');
233 : : }
234 : :
235 : 319588 : return id_return->data;
236 : : }
237 : :
238 : : /*
239 : : * Quotes input string if it's not a legitimate SQL identifier as-is.
240 : : *
241 : : * Note that the returned string must be used before calling fmtId again,
242 : : * since we re-use the same return buffer each time.
243 : : *
244 : : * NB: This assumes setFmtEncoding() previously has been called to configure
245 : : * the encoding of rawid. It is preferable to use fmtIdEnc() with an
246 : : * explicit encoding.
247 : : */
248 : : const char *
249 : 200979 : fmtId(const char *rawid)
250 : : {
251 : 200979 : return fmtIdEnc(rawid, getFmtEncoding());
252 : : }
253 : :
254 : : /*
255 : : * fmtQualifiedIdEnc - construct a schema-qualified name, with quoting as
256 : : * needed.
257 : : *
258 : : * Like fmtId, use the result before calling again.
259 : : *
260 : : * Since we call fmtId and it also uses getLocalPQExpBuffer() we cannot
261 : : * use that buffer until we're finished with calling fmtId().
262 : : */
263 : : const char *
264 : 59269 : fmtQualifiedIdEnc(const char *schema, const char *id, int encoding)
265 : : {
266 : : PQExpBuffer id_return;
267 : 59269 : PQExpBuffer lcl_pqexp = createPQExpBuffer();
268 : :
269 : : /* Some callers might fail to provide a schema name */
270 [ + - + - ]: 59269 : if (schema && *schema)
271 : : {
272 : 59269 : appendPQExpBuffer(lcl_pqexp, "%s.", fmtIdEnc(schema, encoding));
273 : : }
274 : 59269 : appendPQExpBufferStr(lcl_pqexp, fmtIdEnc(id, encoding));
275 : :
276 : 59269 : id_return = getLocalPQExpBuffer();
277 : :
278 : 59269 : appendPQExpBufferStr(id_return, lcl_pqexp->data);
279 : 59269 : destroyPQExpBuffer(lcl_pqexp);
280 : :
281 : 59269 : return id_return->data;
282 : : }
283 : :
284 : : /*
285 : : * fmtQualifiedId - construct a schema-qualified name, with quoting as needed.
286 : : *
287 : : * Like fmtId, use the result before calling again.
288 : : *
289 : : * Since we call fmtId and it also uses getLocalPQExpBuffer() we cannot
290 : : * use that buffer until we're finished with calling fmtId().
291 : : *
292 : : * NB: This assumes setFmtEncoding() previously has been called to configure
293 : : * the encoding of schema/id. It is preferable to use fmtQualifiedIdEnc()
294 : : * with an explicit encoding.
295 : : */
296 : : const char *
297 : 52923 : fmtQualifiedId(const char *schema, const char *id)
298 : : {
299 : 52923 : return fmtQualifiedIdEnc(schema, id, getFmtEncoding());
300 : : }
301 : :
302 : :
303 : : /*
304 : : * Format a Postgres version number (in the PG_VERSION_NUM integer format
305 : : * returned by PQserverVersion()) as a string. This exists mainly to
306 : : * encapsulate knowledge about two-part vs. three-part version numbers.
307 : : *
308 : : * For reentrancy, caller must supply the buffer the string is put in.
309 : : * Recommended size of the buffer is 32 bytes.
310 : : *
311 : : * Returns address of 'buf', as a notational convenience.
312 : : */
313 : : char *
314 : 0 : formatPGVersionNumber(int version_number, bool include_minor,
315 : : char *buf, size_t buflen)
316 : : {
317 [ # # ]: 0 : if (version_number >= 100000)
318 : : {
319 : : /* New two-part style */
320 [ # # ]: 0 : if (include_minor)
321 : 0 : snprintf(buf, buflen, "%d.%d", version_number / 10000,
322 : : version_number % 10000);
323 : : else
324 : 0 : snprintf(buf, buflen, "%d", version_number / 10000);
325 : : }
326 : : else
327 : : {
328 : : /* Old three-part style */
329 [ # # ]: 0 : if (include_minor)
330 : 0 : snprintf(buf, buflen, "%d.%d.%d", version_number / 10000,
331 : 0 : (version_number / 100) % 100,
332 : : version_number % 100);
333 : : else
334 : 0 : snprintf(buf, buflen, "%d.%d", version_number / 10000,
335 : 0 : (version_number / 100) % 100);
336 : : }
337 : 0 : return buf;
338 : : }
339 : :
340 : :
341 : : /*
342 : : * Convert a string value to an SQL string literal and append it to
343 : : * the given buffer. We assume the specified client_encoding and
344 : : * standard_conforming_strings settings.
345 : : *
346 : : * This is essentially equivalent to libpq's PQescapeStringInternal,
347 : : * except for the output buffer structure. We need it in situations
348 : : * where we do not have a PGconn available. Where we do,
349 : : * appendStringLiteralConn is a better choice.
350 : : */
351 : : void
352 : 40391 : appendStringLiteral(PQExpBuffer buf, const char *str,
353 : : int encoding, bool std_strings)
354 : : {
355 : 40391 : size_t length = strlen(str);
356 : 40391 : const char *source = str;
357 : : char *target;
358 : 40391 : size_t remaining = length;
359 : :
360 [ - + ]: 40391 : if (!enlargePQExpBuffer(buf, 2 * length + 2))
361 : 0 : return;
362 : :
363 : 40391 : target = buf->data + buf->len;
364 : 40391 : *target++ = '\'';
365 : :
366 [ + + ]: 1010375 : while (remaining > 0)
367 : : {
368 : 969984 : char c = *source;
369 : : int charlen;
370 : : int i;
371 : :
372 : : /* Fast path for plain ASCII */
373 [ + + ]: 969984 : if (!IS_HIGHBIT_SET(c))
374 : : {
375 : : /* Apply quoting if needed */
376 [ + + + + : 969930 : if (SQL_STR_DOUBLE(c, !std_strings))
- + ]
377 : 205 : *target++ = c;
378 : : /* Copy the character */
379 : 969930 : *target++ = c;
380 : 969930 : source++;
381 : 969930 : remaining--;
382 : 969930 : continue;
383 : : }
384 : :
385 : : /* Slow path for possible multibyte characters */
386 : 54 : charlen = PQmblen(source, encoding);
387 : :
388 [ + + + + ]: 81 : if (remaining < charlen ||
389 : 27 : pg_encoding_verifymbchar(encoding, source, charlen) == -1)
390 : : {
391 : : /*
392 : : * Multibyte character is invalid. It's important to verify that
393 : : * as invalid multibyte characters could e.g. be used to "skip"
394 : : * over quote characters, e.g. when parsing
395 : : * character-by-character.
396 : : *
397 : : * Replace the character's first byte with an invalid sequence.
398 : : * The invalid sequence ensures that the escaped string will
399 : : * trigger an error on the server-side, even if we can't directly
400 : : * report an error here.
401 : : *
402 : : * We know there's enough space for the invalid sequence because
403 : : * the "target" buffer is 2 * length + 2 long, and at worst we're
404 : : * replacing a single input byte with two invalid bytes.
405 : : *
406 : : * It would be a bit faster to verify the whole string the first
407 : : * time we encounter a set highbit, but this way we can replace
408 : : * just the invalid data, which probably makes it easier for users
409 : : * to find the invalidly encoded portion of a larger string.
410 : : */
411 : 40 : pg_encoding_set_invalid(encoding, target);
412 : 40 : target += 2;
413 : :
414 : : /*
415 : : * Handle the following bytes as if this byte didn't exist. That's
416 : : * safer in case the subsequent bytes contain important characters
417 : : * for the caller (e.g. '>' in html).
418 : : */
419 : 40 : source++;
420 : 40 : remaining--;
421 : : }
422 : : else
423 : : {
424 : : /* Copy the character */
425 [ + + ]: 41 : for (i = 0; i < charlen; i++)
426 : : {
427 : 27 : *target++ = *source++;
428 : 27 : remaining--;
429 : : }
430 : : }
431 : : }
432 : :
433 : : /* Write the terminating quote and NUL character. */
434 : 40391 : *target++ = '\'';
435 : 40391 : *target = '\0';
436 : :
437 : 40391 : buf->len = target - buf->data;
438 : : }
439 : :
440 : :
441 : : /*
442 : : * Convert a string value to an SQL string literal and append it to
443 : : * the given buffer. Encoding and string syntax rules are as indicated
444 : : * by current settings of the PGconn.
445 : : */
446 : : void
447 : 6376 : appendStringLiteralConn(PQExpBuffer buf, const char *str, PGconn *conn)
448 : : {
449 : 6376 : size_t length = strlen(str);
450 : :
451 : : /*
452 : : * XXX This is a kluge to silence escape_string_warning in our utility
453 : : * programs. It can go away once pre-v19 servers are out of support.
454 : : */
455 [ + + - + ]: 6376 : if (strchr(str, '\\') != NULL && PQserverVersion(conn) < 190000)
456 : : {
457 : : /* ensure we are not adjacent to an identifier */
458 [ # # # # ]: 0 : if (buf->len > 0 && buf->data[buf->len - 1] != ' ')
459 : 0 : appendPQExpBufferChar(buf, ' ');
460 : 0 : appendPQExpBufferChar(buf, ESCAPE_STRING_SYNTAX);
461 : 0 : appendStringLiteral(buf, str, PQclientEncoding(conn), false);
462 : 0 : return;
463 : : }
464 : : /* XXX end kluge */
465 : :
466 [ - + ]: 6376 : if (!enlargePQExpBuffer(buf, 2 * length + 2))
467 : 0 : return;
468 : 6376 : appendPQExpBufferChar(buf, '\'');
469 : 6376 : buf->len += PQescapeStringConn(conn, buf->data + buf->len,
470 : : str, length, NULL);
471 : 6376 : appendPQExpBufferChar(buf, '\'');
472 : : }
473 : :
474 : :
475 : : /*
476 : : * Convert a string value to a dollar quoted literal and append it to
477 : : * the given buffer. If the dqprefix parameter is not NULL then the
478 : : * dollar quote delimiter will begin with that (after the opening $).
479 : : *
480 : : * No escaping is done at all on str, in compliance with the rules
481 : : * for parsing dollar quoted strings. Also, we need not worry about
482 : : * encoding issues.
483 : : */
484 : : void
485 : 1642 : appendStringLiteralDQ(PQExpBuffer buf, const char *str, const char *dqprefix)
486 : : {
487 : : static const char suffixes[] = "_XXXXXXX";
488 : 1642 : int nextchar = 0;
489 : 1642 : PQExpBuffer delimBuf = createPQExpBuffer();
490 : :
491 : : /* start with $ + dqprefix if not NULL */
492 : 1642 : appendPQExpBufferChar(delimBuf, '$');
493 [ - + ]: 1642 : if (dqprefix)
494 : 0 : appendPQExpBufferStr(delimBuf, dqprefix);
495 : :
496 : : /*
497 : : * Make sure we choose a delimiter which (without the trailing $) is not
498 : : * present in the string being quoted. We don't check with the trailing $
499 : : * because a string ending in $foo must not be quoted with $foo$.
500 : : */
501 [ + + ]: 2173 : while (strstr(str, delimBuf->data) != NULL)
502 : : {
503 : 531 : appendPQExpBufferChar(delimBuf, suffixes[nextchar++]);
504 : 531 : nextchar %= sizeof(suffixes) - 1;
505 : : }
506 : :
507 : : /* add trailing $ */
508 : 1642 : appendPQExpBufferChar(delimBuf, '$');
509 : :
510 : : /* quote it and we are all done */
511 : 1642 : appendPQExpBufferStr(buf, delimBuf->data);
512 : 1642 : appendPQExpBufferStr(buf, str);
513 : 1642 : appendPQExpBufferStr(buf, delimBuf->data);
514 : :
515 : 1642 : destroyPQExpBuffer(delimBuf);
516 : 1642 : }
517 : :
518 : :
519 : : /*
520 : : * Convert a bytea value (presented as raw bytes) to an SQL string literal
521 : : * and append it to the given buffer. We assume the specified
522 : : * standard_conforming_strings setting.
523 : : *
524 : : * This is needed in situations where we do not have a PGconn available.
525 : : * Where we do, PQescapeByteaConn is a better choice.
526 : : */
527 : : void
528 : 45 : appendByteaLiteral(PQExpBuffer buf, const unsigned char *str, size_t length,
529 : : bool std_strings)
530 : : {
531 : 45 : const unsigned char *source = str;
532 : : char *target;
533 : :
534 : : static const char hextbl[] = "0123456789abcdef";
535 : :
536 : : /*
537 : : * This implementation is hard-wired to produce hex-format output. We do
538 : : * not know the server version the output will be loaded into, so making
539 : : * an intelligent format choice is impossible. It might be better to
540 : : * always use the old escaped format.
541 : : */
542 [ - + ]: 45 : if (!enlargePQExpBuffer(buf, 2 * length + 5))
543 : 0 : return;
544 : :
545 : 45 : target = buf->data + buf->len;
546 : 45 : *target++ = '\'';
547 [ - + ]: 45 : if (!std_strings)
548 : 0 : *target++ = '\\';
549 : 45 : *target++ = '\\';
550 : 45 : *target++ = 'x';
551 : :
552 [ + + ]: 4119 : while (length-- > 0)
553 : : {
554 : 4074 : unsigned char c = *source++;
555 : :
556 : 4074 : *target++ = hextbl[(c >> 4) & 0xF];
557 : 4074 : *target++ = hextbl[c & 0xF];
558 : : }
559 : :
560 : : /* Write the terminating quote and NUL character. */
561 : 45 : *target++ = '\'';
562 : 45 : *target = '\0';
563 : :
564 : 45 : buf->len = target - buf->data;
565 : : }
566 : :
567 : :
568 : : /*
569 : : * Append the given string to the shell command being built in the buffer,
570 : : * with shell-style quoting as needed to create exactly one argument.
571 : : *
572 : : * Forbid LF or CR characters, which have scant practical use beyond designing
573 : : * security breaches. The Windows command shell is unusable as a conduit for
574 : : * arguments containing LF or CR characters.
575 : : *
576 : : * appendShellString() simply prints an error and dies if LF or CR appears.
577 : : * appendShellStringNoError() omits those characters from the result, and
578 : : * returns false if there were any.
579 : : */
580 : : void
581 : 557 : appendShellString(PQExpBuffer buf, const char *str)
582 : : {
583 [ - + ]: 557 : if (!appendShellStringNoError(buf, str))
584 : 0 : pg_fatal("shell command argument contains a newline or carriage return: \"%s\"", str);
585 : 557 : }
586 : :
587 : : bool
588 : 557 : appendShellStringNoError(PQExpBuffer buf, const char *str)
589 : : {
590 : : #ifdef WIN32
591 : : int backslash_run_length = 0;
592 : : #endif
593 : 557 : bool ok = true;
594 : : const char *p;
595 : :
596 : : /*
597 : : * Don't bother with adding quotes if the string is nonempty and clearly
598 : : * contains only safe characters.
599 : : */
600 [ + - ]: 557 : if (*str != '\0' &&
601 [ + + ]: 557 : strspn(str, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_./:") == strlen(str))
602 : : {
603 : 469 : appendPQExpBufferStr(buf, str);
604 : 469 : return ok;
605 : : }
606 : :
607 : : #ifndef WIN32
608 : 88 : appendPQExpBufferChar(buf, '\'');
609 [ + + ]: 3412 : for (p = str; *p; p++)
610 : : {
611 [ + - - + ]: 3324 : if (*p == '\n' || *p == '\r')
612 : : {
613 : 0 : ok = false;
614 : 0 : continue;
615 : : }
616 : :
617 [ + + ]: 3324 : if (*p == '\'')
618 : 84 : appendPQExpBufferStr(buf, "'\"'\"'");
619 : : else
620 : 3240 : appendPQExpBufferChar(buf, *p);
621 : : }
622 : 88 : appendPQExpBufferChar(buf, '\'');
623 : : #else /* WIN32 */
624 : :
625 : : /*
626 : : * A Windows system() argument experiences two layers of interpretation.
627 : : * First, cmd.exe interprets the string. Its behavior is undocumented,
628 : : * but a caret escapes any byte except LF or CR that would otherwise have
629 : : * special meaning. Handling of a caret before LF or CR differs between
630 : : * "cmd.exe /c" and other modes, and it is unusable here.
631 : : *
632 : : * Second, the new process parses its command line to construct argv (see
633 : : * https://msdn.microsoft.com/en-us/library/17w5ykft.aspx). This treats
634 : : * backslash-double quote sequences specially.
635 : : */
636 : : appendPQExpBufferStr(buf, "^\"");
637 : : for (p = str; *p; p++)
638 : : {
639 : : if (*p == '\n' || *p == '\r')
640 : : {
641 : : ok = false;
642 : : continue;
643 : : }
644 : :
645 : : /* Change N backslashes before a double quote to 2N+1 backslashes. */
646 : : if (*p == '"')
647 : : {
648 : : while (backslash_run_length)
649 : : {
650 : : appendPQExpBufferStr(buf, "^\\");
651 : : backslash_run_length--;
652 : : }
653 : : appendPQExpBufferStr(buf, "^\\");
654 : : }
655 : : else if (*p == '\\')
656 : : backslash_run_length++;
657 : : else
658 : : backslash_run_length = 0;
659 : :
660 : : /*
661 : : * Decline to caret-escape the most mundane characters, to ease
662 : : * debugging and lest we approach the command length limit.
663 : : */
664 : : if (!((*p >= 'a' && *p <= 'z') ||
665 : : (*p >= 'A' && *p <= 'Z') ||
666 : : (*p >= '0' && *p <= '9')))
667 : : appendPQExpBufferChar(buf, '^');
668 : : appendPQExpBufferChar(buf, *p);
669 : : }
670 : :
671 : : /*
672 : : * Change N backslashes at end of argument to 2N backslashes, because they
673 : : * precede the double quote that terminates the argument.
674 : : */
675 : : while (backslash_run_length)
676 : : {
677 : : appendPQExpBufferStr(buf, "^\\");
678 : : backslash_run_length--;
679 : : }
680 : : appendPQExpBufferStr(buf, "^\"");
681 : : #endif /* WIN32 */
682 : :
683 : 88 : return ok;
684 : : }
685 : :
686 : :
687 : : /*
688 : : * Append the given string to the buffer, with suitable quoting for passing
689 : : * the string as a value in a keyword/value pair in a libpq connection string.
690 : : */
691 : : void
692 : 2620 : appendConnStrVal(PQExpBuffer buf, const char *str)
693 : : {
694 : : const char *s;
695 : : bool needquotes;
696 : :
697 : : /*
698 : : * If the string is one or more plain ASCII characters, no need to quote
699 : : * it. This is quite conservative, but better safe than sorry.
700 : : */
701 : 2620 : needquotes = true;
702 [ + + ]: 17809 : for (s = str; *s; s++)
703 : : {
704 [ + + - + : 16063 : if (!((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') ||
+ + + + ]
705 [ + + + + : 2023 : (*s >= '0' && *s <= '9') || *s == '_' || *s == '.'))
+ + + + ]
706 : : {
707 : 874 : needquotes = true;
708 : 874 : break;
709 : : }
710 : 15189 : needquotes = false;
711 : : }
712 : :
713 [ + + ]: 2620 : if (needquotes)
714 : : {
715 : 874 : appendPQExpBufferChar(buf, '\'');
716 [ + + ]: 18231 : while (*str)
717 : : {
718 : : /* ' and \ must be escaped by to \' and \\ */
719 [ + + + + ]: 17357 : if (*str == '\'' || *str == '\\')
720 : 302 : appendPQExpBufferChar(buf, '\\');
721 : :
722 : 17357 : appendPQExpBufferChar(buf, *str);
723 : 17357 : str++;
724 : : }
725 : 874 : appendPQExpBufferChar(buf, '\'');
726 : : }
727 : : else
728 : 1746 : appendPQExpBufferStr(buf, str);
729 : 2620 : }
730 : :
731 : :
732 : : /*
733 : : * Append a psql meta-command that connects to the given database with the
734 : : * then-current connection's user, host and port.
735 : : */
736 : : void
737 : 35 : appendPsqlMetaConnect(PQExpBuffer buf, const char *dbname)
738 : : {
739 : : const char *s;
740 : : bool complex;
741 : :
742 : : /*
743 : : * If the name is plain ASCII characters, emit a trivial "\connect "foo"".
744 : : * For other names, even many not technically requiring it, skip to the
745 : : * general case. No database has a zero-length name.
746 : : */
747 : 35 : complex = false;
748 : :
749 [ + + ]: 911 : for (s = dbname; *s; s++)
750 : : {
751 [ + - - + ]: 876 : if (*s == '\n' || *s == '\r')
752 : 0 : pg_fatal("database name contains a newline or carriage return: \"%s\"", dbname);
753 : :
754 [ + + + + : 876 : if (!((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') ||
+ + + + ]
755 [ + + + + : 385 : (*s >= '0' && *s <= '9') || *s == '_' || *s == '.'))
+ + + + ]
756 : : {
757 : 327 : complex = true;
758 : : }
759 : : }
760 : :
761 [ + + ]: 35 : if (complex)
762 : : {
763 : : PQExpBufferData connstr;
764 : :
765 : 10 : initPQExpBuffer(&connstr);
766 : :
767 : : /*
768 : : * Force the target psql's encoding to SQL_ASCII. We don't really
769 : : * know the encoding of the database name, and it doesn't matter as
770 : : * long as psql will forward it to the server unchanged.
771 : : */
772 : 10 : appendPQExpBufferStr(buf, "\\encoding SQL_ASCII\n");
773 : 10 : appendPQExpBufferStr(buf, "\\connect -reuse-previous=on ");
774 : :
775 : 10 : appendPQExpBufferStr(&connstr, "dbname=");
776 : 10 : appendConnStrVal(&connstr, dbname);
777 : :
778 : : /*
779 : : * As long as the name does not contain a newline, SQL identifier
780 : : * quoting satisfies the psql meta-command parser. Prefer not to
781 : : * involve psql-interpreted single quotes, which behaved differently
782 : : * before PostgreSQL 9.2.
783 : : */
784 : 10 : appendPQExpBufferStr(buf, fmtIdEnc(connstr.data, PG_SQL_ASCII));
785 : :
786 : 10 : termPQExpBuffer(&connstr);
787 : : }
788 : : else
789 : : {
790 : 25 : appendPQExpBufferStr(buf, "\\connect ");
791 : 25 : appendPQExpBufferStr(buf, fmtIdEnc(dbname, PG_SQL_ASCII));
792 : : }
793 : 35 : appendPQExpBufferChar(buf, '\n');
794 : 35 : }
795 : :
796 : :
797 : : /*
798 : : * SplitGUCList --- parse a string containing identifiers or file names
799 : : *
800 : : * This is used to split the value of a GUC_LIST_QUOTE GUC variable, without
801 : : * presuming whether the elements will be taken as identifiers or file names.
802 : : * See comparable code in src/backend/utils/adt/varlena.c.
803 : : *
804 : : * Inputs:
805 : : * rawstring: the input string; must be overwritable! On return, it's
806 : : * been modified to contain the separated identifiers.
807 : : * separator: the separator punctuation expected between identifiers
808 : : * (typically '.' or ','). Whitespace may also appear around
809 : : * identifiers.
810 : : * Outputs:
811 : : * namelist: receives a malloc'd, null-terminated array of pointers to
812 : : * identifiers within rawstring. Caller should free this
813 : : * even on error return.
814 : : *
815 : : * Returns true if okay, false if there is a syntax error in the string.
816 : : */
817 : : bool
818 : 18 : SplitGUCList(char *rawstring, char separator,
819 : : char ***namelist)
820 : : {
821 : 18 : char *nextp = rawstring;
822 : 18 : bool done = false;
823 : : char **nextptr;
824 : :
825 : : /*
826 : : * Since we disallow empty identifiers, this is a conservative
827 : : * overestimate of the number of pointers we could need. Allow one for
828 : : * list terminator.
829 : : */
830 : 18 : *namelist = nextptr =
831 : 18 : pg_malloc_array(char *, (strlen(rawstring) / 2 + 2));
832 : 18 : *nextptr = NULL;
833 : :
834 [ - + ]: 18 : while (isspace((unsigned char) *nextp))
835 : 0 : nextp++; /* skip leading whitespace */
836 : :
837 [ + + ]: 18 : if (*nextp == '\0')
838 : 5 : return true; /* empty string represents empty list */
839 : :
840 : : /* At the top of the loop, we are at start of a new identifier. */
841 : : do
842 : : {
843 : : char *curname;
844 : : char *endp;
845 : :
846 [ + + ]: 30 : if (*nextp == '"')
847 : : {
848 : : /* Quoted name --- collapse quote-quote pairs */
849 : 20 : curname = nextp + 1;
850 : : for (;;)
851 : : {
852 : 30 : endp = strchr(nextp + 1, '"');
853 [ - + ]: 25 : if (endp == NULL)
854 : 0 : return false; /* mismatched quotes */
855 [ + + ]: 25 : if (endp[1] != '"')
856 : 20 : break; /* found end of quoted name */
857 : : /* Collapse adjacent quotes into one quote, and look again */
858 : 5 : memmove(endp, endp + 1, strlen(endp));
859 : 5 : nextp = endp;
860 : : }
861 : : /* endp now points at the terminating quote */
862 : 20 : nextp = endp + 1;
863 : : }
864 : : else
865 : : {
866 : : /* Unquoted name --- extends to separator or whitespace */
867 : 10 : curname = nextp;
868 [ + + + + ]: 110 : while (*nextp && *nextp != separator &&
869 [ + - ]: 100 : !isspace((unsigned char) *nextp))
870 : 100 : nextp++;
871 : 10 : endp = nextp;
872 [ - + ]: 10 : if (curname == nextp)
873 : 0 : return false; /* empty unquoted name not allowed */
874 : : }
875 : :
876 [ - + ]: 30 : while (isspace((unsigned char) *nextp))
877 : 0 : nextp++; /* skip trailing whitespace */
878 : :
879 [ + + ]: 30 : if (*nextp == separator)
880 : : {
881 : 17 : nextp++;
882 [ + + ]: 34 : while (isspace((unsigned char) *nextp))
883 : 17 : nextp++; /* skip leading whitespace for next */
884 : : /* we expect another name, so done remains false */
885 : : }
886 [ + - ]: 13 : else if (*nextp == '\0')
887 : 13 : done = true;
888 : : else
889 : 0 : return false; /* invalid syntax */
890 : :
891 : : /* Now safe to overwrite separator with a null */
892 : 30 : *endp = '\0';
893 : :
894 : : /*
895 : : * Finished isolating current name --- add it to output array
896 : : */
897 : 30 : *nextptr++ = curname;
898 : :
899 : : /* Loop back if we didn't reach end of string */
900 [ + + ]: 30 : } while (!done);
901 : :
902 : 13 : *nextptr = NULL;
903 : 13 : return true;
904 : : }
905 : :
906 : :
907 : : /*
908 : : * Deconstruct the text representation of a 1-dimensional Postgres array
909 : : * into individual items.
910 : : *
911 : : * On success, returns true and sets *itemarray and *nitems to describe
912 : : * an array of individual strings. On parse failure, returns false;
913 : : * *itemarray may exist or be NULL.
914 : : *
915 : : * NOTE: free'ing itemarray is sufficient to deallocate the working storage.
916 : : */
917 : : bool
918 : 65003 : parsePGArray(const char *atext, char ***itemarray, int *nitems)
919 : : {
920 : : int inputlen;
921 : : char **items;
922 : : char *strings;
923 : : int curitem;
924 : :
925 : : /*
926 : : * We expect input in the form of "{item,item,item}" where any item is
927 : : * either raw data, or surrounded by double quotes (in which case embedded
928 : : * characters including backslashes and quotes are backslashed).
929 : : *
930 : : * We build the result as an array of pointers followed by the actual
931 : : * string data, all in one malloc block for convenience of deallocation.
932 : : * The worst-case storage need is not more than one pointer and one
933 : : * character for each input character (consider "{,,,,,,,,,,}").
934 : : */
935 : 65003 : *itemarray = NULL;
936 : 65003 : *nitems = 0;
937 : 65003 : inputlen = strlen(atext);
938 [ + - + - : 65003 : if (inputlen < 2 || atext[0] != '{' || atext[inputlen - 1] != '}')
- + ]
939 : 0 : return false; /* bad input */
940 : 65003 : items = (char **) malloc(inputlen * (sizeof(char *) + sizeof(char)));
941 [ - + ]: 65003 : if (items == NULL)
942 : 0 : return false; /* out of memory */
943 : 65003 : *itemarray = items;
944 : 65003 : strings = (char *) (items + inputlen);
945 : :
946 : 65003 : atext++; /* advance over initial '{' */
947 : 65003 : curitem = 0;
948 [ + + ]: 178467 : while (*atext != '}')
949 : : {
950 [ - + ]: 113464 : if (*atext == '\0')
951 : 0 : return false; /* premature end of string */
952 : 113464 : items[curitem] = strings;
953 [ + + + + ]: 2267924 : while (*atext != '}' && *atext != ',')
954 : : {
955 [ - + ]: 2154460 : if (*atext == '\0')
956 : 0 : return false; /* premature end of string */
957 [ + + ]: 2154460 : if (*atext != '"')
958 : 2154265 : *strings++ = *atext++; /* copy unquoted data */
959 : : else
960 : : {
961 : : /* process quoted substring */
962 : 195 : atext++;
963 [ + + ]: 6634 : while (*atext != '"')
964 : : {
965 [ - + ]: 6439 : if (*atext == '\0')
966 : 0 : return false; /* premature end of string */
967 [ + + ]: 6439 : if (*atext == '\\')
968 : : {
969 : 975 : atext++;
970 [ - + ]: 975 : if (*atext == '\0')
971 : 0 : return false; /* premature end of string */
972 : : }
973 : 6439 : *strings++ = *atext++; /* copy quoted data */
974 : : }
975 : 195 : atext++;
976 : : }
977 : : }
978 : 113464 : *strings++ = '\0';
979 [ + + ]: 113464 : if (*atext == ',')
980 : 49882 : atext++;
981 : 113464 : curitem++;
982 : : }
983 [ - + ]: 65003 : if (atext[1] != '\0')
984 : 0 : return false; /* bogus syntax (embedded '}') */
985 : 65003 : *nitems = curitem;
986 : 65003 : return true;
987 : : }
988 : :
989 : :
990 : : /*
991 : : * Append one element to the text representation of a 1-dimensional Postgres
992 : : * array.
993 : : *
994 : : * The caller must provide the initial '{' and closing '}' of the array.
995 : : * This function handles all else, including insertion of commas and
996 : : * quoting of values.
997 : : *
998 : : * We assume that typdelim is ','.
999 : : */
1000 : : void
1001 : 3997 : appendPGArray(PQExpBuffer buffer, const char *value)
1002 : : {
1003 : : bool needquote;
1004 : : const char *tmp;
1005 : :
1006 [ + + ]: 3997 : if (buffer->data[buffer->len - 1] != '{')
1007 : 3695 : appendPQExpBufferChar(buffer, ',');
1008 : :
1009 : : /* Decide if we need quotes; this should match array_out()'s choices. */
1010 [ - + ]: 3997 : if (value[0] == '\0')
1011 : 0 : needquote = true; /* force quotes for empty string */
1012 [ - + ]: 3997 : else if (pg_strcasecmp(value, "NULL") == 0)
1013 : 0 : needquote = true; /* force quotes for literal NULL */
1014 : : else
1015 : 3997 : needquote = false;
1016 : :
1017 [ + - ]: 3997 : if (!needquote)
1018 : : {
1019 [ + + ]: 30187 : for (tmp = value; *tmp; tmp++)
1020 : : {
1021 : 26296 : char ch = *tmp;
1022 : :
1023 [ + + + - : 26296 : if (ch == '"' || ch == '\\' ||
+ - ]
1024 [ + - + - : 26190 : ch == '{' || ch == '}' || ch == ',' ||
+ - ]
1025 : : /* these match scanner_isspace(): */
1026 [ + - + - : 26190 : ch == ' ' || ch == '\t' || ch == '\n' ||
+ - ]
1027 [ + - - + ]: 26190 : ch == '\r' || ch == '\v' || ch == '\f')
1028 : : {
1029 : 106 : needquote = true;
1030 : 106 : break;
1031 : : }
1032 : : }
1033 : : }
1034 : :
1035 [ + + ]: 3997 : if (needquote)
1036 : : {
1037 : 106 : appendPQExpBufferChar(buffer, '"');
1038 [ + + ]: 4611 : for (tmp = value; *tmp; tmp++)
1039 : : {
1040 : 4505 : char ch = *tmp;
1041 : :
1042 [ + + + + ]: 4505 : if (ch == '"' || ch == '\\')
1043 : 795 : appendPQExpBufferChar(buffer, '\\');
1044 : 4505 : appendPQExpBufferChar(buffer, ch);
1045 : : }
1046 : 106 : appendPQExpBufferChar(buffer, '"');
1047 : : }
1048 : : else
1049 : 3891 : appendPQExpBufferStr(buffer, value);
1050 : 3997 : }
1051 : :
1052 : :
1053 : : /*
1054 : : * Format a reloptions array and append it to the given buffer.
1055 : : *
1056 : : * "prefix" is prepended to the option names; typically it's "" or "toast.".
1057 : : *
1058 : : * Returns false if the reloptions array could not be parsed (in which case
1059 : : * nothing will have been appended to the buffer), or true on success.
1060 : : *
1061 : : * Note: this logic should generally match the backend's flatten_reloptions()
1062 : : * (in adt/ruleutils.c).
1063 : : */
1064 : : bool
1065 : 223 : appendReloptionsArray(PQExpBuffer buffer, const char *reloptions,
1066 : : const char *prefix, int encoding, bool std_strings)
1067 : : {
1068 : : char **options;
1069 : : int noptions;
1070 : : int i;
1071 : :
1072 [ - + ]: 223 : if (!parsePGArray(reloptions, &options, &noptions))
1073 : : {
1074 : 0 : free(options);
1075 : 0 : return false;
1076 : : }
1077 : :
1078 [ + + ]: 505 : for (i = 0; i < noptions; i++)
1079 : : {
1080 : 282 : char *option = options[i];
1081 : : char *name;
1082 : : char *separator;
1083 : : char *value;
1084 : :
1085 : : /*
1086 : : * Each array element should have the form name=value. If the "=" is
1087 : : * missing for some reason, treat it like an empty value.
1088 : : */
1089 : 282 : name = option;
1090 : 282 : separator = strchr(option, '=');
1091 [ + - ]: 282 : if (separator)
1092 : : {
1093 : 282 : *separator = '\0';
1094 : 282 : value = separator + 1;
1095 : : }
1096 : : else
1097 : 0 : value = "";
1098 : :
1099 [ + + ]: 282 : if (i > 0)
1100 : 59 : appendPQExpBufferStr(buffer, ", ");
1101 : 282 : appendPQExpBuffer(buffer, "%s%s=", prefix, fmtId(name));
1102 : :
1103 : : /*
1104 : : * In general we need to quote the value; but to avoid unnecessary
1105 : : * clutter, do not quote if it is an identifier that would not need
1106 : : * quoting. (We could also allow numbers, but that is a bit trickier
1107 : : * than it looks --- for example, are leading zeroes significant? We
1108 : : * don't want to assume very much here about what custom reloptions
1109 : : * might mean.)
1110 : : */
1111 [ + + ]: 282 : if (strcmp(fmtId(value), value) == 0)
1112 : 32 : appendPQExpBufferStr(buffer, value);
1113 : : else
1114 : 250 : appendStringLiteral(buffer, value, encoding, std_strings);
1115 : : }
1116 : :
1117 : 223 : free(options);
1118 : :
1119 : 223 : return true;
1120 : : }
1121 : :
1122 : :
1123 : : /*
1124 : : * processSQLNamePattern
1125 : : *
1126 : : * Scan a wildcard-pattern string and generate appropriate WHERE clauses
1127 : : * to limit the set of objects returned. The WHERE clauses are appended
1128 : : * to the already-partially-constructed query in buf. Returns whether
1129 : : * any clause was added.
1130 : : *
1131 : : * conn: connection query will be sent to (consulted for escaping rules).
1132 : : * buf: output parameter.
1133 : : * pattern: user-specified pattern option, or NULL if none ("*" is implied).
1134 : : * have_where: true if caller already emitted "WHERE" (clauses will be ANDed
1135 : : * onto the existing WHERE clause).
1136 : : * force_escape: always quote regexp special characters, even outside
1137 : : * double quotes (else they are quoted only between double quotes).
1138 : : * schemavar: name of query variable to match against a schema-name pattern.
1139 : : * Can be NULL if no schema.
1140 : : * namevar: name of query variable to match against an object-name pattern.
1141 : : * altnamevar: NULL, or name of an alternative variable to match against name.
1142 : : * visibilityrule: clause to use if we want to restrict to visible objects
1143 : : * (for example, "pg_catalog.pg_table_is_visible(p.oid)"). Can be NULL.
1144 : : * dbnamebuf: output parameter receiving the database name portion of the
1145 : : * pattern, if any. Can be NULL.
1146 : : * dotcnt: how many separators were parsed from the pattern, by reference.
1147 : : *
1148 : : * Formatting note: the text already present in buf should end with a newline.
1149 : : * The appended text, if any, will end with one too.
1150 : : */
1151 : : bool
1152 : 5030 : processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern,
1153 : : bool have_where, bool force_escape,
1154 : : const char *schemavar, const char *namevar,
1155 : : const char *altnamevar, const char *visibilityrule,
1156 : : PQExpBuffer dbnamebuf, int *dotcnt)
1157 : : {
1158 : : PQExpBufferData schemabuf;
1159 : : PQExpBufferData namebuf;
1160 : 5030 : bool added_clause = false;
1161 : : int dcnt;
1162 : :
1163 : : #define WHEREAND() \
1164 : : (appendPQExpBufferStr(buf, have_where ? " AND " : "WHERE "), \
1165 : : have_where = true, added_clause = true)
1166 : :
1167 [ + + ]: 5030 : if (dotcnt == NULL)
1168 : 8 : dotcnt = &dcnt;
1169 : 5030 : *dotcnt = 0;
1170 [ + + ]: 5030 : if (pattern == NULL)
1171 : : {
1172 : : /* Default: select all visible objects */
1173 [ + + ]: 336 : if (visibilityrule)
1174 : : {
1175 [ + + ]: 78 : WHEREAND();
1176 : 78 : appendPQExpBuffer(buf, "%s\n", visibilityrule);
1177 : : }
1178 : 336 : return added_clause;
1179 : : }
1180 : :
1181 : 4694 : initPQExpBuffer(&schemabuf);
1182 : 4694 : initPQExpBuffer(&namebuf);
1183 : :
1184 : : /*
1185 : : * Convert shell-style 'pattern' into the regular expression(s) we want to
1186 : : * execute. Quoting/escaping into SQL literal format will be done below
1187 : : * using appendStringLiteralConn().
1188 : : *
1189 : : * If the caller provided a schemavar, we want to split the pattern on
1190 : : * ".", otherwise not.
1191 : : */
1192 [ + + + + ]: 4694 : patternToSQLRegex(PQclientEncoding(conn),
1193 : : (schemavar ? dbnamebuf : NULL),
1194 : : (schemavar ? &schemabuf : NULL),
1195 : : &namebuf,
1196 : : pattern, force_escape, true, dotcnt);
1197 : :
1198 : : /*
1199 : : * Now decide what we need to emit. We may run under a hostile
1200 : : * search_path, so qualify EVERY name. Note there will be a leading "^("
1201 : : * in the patterns in any case.
1202 : : *
1203 : : * We want the regex matches to use the database's default collation where
1204 : : * collation-sensitive behavior is required (for example, which characters
1205 : : * match '\w'). That happened by default before PG v12, but if the server
1206 : : * is >= v12 then we need to force it through explicit COLLATE clauses,
1207 : : * otherwise the "C" collation attached to "name" catalog columns wins.
1208 : : */
1209 [ + - + - ]: 4694 : if (namevar && namebuf.len > 2)
1210 : : {
1211 : : /* We have a name pattern, so constrain the namevar(s) */
1212 : :
1213 : : /* Optimize away a "*" pattern */
1214 [ + + ]: 4694 : if (strcmp(namebuf.data, "^(.*)$") != 0)
1215 : : {
1216 [ + + ]: 4620 : WHEREAND();
1217 [ + + ]: 4620 : if (altnamevar)
1218 : : {
1219 : 152 : appendPQExpBuffer(buf,
1220 : : "(%s OPERATOR(pg_catalog.~) ", namevar);
1221 : 152 : appendStringLiteralConn(buf, namebuf.data, conn);
1222 [ + - ]: 152 : if (PQserverVersion(conn) >= 120000)
1223 : 152 : appendPQExpBufferStr(buf, " COLLATE pg_catalog.default");
1224 : 152 : appendPQExpBuffer(buf,
1225 : : "\n OR %s OPERATOR(pg_catalog.~) ",
1226 : : altnamevar);
1227 : 152 : appendStringLiteralConn(buf, namebuf.data, conn);
1228 [ + - ]: 152 : if (PQserverVersion(conn) >= 120000)
1229 : 152 : appendPQExpBufferStr(buf, " COLLATE pg_catalog.default");
1230 : 152 : appendPQExpBufferStr(buf, ")\n");
1231 : : }
1232 : : else
1233 : : {
1234 : 4468 : appendPQExpBuffer(buf, "%s OPERATOR(pg_catalog.~) ", namevar);
1235 : 4468 : appendStringLiteralConn(buf, namebuf.data, conn);
1236 [ + - ]: 4468 : if (PQserverVersion(conn) >= 120000)
1237 : 4468 : appendPQExpBufferStr(buf, " COLLATE pg_catalog.default");
1238 : 4468 : appendPQExpBufferChar(buf, '\n');
1239 : : }
1240 : : }
1241 : : }
1242 : :
1243 [ + + + + ]: 4694 : if (schemavar && schemabuf.len > 2)
1244 : : {
1245 : : /* We have a schema pattern, so constrain the schemavar */
1246 : :
1247 : : /* Optimize away a "*" pattern */
1248 [ + + + - ]: 2010 : if (strcmp(schemabuf.data, "^(.*)$") != 0 && schemavar)
1249 : : {
1250 [ + + ]: 1003 : WHEREAND();
1251 : 1003 : appendPQExpBuffer(buf, "%s OPERATOR(pg_catalog.~) ", schemavar);
1252 : 1003 : appendStringLiteralConn(buf, schemabuf.data, conn);
1253 [ + - ]: 1003 : if (PQserverVersion(conn) >= 120000)
1254 : 1003 : appendPQExpBufferStr(buf, " COLLATE pg_catalog.default");
1255 : 1003 : appendPQExpBufferChar(buf, '\n');
1256 : : }
1257 : : }
1258 : : else
1259 : : {
1260 : : /* No schema pattern given, so select only visible objects */
1261 [ + + ]: 3687 : if (visibilityrule)
1262 : : {
1263 [ + - ]: 2950 : WHEREAND();
1264 : 2950 : appendPQExpBuffer(buf, "%s\n", visibilityrule);
1265 : : }
1266 : : }
1267 : :
1268 : 4694 : termPQExpBuffer(&schemabuf);
1269 : 4694 : termPQExpBuffer(&namebuf);
1270 : :
1271 : 4694 : return added_clause;
1272 : : #undef WHEREAND
1273 : : }
1274 : :
1275 : : /*
1276 : : * Transform a possibly qualified shell-style object name pattern into up to
1277 : : * three SQL-style regular expressions, converting quotes, lower-casing
1278 : : * unquoted letters, and adjusting shell-style wildcard characters into regexp
1279 : : * notation.
1280 : : *
1281 : : * If the dbnamebuf and schemabuf arguments are non-NULL, and the pattern
1282 : : * contains two or more dbname/schema/name separators, we parse the portions of
1283 : : * the pattern prior to the first and second separators into dbnamebuf and
1284 : : * schemabuf, and the rest into namebuf.
1285 : : *
1286 : : * If dbnamebuf is NULL and schemabuf is non-NULL, and the pattern contains at
1287 : : * least one separator, we parse the first portion into schemabuf and the rest
1288 : : * into namebuf.
1289 : : *
1290 : : * Otherwise, we parse all the pattern into namebuf.
1291 : : *
1292 : : * If the pattern contains more dotted parts than buffers to parse into, the
1293 : : * extra dots will be treated as literal characters and written into the
1294 : : * namebuf, though they will be counted. Callers should always check the value
1295 : : * returned by reference in dotcnt and handle this error case appropriately.
1296 : : *
1297 : : * We surround the regexps with "^(...)$" to force them to match whole strings,
1298 : : * as per SQL practice. We have to have parens in case strings contain "|",
1299 : : * else the "^" and "$" will be bound into the first and last alternatives
1300 : : * which is not what we want. Whether this is done for dbnamebuf is controlled
1301 : : * by the want_literal_dbname parameter.
1302 : : *
1303 : : * The regexps we parse into the buffers are appended to the data (if any)
1304 : : * already present. If we parse fewer fields than the number of buffers we
1305 : : * were given, the extra buffers are unaltered.
1306 : : *
1307 : : * encoding: the character encoding for the given pattern
1308 : : * dbnamebuf: output parameter receiving the database name portion of the
1309 : : * pattern, if any. Can be NULL.
1310 : : * schemabuf: output parameter receiving the schema name portion of the
1311 : : * pattern, if any. Can be NULL.
1312 : : * namebuf: output parameter receiving the database name portion of the
1313 : : * pattern, if any. Can be NULL.
1314 : : * pattern: user-specified pattern option, or NULL if none ("*" is implied).
1315 : : * force_escape: always quote regexp special characters, even outside
1316 : : * double quotes (else they are quoted only between double quotes).
1317 : : * want_literal_dbname: if true, regexp special characters within the database
1318 : : * name portion of the pattern will not be escaped, nor will the dbname be
1319 : : * converted into a regular expression.
1320 : : * dotcnt: output parameter receiving the number of separators parsed from the
1321 : : * pattern.
1322 : : */
1323 : : void
1324 : 4796 : patternToSQLRegex(int encoding, PQExpBuffer dbnamebuf, PQExpBuffer schemabuf,
1325 : : PQExpBuffer namebuf, const char *pattern, bool force_escape,
1326 : : bool want_literal_dbname, int *dotcnt)
1327 : : {
1328 : : PQExpBufferData buf[3];
1329 : : PQExpBufferData left_literal;
1330 : : PQExpBuffer curbuf;
1331 : : PQExpBuffer maxbuf;
1332 : : int i;
1333 : : bool inquotes;
1334 : : bool left;
1335 : : const char *cp;
1336 : :
1337 : : Assert(pattern != NULL);
1338 : : Assert(namebuf != NULL);
1339 : :
1340 : : /* callers should never expect "dbname.relname" format */
1341 : : Assert(dbnamebuf == NULL || schemabuf != NULL);
1342 : : Assert(dotcnt != NULL);
1343 : :
1344 : 4796 : *dotcnt = 0;
1345 : 4796 : inquotes = false;
1346 : 4796 : cp = pattern;
1347 : :
1348 [ + + ]: 4796 : if (dbnamebuf != NULL)
1349 : 4008 : maxbuf = &buf[2];
1350 [ + + ]: 788 : else if (schemabuf != NULL)
1351 : 29 : maxbuf = &buf[1];
1352 : : else
1353 : 759 : maxbuf = &buf[0];
1354 : :
1355 : 4796 : curbuf = &buf[0];
1356 [ + + ]: 4796 : if (want_literal_dbname)
1357 : : {
1358 : 4694 : left = true;
1359 : 4694 : initPQExpBuffer(&left_literal);
1360 : : }
1361 : : else
1362 : 102 : left = false;
1363 : 4796 : initPQExpBuffer(curbuf);
1364 : 4796 : appendPQExpBufferStr(curbuf, "^(");
1365 [ + + ]: 84723 : while (*cp)
1366 : : {
1367 : 79927 : char ch = *cp;
1368 : :
1369 [ + + ]: 79927 : if (ch == '"')
1370 : : {
1371 [ + + + + ]: 2136 : if (inquotes && cp[1] == '"')
1372 : : {
1373 : : /* emit one quote, stay in inquotes mode */
1374 : 4 : appendPQExpBufferChar(curbuf, '"');
1375 [ + - ]: 4 : if (left)
1376 : 4 : appendPQExpBufferChar(&left_literal, '"');
1377 : 4 : cp++;
1378 : : }
1379 : : else
1380 : 2132 : inquotes = !inquotes;
1381 : 2136 : cp++;
1382 : : }
1383 [ + + + + ]: 77791 : else if (!inquotes && isupper((unsigned char) ch))
1384 : : {
1385 : 160 : appendPQExpBufferChar(curbuf,
1386 : 160 : pg_tolower((unsigned char) ch));
1387 [ + + ]: 160 : if (left)
1388 : 100 : appendPQExpBufferChar(&left_literal,
1389 : 100 : pg_tolower((unsigned char) ch));
1390 : 160 : cp++;
1391 : : }
1392 [ + + + + ]: 77631 : else if (!inquotes && ch == '*')
1393 : : {
1394 : 297 : appendPQExpBufferStr(curbuf, ".*");
1395 [ + + ]: 297 : if (left)
1396 : 213 : appendPQExpBufferChar(&left_literal, '*');
1397 : 297 : cp++;
1398 : : }
1399 [ + + + + ]: 77334 : else if (!inquotes && ch == '?')
1400 : : {
1401 : 4 : appendPQExpBufferChar(curbuf, '.');
1402 [ + - ]: 4 : if (left)
1403 : 4 : appendPQExpBufferChar(&left_literal, '?');
1404 : 4 : cp++;
1405 : : }
1406 [ + + + + ]: 77330 : else if (!inquotes && ch == '.')
1407 : : {
1408 : 1910 : left = false;
1409 [ + - ]: 1910 : if (dotcnt)
1410 : 1910 : (*dotcnt)++;
1411 [ + + ]: 1910 : if (curbuf < maxbuf)
1412 : : {
1413 : 1529 : appendPQExpBufferStr(curbuf, ")$");
1414 : 1529 : curbuf++;
1415 : 1529 : initPQExpBuffer(curbuf);
1416 : 1529 : appendPQExpBufferStr(curbuf, "^(");
1417 : 1529 : cp++;
1418 : : }
1419 : : else
1420 : 381 : appendPQExpBufferChar(curbuf, *cp++);
1421 : : }
1422 [ + + ]: 75420 : else if (ch == '$')
1423 : : {
1424 : : /*
1425 : : * Dollar is always quoted, whether inside quotes or not. The
1426 : : * reason is that it's allowed in SQL identifiers, so there's a
1427 : : * significant use-case for treating it literally, while because
1428 : : * we anchor the pattern automatically there is no use-case for
1429 : : * having it possess its regexp meaning.
1430 : : */
1431 : 8 : appendPQExpBufferStr(curbuf, "\\$");
1432 [ + - ]: 8 : if (left)
1433 : 8 : appendPQExpBufferChar(&left_literal, '$');
1434 : 8 : cp++;
1435 : : }
1436 : : else
1437 : : {
1438 : : /*
1439 : : * Ordinary data character, transfer to pattern
1440 : : *
1441 : : * Inside double quotes, or at all times if force_escape is true,
1442 : : * quote regexp special characters with a backslash to avoid
1443 : : * regexp errors. Outside quotes, however, let them pass through
1444 : : * as-is; this lets knowledgeable users build regexp expressions
1445 : : * that are more powerful than shell-style patterns.
1446 : : *
1447 : : * As an exception to that, though, always quote "[]", as that's
1448 : : * much more likely to be an attempt to write an array type name
1449 : : * than it is to be the start of a regexp bracket expression.
1450 : : */
1451 [ + + + + ]: 75412 : if ((inquotes || force_escape) &&
1452 [ + + ]: 20037 : strchr("|*+?()[]{}.^$\\", ch))
1453 : 2585 : appendPQExpBufferChar(curbuf, '\\');
1454 [ + + + + ]: 72827 : else if (ch == '[' && cp[1] == ']')
1455 : 4 : appendPQExpBufferChar(curbuf, '\\');
1456 : 75412 : i = PQmblenBounded(cp, encoding);
1457 [ + + ]: 150824 : while (i--)
1458 : : {
1459 [ + + ]: 75412 : if (left)
1460 : 53228 : appendPQExpBufferChar(&left_literal, *cp);
1461 : 75412 : appendPQExpBufferChar(curbuf, *cp++);
1462 : : }
1463 : : }
1464 : : }
1465 : 4796 : appendPQExpBufferStr(curbuf, ")$");
1466 : :
1467 [ + - ]: 4796 : if (namebuf)
1468 : : {
1469 : 4796 : appendPQExpBufferStr(namebuf, curbuf->data);
1470 : 4796 : termPQExpBuffer(curbuf);
1471 : 4796 : curbuf--;
1472 : : }
1473 : :
1474 [ + + + + ]: 4796 : if (schemabuf && curbuf >= buf)
1475 : : {
1476 : 1033 : appendPQExpBufferStr(schemabuf, curbuf->data);
1477 : 1033 : termPQExpBuffer(curbuf);
1478 : 1033 : curbuf--;
1479 : : }
1480 : :
1481 [ + + + + ]: 4796 : if (dbnamebuf && curbuf >= buf)
1482 : : {
1483 [ + + ]: 496 : if (want_literal_dbname)
1484 : 479 : appendPQExpBufferStr(dbnamebuf, left_literal.data);
1485 : : else
1486 : 17 : appendPQExpBufferStr(dbnamebuf, curbuf->data);
1487 : 496 : termPQExpBuffer(curbuf);
1488 : : }
1489 : :
1490 [ + + ]: 4796 : if (want_literal_dbname)
1491 : 4694 : termPQExpBuffer(&left_literal);
1492 : 4796 : }
|