PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
test_escape.c
Go to the documentation of this file.
1/*
2 * test_escape.c Test escape functions
3 *
4 * Copyright (c) 2022-2025, PostgreSQL Global Development Group
5 *
6 * IDENTIFICATION
7 * src/test/modules/test_escape/test_escape.c
8 */
9
10#include "postgres_fe.h"
11
12#include <string.h>
13#include <stdio.h>
14
15#include "fe_utils/psqlscan.h"
17#include "getopt_long.h"
18#include "libpq-fe.h"
19#include "mb/pg_wchar.h"
20#include "utils/memdebug.h"
21
22
23typedef struct pe_test_config
24{
27 const char *conninfo;
29
33
34
35/*
36 * An escape function to be tested by this test.
37 */
38typedef struct pe_test_escape_func
39{
40 const char *name;
41
42 /*
43 * Can the escape method report errors? If so, we validate that it does in
44 * case of various invalid inputs.
45 */
47
48 /*
49 * Is the escape method known to not handle invalidly encoded input? If
50 * so, we don't run the test unless --force-unsupported is used.
51 */
53
54 /*
55 * Is the escape method known to only handle encodings where no byte in a
56 * multi-byte characters are valid ascii.
57 */
59
60 /*
61 * Does the escape function have a length input?
62 */
64
65 bool (*escape) (PGconn *conn, PQExpBuffer target,
66 const char *unescaped, size_t unescaped_len,
67 PQExpBuffer escape_err);
69
70/*
71 * A single test input for this test.
72 */
73typedef struct pe_test_vector
74{
75 const char *client_encoding;
76 size_t escape_len;
77 const char *escape;
79
80
81/*
82 * Callback functions from flex lexer. Not currently used by the test.
83 */
85 NULL
86};
87
88
89static bool
91 const char *unescaped, size_t unescaped_len,
92 PQExpBuffer escape_err)
93{
94 char *escaped;
95
96 escaped = PQescapeLiteral(conn, unescaped, unescaped_len);
97 if (!escaped)
98 {
100 escape_err->data[escape_err->len - 1] = 0;
101 escape_err->len--;
102 return false;
103 }
104 else
105 {
106 appendPQExpBufferStr(target, escaped);
107 PQfreemem(escaped);
108 return true;
109 }
110}
111
112static bool
114 const char *unescaped, size_t unescaped_len,
115 PQExpBuffer escape_err)
116{
117 char *escaped;
118
119 escaped = PQescapeIdentifier(conn, unescaped, unescaped_len);
120 if (!escaped)
121 {
123 escape_err->data[escape_err->len - 1] = 0;
124 escape_err->len--;
125 return false;
126 }
127 else
128 {
129 appendPQExpBufferStr(target, escaped);
130 PQfreemem(escaped);
131 return true;
132 }
133}
134
135static bool
137 const char *unescaped, size_t unescaped_len,
138 PQExpBuffer escape_err)
139{
140 int error;
141 size_t sz;
142
143 appendPQExpBufferChar(target, '\'');
144 enlargePQExpBuffer(target, unescaped_len * 2 + 1);
145 sz = PQescapeStringConn(conn, target->data + target->len,
146 unescaped, unescaped_len,
147 &error);
148
149 target->len += sz;
150 appendPQExpBufferChar(target, '\'');
151
152 if (error)
153 {
155 escape_err->data[escape_err->len - 1] = 0;
156 escape_err->len--;
157 return false;
158 }
159 else
160 {
161 return true;
162 }
163}
164
165static bool
167 const char *unescaped, size_t unescaped_len,
168 PQExpBuffer escape_err)
169{
170 size_t sz;
171
172 appendPQExpBufferChar(target, '\'');
173 enlargePQExpBuffer(target, unescaped_len * 2 + 1);
174 sz = PQescapeString(target->data + target->len,
175 unescaped, unescaped_len);
176 target->len += sz;
177 appendPQExpBufferChar(target, '\'');
178
179
180 return true;
181}
182
183/*
184 * Escape via s/'/''/. Non-core drivers invariably wrap libpq or use this
185 * method. It suffices iff the input passes encoding validation, so it's
186 * marked as supports_only_valid.
187 */
188static bool
190 const char *unescaped, size_t unescaped_len,
191 PQExpBuffer escape_err)
192{
193 const char *s = unescaped;
194
195 appendPQExpBufferChar(target, '\'');
196
197 for (int i = 0; i < unescaped_len; i++)
198 {
199 char c = *s;
200
201 if (c == '\'')
202 {
203 appendPQExpBufferStr(target, "''");
204 }
205 else
206 appendPQExpBufferChar(target, c);
207 s++;
208 }
209 appendPQExpBufferChar(target, '\'');
210
211 return true;
212}
213
214static bool
216 const char *unescaped, size_t unescaped_len,
217 PQExpBuffer escape_err)
218{
219 appendStringLiteral(target, unescaped, PQclientEncoding(conn), 1);
220
221 return true;
222}
223
224static bool
226 const char *unescaped, size_t unescaped_len,
227 PQExpBuffer escape_err)
228{
230 appendPQExpBufferStr(target, fmtId(unescaped));
231
232 return true;
233}
234
236{
237 {
238 .name = "PQescapeLiteral",
239 .reports_errors = true,
240 .supports_input_length = true,
241 .escape = escape_literal,
242 },
243 {
244 .name = "PQescapeIdentifier",
245 .reports_errors = true,
246 .supports_input_length = true,
247 .escape = escape_identifier
248 },
249 {
250 .name = "PQescapeStringConn",
251 .reports_errors = true,
252 .supports_input_length = true,
253 .escape = escape_string_conn
254 },
255 {
256 .name = "PQescapeString",
257 .reports_errors = false,
258 .supports_input_length = true,
259 .escape = escape_string
260 },
261 {
262 .name = "replace",
263 .reports_errors = false,
264 .supports_only_valid = true,
265 .supports_only_ascii_overlap = true,
266 .supports_input_length = true,
267 .escape = escape_replace
268 },
269 {
270 .name = "appendStringLiteral",
271 .reports_errors = false,
272 .escape = escape_append_literal
273 },
274 {
275 .name = "fmtId",
276 .reports_errors = false,
277 .escape = escape_fmt_id
278 },
279};
280
281
282#define TV(enc, string) {.client_encoding = (enc), .escape=string, .escape_len=sizeof(string) - 1, }
283#define TV_LEN(enc, string, len) {.client_encoding = (enc), .escape=string, .escape_len=len, }
285{
286 /* expected to work sanity checks */
287 TV("UTF-8", "1"),
288 TV("UTF-8", "'"),
289 TV("UTF-8", "\""),
290
291 TV("UTF-8", "\'"),
292 TV("UTF-8", "\""),
293
294 TV("UTF-8", "\\"),
295
296 TV("UTF-8", "\\'"),
297 TV("UTF-8", "\\\""),
298
299 /* trailing multi-byte character, paddable in available space */
300 TV("UTF-8", "1\xC0"),
301 TV("UTF-8", "1\xE0 "),
302 TV("UTF-8", "1\xF0 "),
303 TV("UTF-8", "1\xF0 "),
304 TV("UTF-8", "1\xF0 "),
305
306 /* trailing multi-byte character, not enough space to pad */
307 TV("UTF-8", "1\xE0"),
308 TV("UTF-8", "1\xF0"),
309 TV("UTF-8", "\xF0"),
310
311 /* try to smuggle in something in invalid characters */
312 TV("UTF-8", "1\xE0'"),
313 TV("UTF-8", "1\xE0\""),
314 TV("UTF-8", "1\xF0'"),
315 TV("UTF-8", "1\xF0\""),
316 TV("UTF-8", "1\xF0'; "),
317 TV("UTF-8", "1\xF0\"; "),
318 TV("UTF-8", "1\xF0';;;;"),
319 TV("UTF-8", "1\xF0 ';;;;"),
320 TV("UTF-8", "1\xF0 \";;;;"),
321 TV("UTF-8", "1\xE0'; \\l ; "),
322 TV("UTF-8", "1\xE0\"; \\l ; "),
323
324 /* null byte handling */
325 TV("UTF-8", "some\0thing"),
326 TV("UTF-8", "some\0"),
327 TV("UTF-8", "some\xF0'\0"),
328 TV("UTF-8", "some\xF0'\0'"),
329 TV("UTF-8", "some\xF0" "ab\0'"),
330
331 /* GB18030's 4 byte encoding requires a 2nd byte limited values */
332 TV("GB18030", "\x90\x31"),
333 TV("GB18030", "\\\x81\x5c'"),
334 TV("GB18030", "\\\x81\x5c\""),
335 TV("GB18030", "\\\x81\x5c\0'"),
336
337 /*
338 * \x81 indicates a 2 byte char. ' and " are not a valid second byte, but
339 * that requires encoding verification to know. E.g. replace_string()
340 * doesn't cope.
341 */
342 TV("GB18030", "\\\x81';"),
343 TV("GB18030", "\\\x81\";"),
344
345 /*
346 * \x81 indicates a 2 byte char. \ is a valid second character.
347 */
348 TV("GB18030", "\\\x81\\';"),
349 TV("GB18030", "\\\x81\\\";"),
350 TV("GB18030", "\\\x81\0;"),
351 TV("GB18030", "\\\x81\0'"),
352 TV("GB18030", "\\\x81'\0"),
353
354 TV("SJIS", "\xF0\x40;"),
355
356 TV("SJIS", "\xF0';"),
357 TV("SJIS", "\xF0\";"),
358 TV("SJIS", "\xF0\0'"),
359 TV("SJIS", "\\\xF0\\';"),
360 TV("SJIS", "\\\xF0\\\";"),
361
362 TV("gbk", "\x80';"),
363 TV("gbk", "\x80"),
364 TV("gbk", "\x80'"),
365 TV("gbk", "\x80\""),
366 TV("gbk", "\x80\\"),
367
368 TV("mule_internal", "\\\x9c';\0;"),
369
370 TV("sql_ascii", "1\xC0'"),
371
372 /*
373 * Testcases that are not null terminated for the specified input length.
374 * That's interesting to verify that escape functions don't read beyond
375 * the intended input length.
376 */
377 TV_LEN("gbk", "\x80", 1),
378 TV_LEN("UTF-8", "\xC3\xb6 ", 1),
379 TV_LEN("UTF-8", "\xC3\xb6 ", 2),
380};
381
382
383/*
384 * Print the string into buf, making characters outside of plain ascii
385 * somewhat easier to recognize.
386 *
387 * The output format could stand to be improved significantly, it's not at all
388 * unambiguous.
389 */
390static void
391escapify(PQExpBuffer buf, const char *str, size_t len)
392{
393 for (size_t i = 0; i < len; i++)
394 {
395 char c = *str;
396
397 if (c == '\n')
399 else if (c == '\0')
401 else if (c < ' ' || c > '~')
402 appendPQExpBuffer(buf, "\\x%2x", (uint8_t) c);
403 else
405 str++;
406 }
407}
408
409static void
411 bool success,
412 PQExpBuffer testname,
413 PQExpBuffer details,
414 const char *subname,
415 const char *resultdesc)
416{
417 int test_id = ++tc->test_count;
418 bool print_details = true;
419 bool print_result = true;
420
421 if (success)
422 {
423 if (tc->verbosity <= 0)
424 print_details = false;
425 if (tc->verbosity < 0)
426 print_result = false;
427 }
428 else
429 tc->failure_count++;
430
431 if (print_details)
432 printf("%s", details->data);
433
434 if (print_result)
435 printf("%s %d - %s: %s: %s\n",
436 success ? "ok" : "not ok",
437 test_id, testname->data,
438 subname,
439 resultdesc);
440}
441
442/*
443 * Return true for encodings in which bytes in a multi-byte character look
444 * like valid ascii characters.
445 */
446static bool
448{
449 /*
450 * We don't store this property directly anywhere, but whether an encoding
451 * is a client-only encoding is a good proxy.
452 */
454 return true;
455 return false;
456}
457
458static const char *
460{
461#define TOSTR_CASE(sym) case sym: return #sym
462
463 switch (res)
464 {
469 }
470
472 return ""; /* silence compiler */
473}
474
475/*
476 * Verify that psql parses the input as a single statement. If this property
477 * is violated, the escape function does not effectively protect against
478 * smuggling in a second statement.
479 */
480static void
482 PQExpBuffer input_buf, PQExpBuffer details)
483{
484 PsqlScanState scan_state;
485 PsqlScanResult scan_result;
486 PQExpBuffer query_buf;
487 promptStatus_t prompt_status = PROMPT_READY;
488 int matches = 0;
489 bool test_fails;
490 const char *resdesc;
491
492 query_buf = createPQExpBuffer();
493
495
496 /*
497 * TODO: This hardcodes standard conforming strings, it would be useful to
498 * test without as well.
499 */
500 psql_scan_setup(scan_state, input_buf->data, input_buf->len,
501 PQclientEncoding(tc->conn), 1);
502
503 do
504 {
505 resetPQExpBuffer(query_buf);
506
507 scan_result = psql_scan(scan_state, query_buf,
508 &prompt_status);
509
510 appendPQExpBuffer(details,
511 "#\t\t %d: scan_result: %s prompt: %u, query_buf: ",
512 matches, scan_res_s(scan_result), prompt_status);
513 escapify(details, query_buf->data, query_buf->len);
514 appendPQExpBufferChar(details, '\n');
515
516 matches++;
517 }
518 while (scan_result != PSCAN_INCOMPLETE && scan_result != PSCAN_EOL);
519
520 psql_scan_destroy(scan_state);
521 destroyPQExpBuffer(query_buf);
522
523 test_fails = matches > 1 || scan_result != PSCAN_EOL;
524
525 if (matches > 1)
526 resdesc = "more than one match";
527 else if (scan_result != PSCAN_EOL)
528 resdesc = "unexpected end state";
529 else
530 resdesc = "ok";
531
532 report_result(tc, !test_fails, testname, details,
533 "psql parse",
534 resdesc);
535}
536
537static void
539{
540 PQExpBuffer testname;
541 PQExpBuffer details;
542 PQExpBuffer raw_buf;
543 PQExpBuffer escape_buf;
544 PQExpBuffer escape_err;
545 size_t input_encoding_validlen;
546 bool input_encoding_valid;
547 size_t input_encoding0_validlen;
548 bool input_encoding0_valid;
549 bool escape_success;
550 size_t escape_encoding_length;
551 bool escape_encoding_valid;
552
553 escape_err = createPQExpBuffer();
554 testname = createPQExpBuffer();
555 details = createPQExpBuffer();
556 raw_buf = createPQExpBuffer();
557 escape_buf = createPQExpBuffer();
558
561 {
562 goto out;
563 }
564
565 /* name to describe the test */
566 appendPQExpBufferChar(testname, '>');
567 escapify(testname, tv->escape, tv->escape_len);
568 appendPQExpBuffer(testname, "< - %s - %s",
569 tv->client_encoding, ef->name);
570
571 /* details to describe the test, to allow for debugging */
572 appendPQExpBuffer(details, "#\t input: %zd bytes: ",
573 tv->escape_len);
574 escapify(details, tv->escape, tv->escape_len);
575 appendPQExpBufferChar(details, '\n');
576 appendPQExpBuffer(details, "#\t encoding: %s\n",
577 tv->client_encoding);
578
579
580 /* check encoding of input, to compare with after the test */
581 input_encoding_validlen = pg_encoding_verifymbstr(PQclientEncoding(tc->conn),
582 tv->escape,
583 tv->escape_len);
584 input_encoding_valid = input_encoding_validlen == tv->escape_len;
585 appendPQExpBuffer(details, "#\t input encoding valid: %d\n",
586 input_encoding_valid);
587
588 input_encoding0_validlen = pg_encoding_verifymbstr(PQclientEncoding(tc->conn),
589 tv->escape,
590 strnlen(tv->escape, tv->escape_len));
591 input_encoding0_valid = input_encoding0_validlen == strnlen(tv->escape, tv->escape_len);
592 appendPQExpBuffer(details, "#\t input encoding valid till 0: %d\n",
593 input_encoding0_valid);
594
595 appendPQExpBuffer(details, "#\t escape func: %s\n",
596 ef->name);
597
598 if (!input_encoding_valid && ef->supports_only_valid
599 && !tc->force_unsupported)
600 goto out;
601
602
603 /*
604 * Put the to-be-escaped data into a buffer, so that we
605 *
606 * a) can mark memory beyond end of the string as inaccessible when using
607 * valgrind
608 *
609 * b) can append extra data beyond the length passed to the escape
610 * function, to verify that that data is not processed.
611 *
612 * TODO: Should we instead/additionally escape twice, once with unmodified
613 * and once with appended input? That way we could compare the two.
614 */
615 appendBinaryPQExpBuffer(raw_buf, tv->escape, tv->escape_len);
616
617#define NEVER_ACCESS_STR "\xff never-to-be-touched"
618 if (ef->supports_input_length)
619 {
620 /*
621 * Append likely invalid string that does *not* contain a null byte
622 * (which'd prevent some invalid accesses to later memory).
623 */
625
627 raw_buf->len - tv->escape_len);
628 }
629 else
630 {
631 /* append invalid string, after \0 */
632 appendPQExpBufferChar(raw_buf, 0);
634
635 VALGRIND_MAKE_MEM_NOACCESS(&raw_buf->data[tv->escape_len + 1],
636 raw_buf->len - tv->escape_len - 1);
637 }
638
639 /* call the to-be-tested escape function */
640 escape_success = ef->escape(tc->conn, escape_buf,
641 raw_buf->data, tv->escape_len,
642 escape_err);
643 if (!escape_success)
644 {
645 appendPQExpBuffer(details, "#\t escape error: %s\n",
646 escape_err->data);
647 }
648
649 if (escape_buf->len > 0)
650 {
651 bool contains_never;
652
653 appendPQExpBuffer(details, "#\t escaped string: %zd bytes: ", escape_buf->len);
654 escapify(details, escape_buf->data, escape_buf->len);
655 appendPQExpBufferChar(details, '\n');
656
657 escape_encoding_length = pg_encoding_verifymbstr(PQclientEncoding(tc->conn),
658 escape_buf->data,
659 escape_buf->len);
660 escape_encoding_valid = escape_encoding_length == escape_buf->len;
661
662 appendPQExpBuffer(details, "#\t escape encoding valid: %d\n",
663 escape_encoding_valid);
664
665 /*
666 * Verify that no data beyond the end of the input is included in the
667 * escaped string. It'd be better to use something like memmem()
668 * here, but that's not available everywhere.
669 */
670 contains_never = strstr(escape_buf->data, NEVER_ACCESS_STR) == NULL;
671 report_result(tc, contains_never, testname, details,
672 "escaped data beyond end of input",
673 contains_never ? "no" : "all secrets revealed");
674 }
675 else
676 {
677 escape_encoding_length = 0;
678 escape_encoding_valid = 1;
679 }
680
681 /*
682 * If the test reports errors, and the input was invalidly encoded,
683 * escaping should fail. One edge-case that we accept for now is that the
684 * input could have an embedded null byte, which the escape functions will
685 * just treat as a shorter string. If the encoding error is after the zero
686 * byte, the output thus won't contain it.
687 */
688 if (ef->reports_errors)
689 {
690 bool ok = true;
691 const char *resdesc = "ok";
692
693 if (escape_success)
694 {
695 if (!input_encoding0_valid)
696 {
697 ok = false;
698 resdesc = "invalid input escaped successfully";
699 }
700 else if (!input_encoding_valid)
701 resdesc = "invalid input escaped successfully, due to zero byte";
702 }
703 else
704 {
705 if (input_encoding0_valid)
706 {
707 ok = false;
708 resdesc = "valid input failed to escape";
709 }
710 else if (input_encoding_valid)
711 resdesc = "valid input failed to escape, due to zero byte";
712 }
713
714 report_result(tc, ok, testname, details,
715 "input validity vs escape success",
716 resdesc);
717 }
718
719 /*
720 * If the input is invalidly encoded, the output should also be invalidly
721 * encoded. We accept the same zero-byte edge case as above.
722 */
723 {
724 bool ok = true;
725 const char *resdesc = "ok";
726
727 if (input_encoding0_valid && !input_encoding_valid && escape_encoding_valid)
728 {
729 resdesc = "invalid input produced valid output, due to zero byte";
730 }
731 else if (input_encoding0_valid && !escape_encoding_valid)
732 {
733 ok = false;
734 resdesc = "valid input produced invalid output";
735 }
736 else if (!input_encoding0_valid &&
737 (!ef->reports_errors || escape_success) &&
738 escape_encoding_valid)
739 {
740 ok = false;
741 resdesc = "invalid input produced valid output";
742 }
743
744 report_result(tc, ok, testname, details,
745 "input and escaped encoding validity",
746 resdesc);
747 }
748
749 /*
750 * Test psql parsing whenever we get any string back, even if the escape
751 * function returned a failure.
752 */
753 if (escape_buf->len > 0)
754 {
755 test_psql_parse(tc, testname,
756 escape_buf, details);
757 }
758
759out:
760 destroyPQExpBuffer(escape_err);
761 destroyPQExpBuffer(details);
762 destroyPQExpBuffer(testname);
763 destroyPQExpBuffer(escape_buf);
764 destroyPQExpBuffer(raw_buf);
765}
766
767static void
769{
771 {
772 fprintf(stderr, "failed to set encoding to %s:\n%s\n",
774 exit(1);
775 }
776
777 for (int escoff = 0; escoff < lengthof(pe_test_escape_funcs); escoff++)
778 {
779 const pe_test_escape_func *ef = &pe_test_escape_funcs[escoff];
780
781 test_one_vector_escape(tc, tv, ef);
782 }
783}
784
785static void
786usage(const char *hint)
787{
788 if (hint)
789 fprintf(stderr, "Error: %s\n\n", hint);
790
791 printf("PostgreSQL escape function test\n"
792 "\n"
793 "Usage:\n"
794 " test_escape --conninfo=CONNINFO [OPTIONS]\n"
795 "\n"
796 "Options:\n"
797 " -h, --help show this help\n"
798 " -c, --conninfo=CONNINFO connection information to use\n"
799 " -v, --verbose show test details even for successes\n"
800 " -q, --quiet only show failures\n"
801 " -f, --force-unsupported test invalid input even if unsupported\n"
802 );
803
804 if (hint)
805 exit(1);
806}
807
808int
809main(int argc, char *argv[])
810{
811 pe_test_config tc = {0};
812 int c;
813 int option_index;
814
815 static const struct option long_options[] = {
816 {"help", no_argument, NULL, 'h'},
817 {"conninfo", required_argument, NULL, 'c'},
818 {"verbose", no_argument, NULL, 'v'},
819 {"quiet", no_argument, NULL, 'q'},
820 {"force-unsupported", no_argument, NULL, 'f'},
821 {NULL, 0, NULL, 0},
822 };
823
824 while ((c = getopt_long(argc, argv, "c:fhqv", long_options, &option_index)) != -1)
825 {
826 switch (c)
827 {
828 case 'h':
829 usage(NULL);
830 exit(0);
831 break;
832 case 'c':
833 tc.conninfo = optarg;
834 break;
835 case 'v':
836 tc.verbosity++;
837 break;
838 case 'q':
839 tc.verbosity--;
840 break;
841 case 'f':
842 tc.force_unsupported = true;
843 break;
844 }
845 }
846
847 if (argc - optind >= 1)
848 usage("unused option(s) specified");
849
850 if (tc.conninfo == NULL)
851 usage("--conninfo needs to be specified");
852
853 tc.conn = PQconnectdb(tc.conninfo);
854
855 if (!tc.conn || PQstatus(tc.conn) != CONNECTION_OK)
856 {
857 fprintf(stderr, "could not connect: %s\n",
858 PQerrorMessage(tc.conn));
859 exit(1);
860 }
861
862 for (int i = 0; i < lengthof(pe_test_vectors); i++)
863 {
865 }
866
867 PQfinish(tc.conn);
868
869 printf("# %d failures\n", tc.failure_count);
870 printf("1..%d\n", tc.test_count);
871 return tc.failure_count > 0;
872}
#define pg_unreachable()
Definition: c.h:332
#define lengthof(array)
Definition: c.h:759
#define fprintf(file, fmt, msg)
Definition: cubescan.l:21
PGconn * PQconnectdb(const char *conninfo)
Definition: fe-connect.c:813
ConnStatusType PQstatus(const PGconn *conn)
Definition: fe-connect.c:7556
int PQclientEncoding(const PGconn *conn)
Definition: fe-connect.c:7709
void PQfinish(PGconn *conn)
Definition: fe-connect.c:5290
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:7619
int PQsetClientEncoding(PGconn *conn, const char *encoding)
Definition: fe-connect.c:7717
void PQfreemem(void *ptr)
Definition: fe-exec.c:4032
size_t PQescapeStringConn(PGconn *conn, char *to, const char *from, size_t length, int *error)
Definition: fe-exec.c:4176
size_t PQescapeString(char *to, const char *from, size_t length)
Definition: fe-exec.c:4198
char * PQescapeLiteral(PGconn *conn, const char *str, size_t len)
Definition: fe-exec.c:4363
char * PQescapeIdentifier(PGconn *conn, const char *str, size_t len)
Definition: fe-exec.c:4369
int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex)
Definition: getopt_long.c:60
#define no_argument
Definition: getopt_long.h:25
#define required_argument
Definition: getopt_long.h:26
const char * str
static bool success
Definition: initdb.c:187
int i
Definition: isn.c:77
@ CONNECTION_OK
Definition: libpq-fe.h:84
#define VALGRIND_MAKE_MEM_NOACCESS(addr, size)
Definition: memdebug.h:27
const void size_t len
int32 encoding
Definition: pg_database.h:41
PGDLLIMPORT int optind
Definition: getopt.c:51
PGDLLIMPORT char * optarg
Definition: getopt.c:53
NameData subname
static char * buf
Definition: pg_test_fsync.c:72
#define PG_ENCODING_BE_LAST
Definition: pg_wchar.h:275
#define printf(...)
Definition: port.h:245
size_t strnlen(const char *str, size_t maxlen)
Definition: strnlen.c:26
PQExpBuffer createPQExpBuffer(void)
Definition: pqexpbuffer.c:72
int enlargePQExpBuffer(PQExpBuffer str, size_t needed)
Definition: pqexpbuffer.c:172
void resetPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:146
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:265
void appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)
Definition: pqexpbuffer.c:397
void destroyPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:114
void appendPQExpBufferChar(PQExpBuffer str, char ch)
Definition: pqexpbuffer.c:378
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:367
char * c
PsqlScanResult
Definition: psqlscan.h:31
@ PSCAN_BACKSLASH
Definition: psqlscan.h:33
@ PSCAN_EOL
Definition: psqlscan.h:35
@ PSCAN_INCOMPLETE
Definition: psqlscan.h:34
@ PSCAN_SEMICOLON
Definition: psqlscan.h:32
enum _promptStatus promptStatus_t
@ PROMPT_READY
Definition: psqlscan.h:41
void psql_scan_destroy(PsqlScanState state)
Definition: psqlscan.l:1022
PsqlScanResult psql_scan(PsqlScanState state, PQExpBuffer query_buf, promptStatus_t *prompt)
Definition: psqlscan.l:1121
PsqlScanState psql_scan_create(const PsqlScanCallbacks *callbacks)
Definition: psqlscan.l:1001
void psql_scan_setup(PsqlScanState state, const char *line, int line_len, int encoding, bool std_strings)
Definition: psqlscan.l:1059
static void error(void)
Definition: sql-dyntest.c:147
PGconn * conn
Definition: streamutil.c:52
const char * fmtId(const char *rawid)
Definition: string_utils.c:248
void setFmtEncoding(int encoding)
Definition: string_utils.c:69
void appendStringLiteral(PQExpBuffer buf, const char *str, int encoding, bool std_strings)
Definition: string_utils.c:351
bool force_unsupported
Definition: test_escape.c:26
PGconn * conn
Definition: test_escape.c:28
const char * conninfo
Definition: test_escape.c:27
const char * name
Definition: test_escape.c:40
bool supports_only_ascii_overlap
Definition: test_escape.c:58
bool(* escape)(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
Definition: test_escape.c:65
const char * escape
Definition: test_escape.c:77
const char * client_encoding
Definition: test_escape.c:75
size_t escape_len
Definition: test_escape.c:76
int main(int argc, char *argv[])
Definition: test_escape.c:809
#define TOSTR_CASE(sym)
static bool escape_replace(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
Definition: test_escape.c:189
static void escapify(PQExpBuffer buf, const char *str, size_t len)
Definition: test_escape.c:391
static bool escape_string(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
Definition: test_escape.c:166
static bool escape_literal(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
Definition: test_escape.c:90
#define TV_LEN(enc, string, len)
Definition: test_escape.c:283
static bool escape_append_literal(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
Definition: test_escape.c:215
struct pe_test_config pe_test_config
static void test_one_vector(pe_test_config *tc, const pe_test_vector *tv)
Definition: test_escape.c:768
static pe_test_escape_func pe_test_escape_funcs[]
Definition: test_escape.c:235
static const char * scan_res_s(PsqlScanResult res)
Definition: test_escape.c:459
static pe_test_vector pe_test_vectors[]
Definition: test_escape.c:284
static void test_psql_parse(pe_test_config *tc, PQExpBuffer testname, PQExpBuffer input_buf, PQExpBuffer details)
Definition: test_escape.c:481
struct pe_test_vector pe_test_vector
#define TV(enc, string)
Definition: test_escape.c:282
static bool escape_string_conn(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
Definition: test_escape.c:136
static bool escape_identifier(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
Definition: test_escape.c:113
static void usage(const char *hint)
Definition: test_escape.c:786
static void test_one_vector_escape(pe_test_config *tc, const pe_test_vector *tv, const pe_test_escape_func *ef)
Definition: test_escape.c:538
struct pe_test_escape_func pe_test_escape_func
#define NEVER_ACCESS_STR
static bool encoding_conflicts_ascii(int encoding)
Definition: test_escape.c:447
static const PsqlScanCallbacks test_scan_callbacks
Definition: test_escape.c:84
static void report_result(pe_test_config *tc, bool success, PQExpBuffer testname, PQExpBuffer details, const char *subname, const char *resultdesc)
Definition: test_escape.c:410
static bool escape_fmt_id(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
Definition: test_escape.c:225
int pg_encoding_verifymbstr(int encoding, const char *mbstr, int len)
Definition: wchar.c:2163