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:275

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 904 of file test_escape.c.

905{
906 pe_test_config tc = {0};
907 int c;
908 int option_index;
909
910 static const struct option long_options[] = {
911 {"help", no_argument, NULL, 'h'},
912 {"conninfo", required_argument, NULL, 'c'},
913 {"verbose", no_argument, NULL, 'v'},
914 {"quiet", no_argument, NULL, 'q'},
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':
924 usage(NULL);
925 exit(0);
926 break;
927 case 'c':
928 tc.conninfo = optarg;
929 break;
930 case 'v':
931 tc.verbosity++;
932 break;
933 case 'q':
934 tc.verbosity--;
935 break;
936 case 'f':
937 tc.force_unsupported = true;
938 break;
939 }
940 }
941
942 if (argc - optind >= 1)
943 usage("unused option(s) specified");
944
945 if (tc.conninfo == NULL)
946 usage("--conninfo needs to be specified");
947
948 tc.conn = PQconnectdb(tc.conninfo);
949
950 if (!tc.conn || PQstatus(tc.conn) != CONNECTION_OK)
951 {
952 fprintf(stderr, "could not connect: %s\n",
953 PQerrorMessage(tc.conn));
954 exit(1);
955 }
956
959
960 for (int i = 0; i < lengthof(pe_test_vectors); i++)
961 {
963 }
964
965 PQfinish(tc.conn);
966
967 printf("# %d failures\n", tc.failure_count);
968 printf("1..%d\n", tc.test_count);
969 return tc.failure_count > 0;
970}
#define lengthof(array)
Definition c.h:803
#define fprintf(file, fmt, msg)
Definition cubescan.l:21
PGconn * PQconnectdb(const char *conninfo)
Definition fe-connect.c:825
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:84
static void usage(void)
PGDLLIMPORT int optind
Definition getopt.c:51
PGDLLIMPORT char * optarg
Definition getopt.c:53
#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:187
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 555 of file test_escape.c.

556{
557#define TOSTR_CASE(sym) case sym: return #sym
558
559 switch (res)
560 {
565 }
566
568 return ""; /* silence compiler */
569}
#define pg_unreachable()
Definition c.h:341
@ 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:268
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 863 of file test_escape.c.

864{
865 if (PQsetClientEncoding(tc->conn, tv->client_encoding))
866 {
867 fprintf(stderr, "failed to set encoding to %s:\n%s\n",
868 tv->client_encoding, PQerrorMessage(tc->conn));
869 exit(1);
870 }
871
873 {
875
877 }
878}
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 634 of file test_escape.c.

