PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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 char * scan_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}
int32 encoding
Definition: pg_database.h:41
#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{
380 appendStringLiteral(target, unescaped, PQclientEncoding(conn), 1);
381
382 return true;
383}
int PQclientEncoding(const PGconn *conn)
Definition: fe-connect.c:7715
PGconn * conn
Definition: streamutil.c:52
void appendStringLiteral(PQExpBuffer buf, const char *str, int encoding, bool std_strings)
Definition: string_utils.c:351

References appendStringLiteral(), conn, 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{
391 appendPQExpBufferStr(target, fmtId(unescaped));
392
393 return true;
394}
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:367
const char * fmtId(const char *rawid)
Definition: string_utils.c:248
void setFmtEncoding(int encoding)
Definition: string_utils.c:69

References appendPQExpBufferStr(), conn, 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
280 escaped = PQescapeIdentifier(conn, unescaped, unescaped_len);
281 if (!escaped)
282 {
284 escape_err->data[escape_err->len - 1] = 0;
285 escape_err->len--;
286 return false;
287 }
288 else
289 {
290 appendPQExpBufferStr(target, escaped);
291 PQfreemem(escaped);
292 return true;
293 }
294}
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:7625
void PQfreemem(void *ptr)
Definition: fe-exec.c:4032
char * PQescapeIdentifier(PGconn *conn, const char *str, size_t len)
Definition: fe-exec.c:4371

References appendPQExpBufferStr(), conn, PQExpBufferData::data, PQExpBufferData::len, 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
257 escaped = PQescapeLiteral(conn, unescaped, unescaped_len);
258 if (!escaped)
259 {
261 escape_err->data[escape_err->len - 1] = 0;
262 escape_err->len--;
263 return false;
264 }
265 else
266 {
267 appendPQExpBufferStr(target, escaped);
268 PQfreemem(escaped);
269 return true;
270 }
271}
char * PQescapeLiteral(PGconn *conn, const char *str, size_t len)
Definition: fe-exec.c:4365

References appendPQExpBufferStr(), conn, PQExpBufferData::data, PQExpBufferData::len, 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)
Definition: pqexpbuffer.c:378
char * c

References appendPQExpBufferChar(), appendPQExpBufferStr(), 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,
336 unescaped, unescaped_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:4199
int enlargePQExpBuffer(PQExpBuffer str, size_t needed)
Definition: pqexpbuffer.c:172

References appendPQExpBufferChar(), PQExpBufferData::data, enlargePQExpBuffer(), 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,
307 unescaped, unescaped_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:4177
static void error(void)
Definition: sql-dyntest.c:147

References appendPQExpBufferChar(), appendPQExpBufferStr(), conn, PQExpBufferData::data, enlargePQExpBuffer(), error(), 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
Definition: pg_test_fsync.c:72
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:265

References appendPQExpBuffer(), appendPQExpBufferChar(), appendPQExpBufferStr(), buf, 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:759
#define fprintf(file, fmt, msg)
Definition: cubescan.l:21
PGconn * PQconnectdb(const char *conninfo)
Definition: fe-connect.c:819
ConnStatusType PQstatus(const PGconn *conn)
Definition: fe-connect.c:7562
void PQfinish(PGconn *conn)
Definition: fe-connect.c:5296
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
PGDLLIMPORT int optind
Definition: getopt.c:51
PGDLLIMPORT char * optarg
Definition: getopt.c:53
#define printf(...)
Definition: port.h:245
bool force_unsupported
Definition: test_escape.c:27
PGconn * conn
Definition: test_escape.c:29
const char * conninfo
Definition: test_escape.c:28
static void test_gb18030_json(pe_test_config *tc)
Definition: test_escape.c:213
static void test_one_vector(pe_test_config *tc, const pe_test_vector *tv)
Definition: test_escape.c:863
static pe_test_vector pe_test_vectors[]
Definition: test_escape.c:445
static void usage(const char *hint)
Definition: test_escape.c:881
static void test_gb18030_page_multiple(pe_test_config *tc)
Definition: test_escape.c:180

