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