PostgreSQL Source Code git master
Loading...
Searching...
No Matches
test_escape.c
Go to the documentation of this file.
1/*
2 * test_escape.c Test escape functions
3 *
4 * Copyright (c) 2022-2026, 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 "common/jsonapi.h"
16#include "fe_utils/psqlscan.h"
18#include "getopt_long.h"
19#include "libpq-fe.h"
20#include "mb/pg_wchar.h"
21#include "utils/memdebug.h"
22
23
34
35#define NEVER_ACCESS_STR "\xff never-to-be-touched"
36
37
38/*
39 * An escape function to be tested by this test.
40 */
41typedef struct pe_test_escape_func
42{
43 const char *name;
44
45 /*
46 * Can the escape method report errors? If so, we validate that it does in
47 * case of various invalid inputs.
48 */
50
51 /*
52 * Is the escape method known to not handle invalidly encoded input? If
53 * so, we don't run the test unless --force-unsupported is used.
54 */
56
57 /*
58 * Is the escape method known to only handle encodings where no byte in a
59 * multi-byte characters are valid ascii.
60 */
62
63 /*
64 * Does the escape function have a length input?
65 */
67
69 const char *unescaped, size_t unescaped_len,
72
73/*
74 * A single test input for this test.
75 */
76typedef struct pe_test_vector
77{
78 const char *client_encoding;
79 size_t escape_len;
80 const char *escape;
82
83
84/*
85 * Callback functions from flex lexer. Not currently used by the test.
86 */
90
91
92/*
93 * Print the string into buf, making characters outside of plain ascii
94 * somewhat easier to recognize.
95 *
96 * The output format could stand to be improved significantly, it's not at all
97 * unambiguous.
98 */
99static void
100escapify(PQExpBuffer buf, const char *str, size_t len)
101{
102 for (size_t i = 0; i < len; i++)
103 {
104 char c = *str;
105
106 if (c == '\n')
108 else if (c == '\0')
110 else if (c < ' ' || c > '~')
111 appendPQExpBuffer(buf, "\\x%2x", (uint8_t) c);
112 else
114 str++;
115 }
116}
117
118static void
120 bool success,
121 const char *testname,
122 const char *details,
123 const char *subname,
124 const char *resultdesc)
125{
126 int test_id = ++tc->test_count;
127 bool print_details = true;
128 bool print_result = true;
129
130 if (success)
131 {
132 if (tc->verbosity <= 0)
133 print_details = false;
134 if (tc->verbosity < 0)
135 print_result = false;
136 }
137 else
138 tc->failure_count++;
139
140 if (print_details)
141 printf("%s", details);
142
143 if (print_result)
144 printf("%s %d - %s: %s: %s\n",
145 success ? "ok" : "not ok",
147 subname,
148 resultdesc);
149}
150
151/*
152 * Return true for encodings in which bytes in a multi-byte character look
153 * like valid ascii characters.
154 */
155static bool
157{
158 /*
159 * We don't store this property directly anywhere, but whether an encoding
160 * is a client-only encoding is a good proxy.
161 */
163 return true;
164 return false;
165}
166
167
168/*
169 * Confirm escaping doesn't read past the end of an allocation. Consider the
170 * result of malloc(4096), in the absence of freelist entries satisfying the
171 * allocation. On OpenBSD, reading one byte past the end of that object
172 * yields SIGSEGV.
173 *
174 * Run this test before the program's other tests, so freelists are minimal.
175 * len=4096 didn't SIGSEGV, likely due to free() calls in libpq. len=8192
176 * did. Use 128 KiB, to somewhat insulate the outcome from distant new free()
177 * calls and libc changes.
178 */
179static void
181{
183 size_t input_len = 0x20000;
184 char *input;
185
186 /* prepare input */
187 input = pg_malloc(input_len);
188 memset(input, '-', input_len - 1);
189 input[input_len - 1] = 0xfe;
190
191 /* name to describe the test */
193 appendPQExpBuffer(testname, ">repeat(%c, %zu)", input[0], input_len - 1);
194 escapify(testname, input + input_len - 1, 1);
195 appendPQExpBuffer(testname, "< - GB18030 - PQescapeLiteral");
196
197 /* test itself */
198 PQsetClientEncoding(tc->conn, "GB18030");
199 report_result(tc, PQescapeLiteral(tc->conn, input, input_len) == NULL,
200 testname->data, "",
201 "input validity vs escape success", "ok");
202
204 pg_free(input);
205}
206
207/*
208 * Confirm json parsing doesn't read past the end of an allocation. This
209 * exercises wchar.c infrastructure like the true "escape" tests do, but this
210 * isn't an "escape" test.
211 */
212static void
214{
215 PQExpBuffer raw_buf;
217 const char input[] = "{\"\\u\xFE";
218 size_t input_len = sizeof(input) - 1;
219 JsonLexContext *lex;
220 JsonSemAction sem = {0}; /* no callbacks */
222
223 /* prepare input like test_one_vector_escape() does */
224 raw_buf = createPQExpBuffer();
225 appendBinaryPQExpBuffer(raw_buf, input, input_len);
227 VALGRIND_MAKE_MEM_NOACCESS(&raw_buf->data[input_len],
228 raw_buf->len - input_len);
229
230 /* name to describe the test */
233 escapify(testname, input, input_len);
234 appendPQExpBuffer(testname, "< - GB18030 - pg_parse_json");
235
236 /* test itself */
237 lex = makeJsonLexContextCstringLen(NULL, raw_buf->data, input_len,
238 PG_GB18030, false);
241 testname->data, "",
242 "diagnosed", json_errdetail(json_error, lex));
243
246 destroyPQExpBuffer(raw_buf);
247}
248
249
250static bool
252 const char *unescaped, size_t unescaped_len,
254{
255 char *escaped;
256
258 if (!escaped)
259 {
261 escape_err->data[escape_err->len - 1] = 0;
262 escape_err->len--;
263 return false;
264 }
265 else
266 {
269 return true;
270 }
271}
272
273static bool
275 const char *unescaped, size_t unescaped_len,
277{
278 char *escaped;
279
281 if (!escaped)
282 {
284 escape_err->data[escape_err->len - 1] = 0;
285 escape_err->len--;
286 return false;
287 }
288 else
289 {
292 return true;
293 }
294}
295
296static bool
298 const char *unescaped, size_t unescaped_len,
300{
301 int error;
302 size_t sz;
303
304 appendPQExpBufferChar(target, '\'');
305 enlargePQExpBuffer(target, unescaped_len * 2 + 1);
306 sz = PQescapeStringConn(conn, target->data + target->len,
308 &error);
309
310 target->len += sz;
311 appendPQExpBufferChar(target, '\'');
312
313 if (error)
314 {
316 escape_err->data[escape_err->len - 1] = 0;
317 escape_err->len--;
318 return false;
319 }
320 else
321 {
322 return true;
323 }
324}
325
326static bool
328 const char *unescaped, size_t unescaped_len,
330{
331 size_t sz;
332
333 appendPQExpBufferChar(target, '\'');
334 enlargePQExpBuffer(target, unescaped_len * 2 + 1);
335 sz = PQescapeString(target->data + target->len,
337 target->len += sz;
338 appendPQExpBufferChar(target, '\'');
339
340
341 return true;
342}
343
344/*
345 * Escape via s/'/''/. Non-core drivers invariably wrap libpq or use this
346 * method. It suffices iff the input passes encoding validation, so it's
347 * marked as supports_only_valid.
348 */
349static bool
351 const char *unescaped, size_t unescaped_len,
353{
354 const char *s = unescaped;
355
356 appendPQExpBufferChar(target, '\'');
357
358 for (int i = 0; i < unescaped_len; i++)
359 {
360 char c = *s;
361
362 if (c == '\'')
363 {
364 appendPQExpBufferStr(target, "''");
365 }
366 else
367 appendPQExpBufferChar(target, c);
368 s++;
369 }
370 appendPQExpBufferChar(target, '\'');
371
372 return true;
373}
374
375static bool
377 const char *unescaped, size_t unescaped_len,
379{
381
382 return true;
383}
384
385static bool
387 const char *unescaped, size_t unescaped_len,
389{
392
393 return true;
394}
395
397{
398 {
399 .name = "PQescapeLiteral",
400 .reports_errors = true,
401 .supports_input_length = true,
402 .escape = escape_literal,
403 },
404 {
405 .name = "PQescapeIdentifier",
406 .reports_errors = true,
407 .supports_input_length = true,
408 .escape = escape_identifier
409 },
410 {
411 .name = "PQescapeStringConn",
412 .reports_errors = true,
413 .supports_input_length = true,
414 .escape = escape_string_conn
415 },
416 {
417 .name = "PQescapeString",
418 .reports_errors = false,
419 .supports_input_length = true,
420 .escape = escape_string
421 },
422 {
423 .name = "replace",
424 .reports_errors = false,
425 .supports_only_valid = true,
426 .supports_only_ascii_overlap = true,
427 .supports_input_length = true,
428 .escape = escape_replace
429 },
430 {
431 .name = "appendStringLiteral",
432 .reports_errors = false,
433 .escape = escape_append_literal
434 },
435 {
436 .name = "fmtId",
437 .reports_errors = false,
438 .escape = escape_fmt_id
439 },
440};
441
442
443#define TV(enc, string) {.client_encoding = (enc), .escape=string, .escape_len=sizeof(string) - 1, }
444#define TV_LEN(enc, string, len) {.client_encoding = (enc), .escape=string, .escape_len=len, }
446{
447 /* expected to work sanity checks */
448 TV("UTF-8", "1"),
449 TV("UTF-8", "'"),
450 TV("UTF-8", "\""),
451
452 TV("UTF-8", "\'"),
453 TV("UTF-8", "\""),
454
455 TV("UTF-8", "\\"),
456
457 TV("UTF-8", "\\'"),
458 TV("UTF-8", "\\\""),
459
460 /* trailing multi-byte character, paddable in available space */
461 TV("UTF-8", "1\xC0"),
462 TV("UTF-8", "1\xE0 "),
463 TV("UTF-8", "1\xF0 "),
464 TV("UTF-8", "1\xF0 "),
465 TV("UTF-8", "1\xF0 "),
466
467 /* trailing multi-byte character, not enough space to pad */
468 TV("UTF-8", "1\xE0"),
469 TV("UTF-8", "1\xF0"),
470 TV("UTF-8", "\xF0"),
471
472 /* try to smuggle in something in invalid characters */
473 TV("UTF-8", "1\xE0'"),
474 TV("UTF-8", "1\xE0\""),
475 TV("UTF-8", "1\xF0'"),
476 TV("UTF-8", "1\xF0\""),
477 TV("UTF-8", "1\xF0'; "),
478 TV("UTF-8", "1\xF0\"; "),
479 TV("UTF-8", "1\xF0';;;;"),
480 TV("UTF-8", "1\xF0 ';;;;"),
481 TV("UTF-8", "1\xF0 \";;;;"),
482 TV("UTF-8", "1\xE0'; \\l ; "),
483 TV("UTF-8", "1\xE0\"; \\l ; "),
484
485 /* null byte handling */
486 TV("UTF-8", "some\0thing"),
487 TV("UTF-8", "some\0"),
488 TV("UTF-8", "some\xF0'\0"),
489 TV("UTF-8", "some\xF0'\0'"),
490 TV("UTF-8", "some\xF0" "ab\0'"),
491
492 /* GB18030's 4 byte encoding requires a 2nd byte limited values */
493 TV("GB18030", "\x90\x31"),
494 TV("GB18030", "\\\x81\x5c'"),
495 TV("GB18030", "\\\x81\x5c\""),
496 TV("GB18030", "\\\x81\x5c\0'"),
497
498 /*
499 * \x81 indicates a 2 byte char. ' and " are not a valid second byte, but
500 * that requires encoding verification to know. E.g. replace_string()
501 * doesn't cope.
502 */
503 TV("GB18030", "\\\x81';"),
504 TV("GB18030", "\\\x81\";"),
505
506 /*
507 * \x81 indicates a 2 byte char. \ is a valid second character.
508 */
509 TV("GB18030", "\\\x81\\';"),
510 TV("GB18030", "\\\x81\\\";"),
511 TV("GB18030", "\\\x81\0;"),
512 TV("GB18030", "\\\x81\0'"),
513 TV("GB18030", "\\\x81'\0"),
514
515 TV("SJIS", "\xF0\x40;"),
516
517 TV("SJIS", "\xF0';"),
518 TV("SJIS", "\xF0\";"),
519 TV("SJIS", "\xF0\0'"),
520 TV("SJIS", "\\\xF0\\';"),
521 TV("SJIS", "\\\xF0\\\";"),
522
523 TV("gbk", "\x80';"),
524 TV("gbk", "\x80"),
525 TV("gbk", "\x80'"),
526 TV("gbk", "\x80\""),
527 TV("gbk", "\x80\\"),
528
529 TV("sql_ascii", "1\xC0'"),
530
531 /*
532 * Testcases that are not null terminated for the specified input length.
533 * That's interesting to verify that escape functions don't read beyond
534 * the intended input length.
535 *
536 * One interesting special case is GB18030, which has the odd behaviour
537 * needing to read beyond the first byte to determine the length of a
538 * multi-byte character.
539 */
540 TV_LEN("gbk", "\x80", 1),
541 TV_LEN("GB18030", "\x80", 1),
542 TV_LEN("GB18030", "\x80\0", 2),
543 TV_LEN("GB18030", "\x80\x30", 2),
544 TV_LEN("GB18030", "\x80\x30\0", 3),
545 TV_LEN("GB18030", "\x80\x30\x30", 3),
546 TV_LEN("GB18030", "\x80\x30\x30\0", 4),
547 TV_LEN("UTF-8", "\xC3\xb6 ", 1),
548 TV_LEN("UTF-8", "\xC3\xb6 ", 2),
549};
550
551
552static const char *
554{
555#define TOSTR_CASE(sym) case sym: return #sym
556
557 switch (res)
558 {
563 }
564
566 return ""; /* silence compiler */
567}
568
569/*
570 * Verify that psql parses the input as a single statement. If this property
571 * is violated, the escape function does not effectively protect against
572 * smuggling in a second statement.
573 */
574static void
576 PQExpBuffer input_buf, PQExpBuffer details)
577{
582 int matches = 0;
583 bool test_fails;
584 const char *resdesc;
585
587
589
590 /*
591 * TODO: This hardcodes standard conforming strings, it would be useful to
592 * test without as well.
593 */
594 psql_scan_setup(scan_state, input_buf->data, input_buf->len,
595 PQclientEncoding(tc->conn), 1);
596
597 do
598 {
600
603
604 appendPQExpBuffer(details,
605 "#\t\t %d: scan_result: %s prompt: %u, query_buf: ",
607 escapify(details, query_buf->data, query_buf->len);
608 appendPQExpBufferChar(details, '\n');
609
610 matches++;
611 }
613
616
618
619 if (matches > 1)
620 resdesc = "more than one match";
621 else if (scan_result != PSCAN_EOL)
622 resdesc = "unexpected end state";
623 else
624 resdesc = "ok";
625
626 report_result(tc, !test_fails, testname->data, details->data,
627 "psql parse",
628 resdesc);
629}
630
631static void
633{
635 PQExpBuffer details;
636 PQExpBuffer raw_buf;
643 bool escape_success;
646
649 details = createPQExpBuffer();
650 raw_buf = createPQExpBuffer();
652
653 if (ef->supports_only_ascii_overlap &&
655 {
656 goto out;
657 }
658
659 /* name to describe the test */
662 appendPQExpBuffer(testname, "< - %s - %s",
663 tv->client_encoding, ef->name);
664
665 /* details to describe the test, to allow for debugging */
666 appendPQExpBuffer(details, "#\t input: %zd bytes: ",
667 tv->escape_len);
668 escapify(details, tv->escape, tv->escape_len);
669 appendPQExpBufferChar(details, '\n');
670 appendPQExpBuffer(details, "#\t encoding: %s\n",
671 tv->client_encoding);
672
673
674 /* check encoding of input, to compare with after the test */
676 tv->escape,
677 tv->escape_len);
679 appendPQExpBuffer(details, "#\t input encoding valid: %d\n",
681
683 tv->escape,
684 strnlen(tv->escape, tv->escape_len));
686 appendPQExpBuffer(details, "#\t input encoding valid till 0: %d\n",
688
689 appendPQExpBuffer(details, "#\t escape func: %s\n",
690 ef->name);
691
692 if (!input_encoding_valid && ef->supports_only_valid
693 && !tc->force_unsupported)
694 goto out;
695
696
697 /*
698 * Put the to-be-escaped data into a buffer, so that we
699 *
700 * a) can mark memory beyond end of the string as inaccessible when using
701 * valgrind
702 *
703 * b) can append extra data beyond the length passed to the escape
704 * function, to verify that that data is not processed.
705 *
706 * TODO: Should we instead/additionally escape twice, once with unmodified
707 * and once with appended input? That way we could compare the two.
708 */
709 appendBinaryPQExpBuffer(raw_buf, tv->escape, tv->escape_len);
710
711 if (ef->supports_input_length)
712 {
713 /*
714 * Append likely invalid string that does *not* contain a null byte
715 * (which'd prevent some invalid accesses to later memory).
716 */
718
720 raw_buf->len - tv->escape_len);
721 }
722 else
723 {
724 /* append invalid string, after \0 */
725 appendPQExpBufferChar(raw_buf, 0);
727
728 VALGRIND_MAKE_MEM_NOACCESS(&raw_buf->data[tv->escape_len + 1],
729 raw_buf->len - tv->escape_len - 1);
730 }
731
732 /* call the to-be-tested escape function */
733 escape_success = ef->escape(tc->conn, escape_buf,
734 raw_buf->data, tv->escape_len,
735 escape_err);
736 if (!escape_success)
737 {
738 appendPQExpBuffer(details, "#\t escape error: %s\n",
740 }
741
742 if (escape_buf->len > 0)
743 {
744 bool contains_never;
745
746 appendPQExpBuffer(details, "#\t escaped string: %zd bytes: ", escape_buf->len);
747 escapify(details, escape_buf->data, escape_buf->len);
748 appendPQExpBufferChar(details, '\n');
749
751 escape_buf->data,
752 escape_buf->len);
754
755 appendPQExpBuffer(details, "#\t escape encoding valid: %d\n",
757
758 /*
759 * Verify that no data beyond the end of the input is included in the
760 * escaped string. It'd be better to use something like memmem()
761 * here, but that's not available everywhere.
762 */
764 report_result(tc, contains_never, testname->data, details->data,
765 "escaped data beyond end of input",
766 contains_never ? "no" : "all secrets revealed");
767 }
768 else
769 {
772 }
773
774 /*
775 * If the test reports errors, and the input was invalidly encoded,
776 * escaping should fail. One edge-case that we accept for now is that the
777 * input could have an embedded null byte, which the escape functions will
778 * just treat as a shorter string. If the encoding error is after the zero
779 * byte, the output thus won't contain it.
780 */
781 if (ef->reports_errors)
782 {
783 bool ok = true;
784 const char *resdesc = "ok";
785
786 if (escape_success)
787 {
789 {
790 ok = false;
791 resdesc = "invalid input escaped successfully";
792 }
793 else if (!input_encoding_valid)
794 resdesc = "invalid input escaped successfully, due to zero byte";
795 }
796 else
797 {
799 {
800 ok = false;
801 resdesc = "valid input failed to escape";
802 }
803 else if (input_encoding_valid)
804 resdesc = "valid input failed to escape, due to zero byte";
805 }
806
807 report_result(tc, ok, testname->data, details->data,
808 "input validity vs escape success",
809 resdesc);
810 }
811
812 /*
813 * If the input is invalidly encoded, the output should also be invalidly
814 * encoded. We accept the same zero-byte edge case as above.
815 */
816 {
817 bool ok = true;
818 const char *resdesc = "ok";
819
821 {
822 resdesc = "invalid input produced valid output, due to zero byte";
823 }
825 {
826 ok = false;
827 resdesc = "valid input produced invalid output";
828 }
829 else if (!input_encoding0_valid &&
830 (!ef->reports_errors || escape_success) &&
832 {
833 ok = false;
834 resdesc = "invalid input produced valid output";
835 }
836
837 report_result(tc, ok, testname->data, details->data,
838 "input and escaped encoding validity",
839 resdesc);
840 }
841
842 /*
843 * Test psql parsing whenever we get any string back, even if the escape
844 * function returned a failure.
845 */
846 if (escape_buf->len > 0)
847 {
849 escape_buf, details);
850 }
851
852out:
854 destroyPQExpBuffer(details);
857 destroyPQExpBuffer(raw_buf);
858}
859
860static void
862{
863 if (PQsetClientEncoding(tc->conn, tv->client_encoding))
864 {
865 fprintf(stderr, "failed to set encoding to %s:\n%s\n",
866 tv->client_encoding, PQerrorMessage(tc->conn));
867 exit(1);
868 }
869
871 {
873
875 }
876}
877
878static void
879usage(const char *hint)
880{
881 if (hint)
882 fprintf(stderr, "Error: %s\n\n", hint);
883
884 printf("PostgreSQL escape function test\n"
885 "\n"
886 "Usage:\n"
887 " test_escape --conninfo=CONNINFO [OPTIONS]\n"
888 "\n"
889 "Options:\n"
890 " -h, --help show this help\n"
891 " -c, --conninfo=CONNINFO connection information to use\n"
892 " -v, --verbose show test details even for successes\n"
893 " -q, --quiet only show failures\n"
894 " -f, --force-unsupported test invalid input even if unsupported\n"
895 );
896
897 if (hint)
898 exit(1);
899}
900
901int
902main(int argc, char *argv[])
903{
904 pe_test_config tc = {0};
905 int c;
906 int option_index;
907
908 static const struct option long_options[] = {
909 {"help", no_argument, NULL, 'h'},
910 {"conninfo", required_argument, NULL, 'c'},
911 {"verbose", no_argument, NULL, 'v'},
912 {"quiet", no_argument, NULL, 'q'},
913 {"force-unsupported", no_argument, NULL, 'f'},
914 {NULL, 0, NULL, 0},
915 };
916
917 while ((c = getopt_long(argc, argv, "c:fhqv", long_options, &option_index)) != -1)
918 {
919 switch (c)
920 {
921 case 'h':
922 usage(NULL);
923 exit(0);
924 break;
925 case 'c':
926 tc.conninfo = optarg;
927 break;
928 case 'v':
929 tc.verbosity++;
930 break;
931 case 'q':
932 tc.verbosity--;
933 break;
934 case 'f':
935 tc.force_unsupported = true;
936 break;
937 }
938 }
939
940 if (argc - optind >= 1)
941 usage("unused option(s) specified");
942
943 if (tc.conninfo == NULL)
944 usage("--conninfo needs to be specified");
945
946 tc.conn = PQconnectdb(tc.conninfo);
947
948 if (!tc.conn || PQstatus(tc.conn) != CONNECTION_OK)
949 {
950 fprintf(stderr, "could not connect: %s\n",
951 PQerrorMessage(tc.conn));
952 exit(1);
953 }
954
957
958 for (int i = 0; i < lengthof(pe_test_vectors); i++)
959 {
961 }
962
963 PQfinish(tc.conn);
964
965 printf("# %d failures\n", tc.failure_count);
966 printf("1..%d\n", tc.test_count);
967 return tc.failure_count > 0;
968}
#define pg_unreachable()
Definition c.h:367
#define lengthof(array)
Definition c.h:873
int main(void)
#define fprintf(file, fmt, msg)
Definition cubescan.l:21
PGconn * PQconnectdb(const char *conninfo)
Definition fe-connect.c:830
ConnStatusType PQstatus(const PGconn *conn)
int PQclientEncoding(const PGconn *conn)
void PQfinish(PGconn *conn)
char * PQerrorMessage(const PGconn *conn)
int PQsetClientEncoding(PGconn *conn, const char *encoding)
void PQfreemem(void *ptr)
Definition fe-exec.c:4049
size_t PQescapeStringConn(PGconn *conn, char *to, const char *from, size_t length, int *error)
Definition fe-exec.c:4194
size_t PQescapeString(char *to, const char *from, size_t length)
Definition fe-exec.c:4216
char * PQescapeLiteral(PGconn *conn, const char *str, size_t len)
Definition fe-exec.c:4399
char * PQescapeIdentifier(PGconn *conn, const char *str, size_t len)
Definition fe-exec.c:4405
void * pg_malloc(size_t size)
Definition fe_memutils.c:47
void pg_free(void *ptr)
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
FILE * input
static bool success
Definition initdb.c:188
static char * encoding
Definition initdb.c:139
int i
Definition isn.c:77
JsonParseErrorType pg_parse_json(JsonLexContext *lex, const JsonSemAction *sem)
Definition jsonapi.c:744
JsonLexContext * makeJsonLexContextCstringLen(JsonLexContext *lex, const char *json, size_t len, int encoding, bool need_escapes)
Definition jsonapi.c:392
char * json_errdetail(JsonParseErrorType error, JsonLexContext *lex)
Definition jsonapi.c:2404
void freeJsonLexContext(JsonLexContext *lex)
Definition jsonapi.c:687
JsonParseErrorType
Definition jsonapi.h:35
@ JSON_UNICODE_ESCAPE_FORMAT
Definition jsonapi.h:54
@ CONNECTION_OK
Definition libpq-fe.h:90
#define VALGRIND_MAKE_MEM_NOACCESS(addr, size)
Definition memdebug.h:27
static void usage(void)
const void size_t len
PGDLLIMPORT int optind
Definition getopt.c:47
PGDLLIMPORT char * optarg
Definition getopt.c:49
NameData subname
static char buf[DEFAULT_XLOG_SEG_SIZE]
@ PG_GB18030
Definition pg_wchar.h:118
#define PG_ENCODING_BE_LAST
Definition pg_wchar.h:125
#define printf(...)
Definition port.h:266
PQExpBuffer createPQExpBuffer(void)
Definition pqexpbuffer.c:72
int enlargePQExpBuffer(PQExpBuffer str, size_t needed)
void resetPQExpBuffer(PQExpBuffer str)
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
void appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)
void destroyPQExpBuffer(PQExpBuffer str)
void appendPQExpBufferChar(PQExpBuffer str, char ch)
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
char * c
static int fb(int x)
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:1034
PsqlScanResult psql_scan(PsqlScanState state, PQExpBuffer query_buf, promptStatus_t *prompt)
Definition psqlscan.l:1133
PsqlScanState psql_scan_create(const PsqlScanCallbacks *callbacks)
Definition psqlscan.l:1013
void psql_scan_setup(PsqlScanState state, const char *line, int line_len, int encoding, bool std_strings)
Definition psqlscan.l:1071
static void error(void)
PGconn * conn
Definition streamutil.c:52
const char * fmtId(const char *rawid)
void setFmtEncoding(int encoding)
void appendStringLiteral(PQExpBuffer buf, const char *str, int encoding, bool std_strings)
bool force_unsupported
Definition test_escape.c:27
PGconn * conn
Definition test_escape.c:29
const char * conninfo
Definition test_escape.c:28
const char * name
Definition test_escape.c:43
bool supports_only_ascii_overlap
Definition test_escape.c:61
bool(* escape)(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
Definition test_escape.c:68
const char * escape
Definition test_escape.c:80
const char * client_encoding
Definition test_escape.c:78
size_t escape_len
Definition test_escape.c:79
static void test_gb18030_json(pe_test_config *tc)
#define TOSTR_CASE(sym)
static bool escape_replace(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
static void escapify(PQExpBuffer buf, const char *str, size_t len)
static bool escape_string(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
static bool escape_literal(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
#define TV_LEN(enc, string, len)
static bool escape_append_literal(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
static void test_one_vector(pe_test_config *tc, const pe_test_vector *tv)
static pe_test_escape_func pe_test_escape_funcs[]
static const char * scan_res_s(PsqlScanResult res)
static pe_test_vector pe_test_vectors[]
static void report_result(pe_test_config *tc, bool success, const char *testname, const char *details, const char *subname, const char *resultdesc)
static void test_psql_parse(pe_test_config *tc, PQExpBuffer testname, PQExpBuffer input_buf, PQExpBuffer details)
#define TV(enc, string)
static bool escape_string_conn(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
static bool escape_identifier(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
static void test_one_vector_escape(pe_test_config *tc, const pe_test_vector *tv, const pe_test_escape_func *ef)
#define NEVER_ACCESS_STR
Definition test_escape.c:35
static bool encoding_conflicts_ascii(int encoding)
static const PsqlScanCallbacks test_scan_callbacks
Definition test_escape.c:87
static bool escape_fmt_id(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
static void test_gb18030_page_multiple(pe_test_config *tc)
static JsonSemAction sem
int pg_encoding_verifymbstr(int encoding, const char *mbstr, int len)
Definition wchar.c:2001