References pe_test_config::conn, CONNECTION_OK, pe_test_config::conninfo, pe_test_config::failure_count, pe_test_config::force_unsupported, fprintf, getopt_long(), i, lengthof, no_argument, optarg, optind, pe_test_vectors, PQconnectdb(), PQerrorMessage(), PQfinish(), PQstatus(), printf, required_argument, pe_test_config::test_count, test_gb18030_json(), test_gb18030_page_multiple(), test_one_vector(), usage(), and pe_test_config::verbosity.

◆ 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",
146 test_id, testname,
147 subname,
148 resultdesc);
149}
static bool success
Definition: initdb.c:187
NameData subname

References pe_test_config::failure_count, printf, subname, success, pe_test_config::test_count, and pe_test_config::verbosity.

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:332
@ 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;
216 PQExpBuffer testname;
217 const char input[] = "{\"\\u\xFE";
218 size_t input_len = sizeof(input) - 1;
219 JsonLexContext *lex;
220 JsonSemAction sem = {0}; /* no callbacks */
221 JsonParseErrorType json_error;
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 */
231 testname = createPQExpBuffer();
232 appendPQExpBuffer(testname, ">");
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);
239 json_error = pg_parse_json(lex, &sem);
241 testname->data, "",
242 "diagnosed", json_errdetail(json_error, lex));
243
245 destroyPQExpBuffer(testname);
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)
Definition: pqexpbuffer.c:397
void destroyPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:114
static void escapify(PQExpBuffer buf, const char *str, size_t len)
Definition: test_escape.c:100
static void report_result(pe_test_config *tc, bool success, const char *testname, const char *details, const char *subname, const char *resultdesc)
Definition: test_escape.c:119
#define NEVER_ACCESS_STR
Definition: test_escape.c:35
static JsonSemAction sem

References appendBinaryPQExpBuffer(), appendPQExpBuffer(), appendPQExpBufferStr(), createPQExpBuffer(), PQExpBufferData::data, destroyPQExpBuffer(), escapify(), 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{
182 PQExpBuffer testname;
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 */
192 testname = createPQExpBuffer();
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
203 destroyPQExpBuffer(testname);
204 pg_free(input);
205}
int PQsetClientEncoding(PGconn *conn, const char *encoding)
Definition: fe-connect.c:7723
void * pg_malloc(size_t size)
Definition: fe_memutils.c:47
void pg_free(void *ptr)
Definition: fe_memutils.c:105

