PostgreSQL Source Code git master
Loading...
Searching...
No Matches
test_escape.c File Reference
#include "postgres_fe.h"
#include <string.h>
#include <stdio.h>
#include "common/jsonapi.h"
#include "fe_utils/psqlscan.h"
#include "fe_utils/string_utils.h"
#include "getopt_long.h"
#include "libpq-fe.h"
#include "mb/pg_wchar.h"
#include "utils/memdebug.h"
Include dependency graph for test_escape.c:

Go to the source code of this file.

Data Structures

struct  pe_test_config
 
struct  pe_test_escape_func
 
struct  pe_test_vector
 

Macros

#define NEVER_ACCESS_STR   "\xff never-to-be-touched"
 
#define TV(enc, string)   {.client_encoding = (enc), .escape=string, .escape_len=sizeof(string) - 1, }
 
#define TV_LEN(enc, string, len)   {.client_encoding = (enc), .escape=string, .escape_len=len, }
 
#define TOSTR_CASE(sym)   case sym: return #sym
 

Typedefs

typedef struct pe_test_config pe_test_config
 
typedef struct pe_test_escape_func pe_test_escape_func
 
typedef struct pe_test_vector pe_test_vector
 

Functions

static void escapify (PQExpBuffer buf, const char *str, size_t len)
 
static void report_result (pe_test_config *tc, bool success, const char *testname, const char *details, const char *subname, const char *resultdesc)
 
static bool encoding_conflicts_ascii (int encoding)
 
static void test_gb18030_page_multiple (pe_test_config *tc)
 
static void test_gb18030_json (pe_test_config *tc)
 
static bool escape_literal (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 bool escape_string_conn (PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
 
static bool escape_string (PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
 
static bool escape_replace (PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
 
static bool escape_append_literal (PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
 
static bool escape_fmt_id (PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
 
static const charscan_res_s (PsqlScanResult res)
 
static void test_psql_parse (pe_test_config *tc, PQExpBuffer testname, PQExpBuffer input_buf, PQExpBuffer details)
 
static void test_one_vector_escape (pe_test_config *tc, const pe_test_vector *tv, const pe_test_escape_func *ef)
 
static void test_one_vector (pe_test_config *tc, const pe_test_vector *tv)
 
static void usage (const char *hint)
 
int main (int argc, char *argv[])
 

Variables

static const PsqlScanCallbacks test_scan_callbacks
 
static pe_test_escape_func pe_test_escape_funcs []
 
static pe_test_vector pe_test_vectors []
 

Macro Definition Documentation

◆ NEVER_ACCESS_STR

#define NEVER_ACCESS_STR   "\xff never-to-be-touched"

Definition at line 35 of file test_escape.c.

◆ TOSTR_CASE

#define TOSTR_CASE (   sym)    case sym: return #sym

◆ TV

#define TV (   enc,
  string 
)    {.client_encoding = (enc), .escape=string, .escape_len=sizeof(string) - 1, }

Definition at line 443 of file test_escape.c.

◆ TV_LEN

#define TV_LEN (   enc,
  string,
  len 
)    {.client_encoding = (enc), .escape=string, .escape_len=len, }

Definition at line 444 of file test_escape.c.

Typedef Documentation

◆ pe_test_config

◆ pe_test_escape_func

◆ pe_test_vector

Function Documentation

◆ encoding_conflicts_ascii()

static bool encoding_conflicts_ascii ( int  encoding)
static

Definition at line 156 of file test_escape.c.

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}
static char * encoding
Definition initdb.c:139
#define PG_ENCODING_BE_LAST
Definition pg_wchar.h:125

References encoding, and PG_ENCODING_BE_LAST.

Referenced by test_one_vector_escape().

◆ escape_append_literal()

static bool escape_append_literal ( PGconn conn,
PQExpBuffer  target,
const char unescaped,
size_t  unescaped_len,
PQExpBuffer  escape_err 
)
static

Definition at line 376 of file test_escape.c.

379{
381
382 return true;
383}
int PQclientEncoding(const PGconn *conn)
static int fb(int x)
PGconn * conn
Definition streamutil.c:52
void appendStringLiteral(PQExpBuffer buf, const char *str, int encoding, bool std_strings)

References appendStringLiteral(), conn, fb(), and PQclientEncoding().

◆ escape_fmt_id()

static bool escape_fmt_id ( PGconn conn,
PQExpBuffer  target,
const char unescaped,
size_t  unescaped_len,
PQExpBuffer  escape_err 
)
static

Definition at line 386 of file test_escape.c.

389{
392
393 return true;
394}
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
const char * fmtId(const char *rawid)
void setFmtEncoding(int encoding)

References appendPQExpBufferStr(), conn, fb(), fmtId(), PQclientEncoding(), and setFmtEncoding().

◆ escape_identifier()

static bool escape_identifier ( PGconn conn,
PQExpBuffer  target,
const char unescaped,
size_t  unescaped_len,
PQExpBuffer  escape_err 
)
static

Definition at line 274 of file test_escape.c.

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}
char * PQerrorMessage(const PGconn *conn)
void PQfreemem(void *ptr)
Definition fe-exec.c:4049
char * PQescapeIdentifier(PGconn *conn, const char *str, size_t len)
Definition fe-exec.c:4405

References appendPQExpBufferStr(), conn, fb(), PQerrorMessage(), PQescapeIdentifier(), and PQfreemem().

◆ escape_literal()

static bool escape_literal ( PGconn conn,
PQExpBuffer  target,
const char unescaped,
size_t  unescaped_len,
PQExpBuffer  escape_err 
)
static

Definition at line 251 of file test_escape.c.

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}
char * PQescapeLiteral(PGconn *conn, const char *str, size_t len)
Definition fe-exec.c:4399

References appendPQExpBufferStr(), conn, fb(), PQerrorMessage(), PQescapeLiteral(), and PQfreemem().

◆ escape_replace()

static bool escape_replace ( PGconn conn,
PQExpBuffer  target,
const char unescaped,
size_t  unescaped_len,
PQExpBuffer  escape_err 
)
static

Definition at line 350 of file test_escape.c.

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}
int i
Definition isn.c:77
void appendPQExpBufferChar(PQExpBuffer str, char ch)
char * c