635{
637 PQExpBuffer details;
638 PQExpBuffer raw_buf;
645 bool escape_success;
648
651 details = createPQExpBuffer();
652 raw_buf = createPQExpBuffer();
654
655 if (ef->supports_only_ascii_overlap &&
657 {
658 goto out;
659 }
660
661 /* name to describe the test */
664 appendPQExpBuffer(testname, "< - %s - %s",
665 tv->client_encoding, ef->name);
666
667 /* details to describe the test, to allow for debugging */
668 appendPQExpBuffer(details, "#\t input: %zd bytes: ",
669 tv->escape_len);
670 escapify(details, tv->escape, tv->escape_len);
671 appendPQExpBufferChar(details, '\n');
672 appendPQExpBuffer(details, "#\t encoding: %s\n",
673 tv->client_encoding);
674
675
676 /* check encoding of input, to compare with after the test */
678 tv->escape,
679 tv->escape_len);
681 appendPQExpBuffer(details, "#\t input encoding valid: %d\n",
683
685 tv->escape,
686 strnlen(tv->escape, tv->escape_len));
688 appendPQExpBuffer(details, "#\t input encoding valid till 0: %d\n",
690
691 appendPQExpBuffer(details, "#\t escape func: %s\n",
692 ef->name);
693
694 if (!input_encoding_valid && ef->supports_only_valid
695 && !tc->force_unsupported)
696 goto out;
697
698
699 /*
700 * Put the to-be-escaped data into a buffer, so that we
701 *
702 * a) can mark memory beyond end of the string as inaccessible when using
703 * valgrind
704 *
705 * b) can append extra data beyond the length passed to the escape
706 * function, to verify that that data is not processed.
707 *
708 * TODO: Should we instead/additionally escape twice, once with unmodified
709 * and once with appended input? That way we could compare the two.
710 */
711 appendBinaryPQExpBuffer(raw_buf, tv->escape, tv->escape_len);
712
713 if (ef->supports_input_length)
714 {
715 /*
716 * Append likely invalid string that does *not* contain a null byte
717 * (which'd prevent some invalid accesses to later memory).
718 */
720
722 raw_buf->len - tv->escape_len);
723 }
724 else
725 {
726 /* append invalid string, after \0 */
727 appendPQExpBufferChar(raw_buf, 0);
729
730 VALGRIND_MAKE_MEM_NOACCESS(&raw_buf->data[tv->escape_len + 1],
731 raw_buf->len - tv->escape_len - 1);
732 }
733
734 /* call the to-be-tested escape function */
735 escape_success = ef->escape(tc->conn, escape_buf,
736 raw_buf->data, tv->escape_len,
737 escape_err);
738 if (!escape_success)
739 {
740 appendPQExpBuffer(details, "#\t escape error: %s\n",
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);
749 escapify(details, escape_buf->data, escape_buf->len);
750 appendPQExpBufferChar(details, '\n');
751
753 escape_buf->data,
754 escape_buf->len);
756
757 appendPQExpBuffer(details, "#\t escape encoding valid: %d\n",
759
760 /*
761 * Verify that no data beyond the end of the input is included in the
762 * escaped string. It'd be better to use something like memmem()
763 * here, but that's not available everywhere.
764 */
766 report_result(tc, contains_never, testname->data, details->data,
767 "escaped data beyond end of input",
768 contains_never ? "no" : "all secrets revealed");
769 }
770 else
771 {
774 }
775
776 /*
777 * If the test reports errors, and the input was invalidly encoded,
778 * escaping should fail. One edge-case that we accept for now is that the
779 * input could have an embedded null byte, which the escape functions will
780 * just treat as a shorter string. If the encoding error is after the zero
781 * byte, the output thus won't contain it.
782 */
783 if (ef->reports_errors)
784 {
785 bool ok = true;
786 const char *resdesc = "ok";
787
788 if (escape_success)
789 {
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 {
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
809 report_result(tc, ok, testname->data, details->data,
810 "input validity vs escape success",
811 resdesc);
812 }
813
814 /*
815 * If the input is invalidly encoded, the output should also be invalidly
816 * encoded. We accept the same zero-byte edge case as above.
817 */
818 {
819 bool ok = true;
820 const char *resdesc = "ok";
821
823 {
824 resdesc = "invalid input produced valid output, due to zero byte";
825 }
827 {
828 ok = false;
829 resdesc = "valid input produced invalid output";
830 }
831 else if (!input_encoding0_valid &&
832 (!ef->reports_errors || escape_success) &&
834 {
835 ok = false;
836 resdesc = "invalid input produced valid output";
837 }
838
839 report_result(tc, ok, testname->data, details->data,
840 "input and escaped encoding validity",
841 resdesc);
842 }
843
844 /*
845 * Test psql parsing whenever we get any string back, even if the escape
846 * function returned a failure.
847 */
848 if (escape_buf->len > 0)
849 {
851 escape_buf, details);
852 }
853
854out:
856 destroyPQExpBuffer(details);
859 destroyPQExpBuffer(raw_buf);
860}
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:2202

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 577 of file test_escape.c.

579{
584 int matches = 0;
585 bool test_fails;
586 const char *resdesc;
587
589
591
592 /*
593 * TODO: This hardcodes standard conforming strings, it would be useful to
594 * test without as well.
595 */
596 psql_scan_setup(scan_state, input_buf->data, input_buf->len,
597 PQclientEncoding(tc->conn), 1);
598
599 do
600 {
602
605
606 appendPQExpBuffer(details,
607 "#\t\t %d: scan_result: %s prompt: %u, query_buf: ",
609 escapify(details, query_buf->data, query_buf->len);
610 appendPQExpBufferChar(details, '\n');
611
612 matches++;
613 }
615
618
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
628 report_result(tc, !test_fails, testname->data, details->data,
629 "psql parse",
630 resdesc);
631}
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: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 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 881 of file test_escape.c.

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}

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("mule_internal", "\\\x9c';\0;"),
530
531 TV("sql_ascii", "1\xC0'"),
532
533 /*
534 * Testcases that are not null terminated for the specified input length.
535 * That's interesting to verify that escape functions don't read beyond
536 * the intended input length.
537 *
538 * One interesting special case is GB18030, which has the odd behaviour
539 * needing to read beyond the first byte to determine the length of a
540 * multi-byte character.
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};
#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().