PostgreSQL Source Code: src/test/modules/test_escape/test_escape.c Source File (original) (raw)
1
2
3
4
5
6
7
8
9
11
13#include <stdio.h>
14
22
23
25{
30
34
35#define NEVER_ACCESS_STR "\xff never-to-be-touched"
36
37
38
39
40
42{
44
45
46
47
48
50
51
52
53
54
56
57
58
59
60
62
63
64
65
67
69 const char *unescaped, size_t unescaped_len,
72
73
74
75
77{
82
83
84
85
86
88 NULL
89};
90
91
92
93
94
95
96
97
98
99static void
101{
102 for (size_t i = 0; i < len; i++)
103 {
105
106 if (c == '\n')
108 else if (c == '\0')
112 else
115 }
116}
117
118static void
121 const char *testname,
122 const char *details,
124 const char *resultdesc)
125{
127 bool print_details = true;
128 bool print_result = true;
129
131 {
133 print_details = false;
135 print_result = false;
136 }
137 else
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",
146 test_id, testname,
148 resultdesc);
149}
150
151
152
153
154
155static bool
157{
158
159
160
161
163 return true;
164 return false;
165}
166
167
168
169
170
171
172
173
174
175
176
177
178
179static void
181{
183 size_t input_len = 0x20000;
185
186
188 memset(input, '-', input_len - 1);
189 input[input_len - 1] = 0xfe;
190
191
196
197
200 testname->data, "",
201 "input validity vs escape success", "ok");
202
205}
206
207
208
209
210
211
212static void
214{
217 const char input[] = "{\"\\u\xFE";
218 size_t input_len = sizeof(input) - 1;
222
223
228 raw_buf->len - input_len);
229
230
235
236
241 testname->data, "",
243
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{
302 size_t sz;
303
307 unescaped, unescaped_len,
309
310 target->len += sz;
312
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
336 unescaped, unescaped_len);
337 target->len += sz;
339
340
341 return true;
342}
343
344
345
346
347
348
349static bool
351 const char *unescaped, size_t unescaped_len,
353{
354 const char *s = unescaped;
355
357
358 for (int i = 0; i < unescaped_len; i++)
359 {
360 char c = *s;
361
362 if (c == '\'')
363 {
365 }
366 else
368 s++;
369 }
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,
403 },
404 {
405 .name = "PQescapeIdentifier",
406 .reports_errors = true,
407 .supports_input_length = true,
409 },
410 {
411 .name = "PQescapeStringConn",
412 .reports_errors = true,
413 .supports_input_length = true,
415 },
416 {
417 .name = "PQescapeString",
418 .reports_errors = false,
419 .supports_input_length = true,
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,
429 },
430 {
431 .name = "appendStringLiteral",
432 .reports_errors = false,
434 },
435 {
436 .name = "fmtId",
437 .reports_errors = false,
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
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
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
468 TV("UTF-8", "1\xE0"),
469 TV("UTF-8", "1\xF0"),
470 TV("UTF-8", "\xF0"),
471
472
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
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
493 TV("GB18030", "\x90\x31"),
494 TV("GB18030", "\\\x81\x5c'"),
495 TV("GB18030", "\\\x81\x5c\""),
496 TV("GB18030", "\\\x81\x5c\0'"),
497
498
499
500
501
502
503 TV("GB18030", "\\\x81';"),
504 TV("GB18030", "\\\x81\";"),
505
506
507
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("mule_internal", "\\\x9c';\0;"),
530
531 TV("sql_ascii", "1\xC0'"),
532
533
534
535
536
537
538
539
540
541
542 TV_LEN("gbk", "\x80", 1),
543 TV_LEN("GB18030", "\x80", 1),
544 TV_LEN("GB18030", "\x80\0", 2),
545 TV_LEN("GB18030", "\x80\x30", 2),
546 TV_LEN("GB18030", "\x80\x30\0", 3),
547 TV_LEN("GB18030", "\x80\x30\x30", 3),
548 TV_LEN("GB18030", "\x80\x30\x30\0", 4),
549 TV_LEN("UTF-8", "\xC3\xb6 ", 1),
550 TV_LEN("UTF-8", "\xC3\xb6 ", 2),
551};
552
553
554static const char *
556{
557#define TOSTR_CASE(sym) case sym: return #sym
558
559 switch (res)
560 {
565 }
566
568 return "";
569}
570
571
572
573
574
575
576static void
579{
584 int matches = 0;
585 bool test_fails;
586 const char *resdesc;
587
589
591
592
593
594
595
598
599 do
600 {
602
603 scan_result = psql_scan(scan_state, query_buf,
604 &prompt_status);
605
607 "#\t\t %d: scan_result: %s prompt: %u, query_buf: ",
608 matches, scan_res_s(scan_result), prompt_status);
611
612 matches++;
613 }
615
618
619 test_fails = matches > 1 || scan_result != PSCAN_EOL;
620
621 if (matches > 1)
622 resdesc = "more than one match";
623 else if (scan_result != PSCAN_EOL)
624 resdesc = "unexpected end state";
625 else
626 resdesc = "ok";
627
629 "psql parse",
630 resdesc);
631}
632
633static void
635{
641 size_t input_encoding_validlen;
642 bool input_encoding_valid;
643 size_t input_encoding0_validlen;
644 bool input_encoding0_valid;
645 bool escape_success;
646 size_t escape_encoding_length;
647 bool escape_encoding_valid;
648
654
657 {
658 goto out;
659 }
660
661
666
667
674
675
676
680 input_encoding_valid = input_encoding_validlen == tv->escape_len;
682 input_encoding_valid);
683
688 appendPQExpBuffer(details, "#\t input encoding valid till 0: %d\n",
689 input_encoding0_valid);
690
693
696 goto out;
697
698
699
700
701
702
703
704
705
706
707
708
709
710
712
714 {
715
716
717
718
720
723 }
724 else
725 {
726
729
732 }
733
734
735 escape_success = ef->escape(tc->conn, escape_buf,
737 escape_err);
738 if (!escape_success)
739 {
741 escape_err->data);
742 }
743
744 if (escape_buf->len > 0)
745 {
746 bool contains_never;
747
748 appendPQExpBuffer(details, "#\t escaped string: %zd bytes: ", escape_buf->len);
751
753 escape_buf->data,
754 escape_buf->len);
755 escape_encoding_valid = escape_encoding_length == escape_buf->len;
756
758 escape_encoding_valid);
759
760
761
762
763
764
767 "escaped data beyond end of input",
768 contains_never ? "no" : "all secrets revealed");
769 }
770 else
771 {
772 escape_encoding_length = 0;
773 escape_encoding_valid = 1;
774 }
775
776
777
778
779
780
781
782
784 {
785 bool ok = true;
786 const char *resdesc = "ok";
787
788 if (escape_success)
789 {
790 if (!input_encoding0_valid)
791 {
792 ok = false;
793 resdesc = "invalid input escaped successfully";
794 }
795 else if (!input_encoding_valid)
796 resdesc = "invalid input escaped successfully, due to zero byte";
797 }
798 else
799 {
800 if (input_encoding0_valid)
801 {
802 ok = false;
803 resdesc = "valid input failed to escape";
804 }
805 else if (input_encoding_valid)
806 resdesc = "valid input failed to escape, due to zero byte";
807 }
808
810 "input validity vs escape success",
811 resdesc);
812 }
813
814
815
816
817
818 {
819 bool ok = true;
820 const char *resdesc = "ok";
821
822 if (input_encoding0_valid && !input_encoding_valid && escape_encoding_valid)
823 {
824 resdesc = "invalid input produced valid output, due to zero byte";
825 }
826 else if (input_encoding0_valid && !escape_encoding_valid)
827 {
828 ok = false;
829 resdesc = "valid input produced invalid output";
830 }
831 else if (!input_encoding0_valid &&
833 escape_encoding_valid)
834 {
835 ok = false;
836 resdesc = "invalid input produced valid output";
837 }
838
840 "input and escaped encoding validity",
841 resdesc);
842 }
843
844
845
846
847
848 if (escape_buf->len > 0)
849 {
851 escape_buf, details);
852 }
853
854out:
860}
861
862static void
864{
866 {
867 fprintf(stderr, "failed to set encoding to %s:\n%s\n",
869 exit(1);
870 }
871
873 {
875
877 }
878}
879
880static void
882{
883 if (hint)
884 fprintf(stderr, "Error: %s\n\n", hint);
885
886 printf("PostgreSQL escape function test\n"
887 "\n"
888 "Usage:\n"
889 " test_escape --conninfo=CONNINFO [OPTIONS]\n"
890 "\n"
891 "Options:\n"
892 " -h, --help show this help\n"
893 " -c, --conninfo=CONNINFO connection information to use\n"
894 " -v, --verbose show test details even for successes\n"
895 " -q, --quiet only show failures\n"
896 " -f, --force-unsupported test invalid input even if unsupported\n"
897 );
898
899 if (hint)
900 exit(1);
901}
902
903int
904main(int argc, char *argv[])
905{
907 int c;
908 int option_index;
909
910 static const struct option long_options[] = {
915 {"force-unsupported", no_argument, NULL, 'f'},
916 {NULL, 0, NULL, 0},
917 };
918
919 while ((c = getopt_long(argc, argv, "c:fhqv", long_options, &option_index)) != -1)
920 {
921 switch (c)
922 {
923 case 'h':
925 exit(0);
926 break;
927 case 'c':
929 break;
930 case 'v':
932 break;
933 case 'q':
935 break;
936 case 'f':
938 break;
939 }
940 }
941
942 if (argc - optind >= 1)
943 usage("unused option(s) specified");
944
946 usage("--conninfo needs to be specified");
947
949
951 {
952 fprintf(stderr, "could not connect: %s\n",
954 exit(1);
955 }
956
959
961 {
963 }
964
966
970}
#define fprintf(file, fmt, msg)
PGconn * PQconnectdb(const char *conninfo)
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)
size_t PQescapeStringConn(PGconn *conn, char *to, const char *from, size_t length, int *error)
size_t PQescapeString(char *to, const char *from, size_t length)
char * PQescapeLiteral(PGconn *conn, const char *str, size_t len)
char * PQescapeIdentifier(PGconn *conn, const char *str, size_t len)
void * pg_malloc(size_t size)
int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex)
#define required_argument
JsonParseErrorType pg_parse_json(JsonLexContext *lex, const JsonSemAction *sem)
JsonLexContext * makeJsonLexContextCstringLen(JsonLexContext *lex, const char *json, size_t len, int encoding, bool need_escapes)
char * json_errdetail(JsonParseErrorType error, JsonLexContext *lex)
void freeJsonLexContext(JsonLexContext *lex)
@ JSON_UNICODE_ESCAPE_FORMAT
#define VALGRIND_MAKE_MEM_NOACCESS(addr, size)
PGDLLIMPORT char * optarg
#define PG_ENCODING_BE_LAST
size_t strnlen(const char *str, size_t maxlen)
PQExpBuffer createPQExpBuffer(void)
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)
enum _promptStatus promptStatus_t
void psql_scan_destroy(PsqlScanState state)
PsqlScanResult psql_scan(PsqlScanState state, PQExpBuffer query_buf, promptStatus_t *prompt)
PsqlScanState psql_scan_create(const PsqlScanCallbacks *callbacks)
void psql_scan_setup(PsqlScanState state, const char *line, int line_len, int encoding, bool std_strings)
const char * fmtId(const char *rawid)
void setFmtEncoding(int encoding)
void appendStringLiteral(PQExpBuffer buf, const char *str, int encoding, bool std_strings)
bool supports_only_ascii_overlap
bool supports_input_length
bool(* escape)(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
const char * client_encoding
static void test_gb18030_json(pe_test_config *tc)
int main(int argc, char *argv[])
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)
struct pe_test_config pe_test_config
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)
struct pe_test_vector pe_test_vector
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 usage(const char *hint)
static void test_one_vector_escape(pe_test_config *tc, const pe_test_vector *tv, const pe_test_escape_func *ef)
struct pe_test_escape_func pe_test_escape_func
static bool encoding_conflicts_ascii(int encoding)
static const PsqlScanCallbacks test_scan_callbacks
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)
int pg_encoding_verifymbstr(int encoding, const char *mbstr, int len)