References appendPQExpBufferChar(), appendPQExpBufferStr(), fb(), and i.

◆ escape_string()

static bool escape_string ( PGconn conn,
PQExpBuffer  target,
const char unescaped,
size_t  unescaped_len,
PQExpBuffer  escape_err 
)
static

Definition at line 327 of file test_escape.c.

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}
size_t PQescapeString(char *to, const char *from, size_t length)
Definition fe-exec.c:4216
int enlargePQExpBuffer(PQExpBuffer str, size_t needed)

References appendPQExpBufferChar(), PQExpBufferData::data, enlargePQExpBuffer(), fb(), PQExpBufferData::len, and PQescapeString().

◆ escape_string_conn()

static bool escape_string_conn ( PGconn conn,
PQExpBuffer  target,
const char unescaped,
size_t  unescaped_len,
PQExpBuffer  escape_err 
)
static

Definition at line 297 of file test_escape.c.

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}
size_t PQescapeStringConn(PGconn *conn, char *to, const char *from, size_t length, int *error)
Definition fe-exec.c:4194
static void error(void)

References appendPQExpBufferChar(), appendPQExpBufferStr(), conn, PQExpBufferData::data, enlargePQExpBuffer(), error(), fb(), PQExpBufferData::len, PQerrorMessage(), and PQescapeStringConn().

◆ escapify()

static void escapify ( PQExpBuffer  buf,
const char str,
size_t  len 
)
static

Definition at line 100 of file test_escape.c.

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}
const char * str
const void size_t len
static char buf[DEFAULT_XLOG_SEG_SIZE]
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)

References appendPQExpBuffer(), appendPQExpBufferChar(), appendPQExpBufferStr(), buf, fb(), i, len, and str.

Referenced by test_gb18030_json(), test_gb18030_page_multiple(), test_one_vector_escape(), and test_psql_parse().

◆ main()

int main ( int  argc,
char argv[] 
)

