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