References appendPQExpBuffer(), pe_test_config::conn, createPQExpBuffer(), PQExpBufferData::data, destroyPQExpBuffer(), escapify(), 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{
866 {
867 fprintf(stderr, "failed to set encoding to %s:\n%s\n",
869 exit(1);
870 }
871
872 for (int escoff = 0; escoff < lengthof(pe_test_escape_funcs); escoff++)
873 {
874 const pe_test_escape_func *ef = &pe_test_escape_funcs[escoff];
875
876 test_one_vector_escape(tc, tv, ef);
877 }
878}
const char * client_encoding
Definition: test_escape.c:78
static pe_test_escape_func pe_test_escape_funcs[]
Definition: test_escape.c:396
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:634

References pe_test_vector::client_encoding, pe_test_config::conn, 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{
636 PQExpBuffer testname;
637 PQExpBuffer details;
638 PQExpBuffer raw_buf;
639 PQExpBuffer escape_buf;
640 PQExpBuffer escape_err;
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
649 escape_err = createPQExpBuffer();
650 testname = createPQExpBuffer();
651 details = createPQExpBuffer();
652 raw_buf = createPQExpBuffer();
653 escape_buf = createPQExpBuffer();
654
657 {
658 goto out;
659 }
660
661 /* name to describe the test */
662 appendPQExpBufferChar(testname, '>');
663 escapify(testname, tv->escape, tv->escape_len);
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 */
677 input_encoding_validlen = pg_encoding_verifymbstr(PQclientEncoding(tc->conn),
678 tv->escape,
679 tv->escape_len);
680 input_encoding_valid = input_encoding_validlen == tv->escape_len;
681 appendPQExpBuffer(details, "#\t input encoding valid: %d\n",
682 input_encoding_valid);
683
684 input_encoding0_validlen = pg_encoding_verifymbstr(PQclientEncoding(tc->conn),
685 tv->escape,
686 strnlen(tv->escape, tv->escape_len));
687 input_encoding0_valid = input_encoding0_validlen == strnlen(tv->escape, tv->escape_len);
688 appendPQExpBuffer(details, "#\t input encoding valid till 0: %d\n",
689 input_encoding0_valid);
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
752 escape_encoding_length = pg_encoding_verifymbstr(PQclientEncoding(tc->conn),
753 escape_buf->data,
754 escape_buf->len);
755 escape_encoding_valid = escape_encoding_length == escape_buf->len;
756
757 appendPQExpBuffer(details, "#\t escape encoding valid: %d\n",
758 escape_encoding_valid);
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 */
765 contains_never = strstr(escape_buf->data, NEVER_ACCESS_STR) == NULL;
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 {
772 escape_encoding_length = 0;
773 escape_encoding_valid = 1;
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 {
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
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
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 &&
832 (!ef->reports_errors || escape_success) &&
833 escape_encoding_valid)
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 {
850 test_psql_parse(tc, testname,
851 escape_buf, details);
852 }
853
854out:
855 destroyPQExpBuffer(escape_err);
856 destroyPQExpBuffer(details);
857 destroyPQExpBuffer(testname);
858 destroyPQExpBuffer(escape_buf);
859 destroyPQExpBuffer(raw_buf);
860}
size_t strnlen(const char *str, size_t maxlen)
Definition: strnlen.c:26
const char * name
Definition: test_escape.c:43
bool supports_only_ascii_overlap
Definition: test_escape.c:61
bool(* escape)(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
Definition: test_escape.c:68
const char * escape
Definition: test_escape.c:80
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)
Definition: test_escape.c:577
static bool encoding_conflicts_ascii(int encoding)
Definition: test_escape.c:156
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, pe_test_config::conn, createPQExpBuffer(), PQExpBufferData::data, destroyPQExpBuffer(), encoding_conflicts_ascii(), pe_test_escape_func::escape, pe_test_vector::escape, pe_test_vector::escape_len, escapify(), pe_test_config::force_unsupported, PQExpBufferData::len, pe_test_escape_func::name, NEVER_ACCESS_STR, pg_encoding_verifymbstr(), PQclientEncoding(), report_result(), pe_test_escape_func::reports_errors, strnlen(), pe_test_escape_func::supports_input_length, pe_test_escape_func::supports_only_ascii_overlap, pe_test_escape_func::supports_only_valid, 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{
580 PsqlScanState scan_state;
581 PsqlScanResult scan_result;
582 PQExpBuffer query_buf;
583 promptStatus_t prompt_status = PROMPT_READY;
584 int matches = 0;
585 bool test_fails;
586 const char *resdesc;
587
588 query_buf = createPQExpBuffer();
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 {
601 resetPQExpBuffer(query_buf);
602
603 scan_result = psql_scan(scan_state, query_buf,
604 &prompt_status);
605
606 appendPQExpBuffer(details,
607 "#\t\t %d: scan_result: %s prompt: %u, query_buf: ",
608 matches, scan_res_s(scan_result), prompt_status);
609 escapify(details, query_buf->data, query_buf->len);
610 appendPQExpBufferChar(details, '\n');
611
612 matches++;
613 }
614 while (scan_result != PSCAN_INCOMPLETE && scan_result != PSCAN_EOL);
615
616 psql_scan_destroy(scan_state);
617 destroyPQExpBuffer(query_buf);
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
628 report_result(tc, !test_fails, testname->data, details->data,
629 "psql parse",
630 resdesc);
631}
void resetPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:146
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)
Definition: test_escape.c:555
static const PsqlScanCallbacks test_scan_callbacks
Definition: test_escape.c:87

References appendPQExpBuffer(), appendPQExpBufferChar(), pe_test_config::conn, createPQExpBuffer(), PQExpBufferData::data, destroyPQExpBuffer(), escapify(), 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 fprintf, and printf.

Referenced by main().

Variable Documentation

◆ pe_test_escape_funcs

pe_test_escape_func pe_test_escape_funcs[]
static

Definition at line 396 of file test_escape.c.

Referenced by test_one_vector().

◆ pe_test_vectors

pe_test_vector pe_test_vectors[]
static

Definition at line 445 of file test_escape.c.

Referenced by main().

◆ test_scan_callbacks

const PsqlScanCallbacks test_scan_callbacks
static
Initial value:
= {
NULL
}

Definition at line 87 of file test_escape.c.

Referenced by test_psql_parse().