Definition at line 902 of file test_escape.c.

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 lengthof(array)
Definition c.h:873
#define fprintf(file, fmt, msg)
Definition cubescan.l:21
PGconn * PQconnectdb(const char *conninfo)
Definition fe-connect.c:830
ConnStatusType PQstatus(const PGconn *conn)
void PQfinish(PGconn *conn)
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
@ CONNECTION_OK
Definition libpq-fe.h:90
static void usage(void)
PGDLLIMPORT int optind
Definition getopt.c:47
PGDLLIMPORT char * optarg
Definition getopt.c:49
#define printf(...)
Definition port.h:266
static void test_gb18030_json(pe_test_config *tc)
static void test_one_vector(pe_test_config *tc, const pe_test_vector *tv)
static pe_test_vector pe_test_vectors[]
static void test_gb18030_page_multiple(pe_test_config *tc)

References CONNECTION_OK, fb(), fprintf, getopt_long(), i, lengthof, no_argument, optarg, optind, pe_test_vectors, PQconnectdb(), PQerrorMessage(), PQfinish(), PQstatus(), printf, required_argument, test_gb18030_json(), test_gb18030_page_multiple(), test_one_vector(), and usage().

◆ report_result()

static void report_result ( pe_test_config tc,
bool  success,
const char testname,
const char details,
const char subname,
const char resultdesc 
)
static

Definition at line 119 of file test_escape.c.

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}
static bool success
Definition initdb.c:188
NameData subname

References fb(), printf, subname, and success.

Referenced by test_gb18030_json(), test_gb18030_page_multiple(), test_one_vector_escape(), and test_psql_parse().

◆ scan_res_s()

static const char * scan_res_s ( PsqlScanResult  res)
static

Definition at line 553 of file test_escape.c.

554{
555#define TOSTR_CASE(sym) case sym: return #sym
556
557 switch (res)
558 {
563 }
564
566 return ""; /* silence compiler */
567}
#define pg_unreachable()
Definition c.h:367
@ PSCAN_BACKSLASH
Definition psqlscan.h:33
@ PSCAN_EOL
Definition psqlscan.h:35
@ PSCAN_INCOMPLETE
Definition psqlscan.h:34
@ PSCAN_SEMICOLON
Definition psqlscan.h:32
#define TOSTR_CASE(sym)

References pg_unreachable, PSCAN_BACKSLASH, PSCAN_EOL, PSCAN_INCOMPLETE, PSCAN_SEMICOLON, and TOSTR_CASE.

Referenced by test_psql_parse().

◆ test_gb18030_json()

static void test_gb18030_json ( pe_test_config tc)
static

Definition at line 213 of file test_escape.c.

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}
FILE * input
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
#define VALGRIND_MAKE_MEM_NOACCESS(addr, size)
Definition memdebug.h:27
@ PG_GB18030
Definition pg_wchar.h:118
PQExpBuffer createPQExpBuffer(void)
Definition pqexpbuffer.c:72
void appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)
void destroyPQExpBuffer(PQExpBuffer str)
static void escapify(PQExpBuffer buf, const char *str, size_t len)
static void report_result(pe_test_config *tc, bool success, const char *testname, const char *details, const char *subname, const char *resultdesc)
#define NEVER_ACCESS_STR
Definition test_escape.c:35
static JsonSemAction sem

References appendBinaryPQExpBuffer(), appendPQExpBuffer(), appendPQExpBufferStr(), createPQExpBuffer(), PQExpBufferData::data, destroyPQExpBuffer(), escapify(), fb(), freeJsonLexContext(), input, json_errdetail(), JSON_UNICODE_ESCAPE_FORMAT, PQExpBufferData::len, makeJsonLexContextCstringLen(), NEVER_ACCESS_STR, PG_GB18030, pg_parse_json(), report_result(), sem, and VALGRIND_MAKE_MEM_NOACCESS.

Referenced by main().

◆ test_gb18030_page_multiple()

static void test_gb18030_page_multiple ( pe_test_config tc)
static

Definition at line 180 of file test_escape.c.

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}
int PQsetClientEncoding(PGconn *conn, const char *encoding)
void * pg_malloc(size_t size)
Definition fe_memutils.c:47
void pg_free(void *ptr)

References appendPQExpBuffer(), createPQExpBuffer(), destroyPQExpBuffer(), escapify(), fb(), input, pg_free(), pg_malloc(), PQescapeLiteral(), PQsetClientEncoding(), and report_result().

Referenced by main().

◆ test_one_vector()

static void test_one_vector ( pe_test_config tc,
const pe_test_vector tv 
)
static

Definition at line 861 of file test_escape.c.

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}
const char * client_encoding
Definition test_escape.c:78
static pe_test_escape_func pe_test_escape_funcs[]
static void test_one_vector_escape(pe_test_config *tc, const pe_test_vector *tv, const pe_test_escape_func *ef)

References pe_test_vector::client_encoding, fb(), fprintf, lengthof, pe_test_escape_funcs, PQerrorMessage(), PQsetClientEncoding(), and test_one_vector_escape().

Referenced by main().

◆ test_one_vector_escape()

static void test_one_vector_escape ( pe_test_config tc,
const pe_test_vector tv,
const pe_test_escape_func ef 
)
static

Definition at line 632 of file test_escape.c.

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",
739 escape_err->data);
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}
const char * escape
Definition test_escape.c:80
size_t escape_len
Definition test_escape.c:79
static void test_psql_parse(pe_test_config *tc, PQExpBuffer testname, PQExpBuffer input_buf, PQExpBuffer details)
static bool encoding_conflicts_ascii(int encoding)
int pg_encoding_verifymbstr(int encoding, const char *mbstr, int len)
Definition wchar.c:2001

References appendBinaryPQExpBuffer(), appendPQExpBuffer(), appendPQExpBufferChar(), appendPQExpBufferStr(), pe_test_vector::client_encoding, createPQExpBuffer(), PQExpBufferData::data, destroyPQExpBuffer(), encoding_conflicts_ascii(), pe_test_vector::escape, pe_test_vector::escape_len, escapify(), fb(), PQExpBufferData::len, NEVER_ACCESS_STR, pg_encoding_verifymbstr(), PQclientEncoding(), report_result(), test_psql_parse(), and VALGRIND_MAKE_MEM_NOACCESS.

Referenced by test_one_vector().

◆ test_psql_parse()

static void test_psql_parse ( pe_test_config tc,
PQExpBuffer  testname,
PQExpBuffer  input_buf,
PQExpBuffer  details 
)
static

Definition at line 575 of file test_escape.c.

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}
void resetPQExpBuffer(PQExpBuffer str)
PsqlScanResult
Definition psqlscan.h:31
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 const char * scan_res_s(PsqlScanResult res)
static const PsqlScanCallbacks test_scan_callbacks
Definition test_escape.c:87

References appendPQExpBuffer(), appendPQExpBufferChar(), createPQExpBuffer(), PQExpBufferData::data, destroyPQExpBuffer(), escapify(), fb(), PQExpBufferData::len, PQclientEncoding(), PROMPT_READY, PSCAN_EOL, PSCAN_INCOMPLETE, psql_scan(), psql_scan_create(), psql_scan_destroy(), psql_scan_setup(), report_result(), resetPQExpBuffer(), scan_res_s(), and test_scan_callbacks.

Referenced by test_one_vector_escape().

◆ usage()

static void usage ( const char hint)
static

Definition at line 879 of file test_escape.c.

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}

References fb(), fprintf, and printf.

Variable Documentation

◆ pe_test_escape_funcs

pe_test_escape_func pe_test_escape_funcs[]
static

Definition at line 396 of file test_escape.c.

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};
static bool escape_replace(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
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)
static bool escape_append_literal(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
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 bool escape_fmt_id(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)

Referenced by test_one_vector().

◆ pe_test_vectors

pe_test_vector pe_test_vectors[]
static

Definition at line 445 of file test_escape.c.

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};
#define TV_LEN(enc, string, len)
#define TV(enc, string)

Referenced by main().

◆ test_scan_callbacks

const PsqlScanCallbacks test_scan_callbacks
static
Initial value:
= {
}

Definition at line 87 of file test_escape.c.

87 {
88 NULL
89};

Referenced by test_psql_parse().