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 "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 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
 
#define NEVER_ACCESS_STR   "\xff never-to-be-touched"
 

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 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 void escapify (PQExpBuffer buf, const char *str, size_t len)
 
static void report_result (pe_test_config *tc, bool success, PQExpBuffer testname, PQExpBuffer details, const char *subname, const char *resultdesc)
 
static bool encoding_conflicts_ascii (int encoding)
 
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"

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

◆ TV_LEN

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

Definition at line 286 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 450 of file test_escape.c.

451{
452 /*
453 * We don't store this property directly anywhere, but whether an encoding
454 * is a client-only encoding is a good proxy.
455 */
457 return true;
458 return false;
459}
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 218 of file test_escape.c.

221{
222 appendStringLiteral(target, unescaped, PQclientEncoding(conn), 1);
223
224 return true;
225}
int PQclientEncoding(const PGconn *conn)
Definition: fe-connect.c:7643
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 228 of file test_escape.c.

231{
233 appendPQExpBufferStr(target, fmtId(unescaped));
234
235 return true;
236}
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 114 of file test_escape.c.

117{
118 char *escaped;
119
120 escaped = PQescapeIdentifier(conn, unescaped, unescaped_len);
121 if (!escaped)
122 {
123 appendPQExpBuffer(escape_err, "%s",
125 escape_err->data[escape_err->len - 1] = 0;
126 escape_err->len--;
127 return false;
128 }
129 else
130 {
131 appendPQExpBufferStr(target, escaped);
132 PQfreemem(escaped);
133 return true;
134 }
135}
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:7553
void PQfreemem(void *ptr)
Definition: fe-exec.c:4032
char * PQescapeIdentifier(PGconn *conn, const char *str, size_t len)
Definition: fe-exec.c:4369
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:265

References appendPQExpBuffer(), 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 90 of file test_escape.c.

93{
94 char *escaped;
95
96 escaped = PQescapeLiteral(conn, unescaped, unescaped_len);
97 if (!escaped)
98 {
99 appendPQExpBuffer(escape_err, "%s",
101 escape_err->data[escape_err->len - 1] = 0;
102 escape_err->len--;
103 return false;
104 }
105 else
106 {
107 appendPQExpBufferStr(target, escaped);
108 PQfreemem(escaped);
109 return true;
110 }
111}
char * PQescapeLiteral(PGconn *conn, const char *str, size_t len)
Definition: fe-exec.c:4363

References appendPQExpBuffer(), 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 192 of file test_escape.c.

195{
196 const char *s = unescaped;
197
198 appendPQExpBufferChar(target, '\'');
199
200 for (int i = 0; i < unescaped_len; i++)
201 {
202 char c = *s;
203
204 if (c == '\'')
205 {
206 appendPQExpBufferStr(target, "''");
207 }
208 else
209 appendPQExpBufferChar(target, c);
210 s++;
211 }
212 appendPQExpBufferChar(target, '\'');
213
214 return true;
215}
int i
Definition: isn.c:74
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 169 of file test_escape.c.

172{
173 size_t sz;
174
175 appendPQExpBufferChar(target, '\'');
176 enlargePQExpBuffer(target, unescaped_len * 2 + 1);
177 sz = PQescapeString(target->data + target->len,
178 unescaped, unescaped_len);
179 target->len += sz;
180 appendPQExpBufferChar(target, '\'');
181
182
183 return true;
184}
size_t PQescapeString(char *to, const char *from, size_t length)
Definition: fe-exec.c:4198
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 138 of file test_escape.c.

141{
142 int error;
143 size_t sz;
144
145 appendPQExpBufferChar(target, '\'');
146 enlargePQExpBuffer(target, unescaped_len * 2 + 1);
147 sz = PQescapeStringConn(conn, target->data + target->len,
148 unescaped, unescaped_len,
149 &error);
150
151 target->len += sz;
152 appendPQExpBufferChar(target, '\'');
153
154 if (error)
155 {
156 appendPQExpBuffer(escape_err, "%s",
158 escape_err->data[escape_err->len - 1] = 0;
159 escape_err->len--;
160 return false;
161 }
162 else
163 {
164 return true;
165 }
166}
size_t PQescapeStringConn(PGconn *conn, char *to, const char *from, size_t length, int *error)
Definition: fe-exec.c:4176
static void error(void)
Definition: sql-dyntest.c:147

References appendPQExpBuffer(), appendPQExpBufferChar(), 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 394 of file test_escape.c.

395{
396 for (size_t i = 0; i < len; i++)
397 {
398 char c = *str;
399
400 if (c == '\n')
402 else if (c == '\0')
404 else if (c < ' ' || c > '~')
405 appendPQExpBuffer(buf, "\\x%2x", (uint8_t) c);
406 else
408 str++;
409 }
410}
const char * str
const void size_t len
static char * buf
Definition: pg_test_fsync.c:72

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

Referenced by test_one_vector_escape(), and test_psql_parse().

◆ main()

int main ( int  argc,
char *  argv[] 
)

Definition at line 812 of file test_escape.c.

813{
814 pe_test_config tc = {0};
815 int c;
816 int option_index;
817
818 static const struct option long_options[] = {
819 {"help", no_argument, NULL, 'h'},
820 {"conninfo", required_argument, NULL, 'c'},
821 {"verbose", no_argument, NULL, 'v'},
822 {"quiet", no_argument, NULL, 'q'},
823 {"force-unsupported", no_argument, NULL, 'f'},
824 {NULL, 0, NULL, 0},
825 };
826
827 while ((c = getopt_long(argc, argv, "c:fhqv", long_options, &option_index)) != -1)
828 {
829 switch (c)
830 {
831 case 'h':
832 usage(NULL);
833 exit(0);
834 break;
835 case 'c':
836 tc.conninfo = optarg;
837 break;
838 case 'v':
839 tc.verbosity++;
840 break;
841 case 'q':
842 tc.verbosity--;
843 break;
844 case 'f':
845 tc.force_unsupported = true;
846 break;
847 }
848 }
849
850 if (argc - optind >= 1)
851 usage("unused option(s) specified");
852
853 if (tc.conninfo == NULL)
854 usage("--conninfo needs to be specified");
855
856 tc.conn = PQconnectdb(tc.conninfo);
857
858 if (!tc.conn || PQstatus(tc.conn) != CONNECTION_OK)
859 {
860 fprintf(stderr, "could not connect: %s\n",
861 PQerrorMessage(tc.conn));
862 exit(1);
863 }
864
865 for (int i = 0; i < lengthof(pe_test_vectors); i++)
866 {
868 }
869
870 PQfinish(tc.conn);
871
872 printf("# %d failures\n", tc.failure_count);
873 printf("1..%d\n", tc.test_count);
874 return tc.failure_count > 0;
875}
#define lengthof(array)
Definition: c.h:759
#define fprintf(file, fmt, msg)
Definition: cubescan.l:21
PGconn * PQconnectdb(const char *conninfo)
Definition: fe-connect.c:792
ConnStatusType PQstatus(const PGconn *conn)
Definition: fe-connect.c:7490
void PQfinish(PGconn *conn)
Definition: fe-connect.c:5224
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:26
PGconn * conn
Definition: test_escape.c:28
const char * conninfo
Definition: test_escape.c:27
static void test_one_vector(pe_test_config *tc, const pe_test_vector *tv)
Definition: test_escape.c:771
static pe_test_vector pe_test_vectors[]
Definition: test_escape.c:287
static void usage(const char *hint)
Definition: test_escape.c:789

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_one_vector(), usage(), and pe_test_config::verbosity.

◆ report_result()

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

Definition at line 413 of file test_escape.c.

419{
420 int test_id = ++tc->test_count;
421 bool print_details = true;
422 bool print_result = true;
423
424 if (success)
425 {
426 if (tc->verbosity <= 0)
427 print_details = false;
428 if (tc->verbosity < 0)
429 print_result = false;
430 }
431 else
432 tc->failure_count++;
433
434 if (print_details)
435 printf("%s", details->data);
436
437 if (print_result)
438 printf("%s %d - %s: %s: %s\n",
439 success ? "ok" : "not ok",
440 test_id, testname->data,
441 subname,
442 resultdesc);
443}
static bool success
Definition: initdb.c:187
NameData subname

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

Referenced by test_one_vector_escape(), and test_psql_parse().

◆ scan_res_s()

static const char * scan_res_s ( PsqlScanResult  res)
static

Definition at line 462 of file test_escape.c.

463{
464#define TOSTR_CASE(sym) case sym: return #sym
465
466 switch (res)
467 {
472 }
473
475 return ""; /* silence compiler */
476}
#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_one_vector()

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

Definition at line 771 of file test_escape.c.

772{
774 {
775 fprintf(stderr, "failed to set encoding to %s:\n%s\n",
777 exit(1);
778 }
779
780 for (int escoff = 0; escoff < lengthof(pe_test_escape_funcs); escoff++)
781 {
782 const pe_test_escape_func *ef = &pe_test_escape_funcs[escoff];
783
784 test_one_vector_escape(tc, tv, ef);
785 }
786}
int PQsetClientEncoding(PGconn *conn, const char *encoding)
Definition: fe-connect.c:7651
const char * client_encoding
Definition: test_escape.c:75
static pe_test_escape_func pe_test_escape_funcs[]
Definition: test_escape.c:238
static void test_one_vector_escape(pe_test_config *tc, const pe_test_vector *tv, const pe_test_escape_func *ef)
Definition: test_escape.c:541

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

542{
543 PQExpBuffer testname;
544 PQExpBuffer details;
545 PQExpBuffer raw_buf;
546 PQExpBuffer escape_buf;
547 PQExpBuffer escape_err;
548 size_t input_encoding_validlen;
549 bool input_encoding_valid;
550 size_t input_encoding0_validlen;
551 bool input_encoding0_valid;
552 bool escape_success;
553 size_t escape_encoding_length;
554 bool escape_encoding_valid;
555
556 escape_err = createPQExpBuffer();
557 testname = createPQExpBuffer();
558 details = createPQExpBuffer();
559 raw_buf = createPQExpBuffer();
560 escape_buf = createPQExpBuffer();
561
564 {
565 goto out;
566 }
567
568 /* name to describe the test */
569 appendPQExpBuffer(testname, ">");
570 escapify(testname, tv->escape, tv->escape_len);
571 appendPQExpBuffer(testname, "< - %s - %s",
572 tv->client_encoding, ef->name);
573
574 /* details to describe the test, to allow for debugging */
575 appendPQExpBuffer(details, "#\t input: %zd bytes: ",
576 tv->escape_len);
577 escapify(details, tv->escape, tv->escape_len);
578 appendPQExpBufferStr(details, "\n");
579 appendPQExpBuffer(details, "#\t encoding: %s\n",
580 tv->client_encoding);
581
582
583 /* check encoding of input, to compare with after the test */
584 input_encoding_validlen = pg_encoding_verifymbstr(PQclientEncoding(tc->conn),
585 tv->escape,
586 tv->escape_len);
587 input_encoding_valid = input_encoding_validlen == tv->escape_len;
588 appendPQExpBuffer(details, "#\t input encoding valid: %d\n",
589 input_encoding_valid);
590
591 input_encoding0_validlen = pg_encoding_verifymbstr(PQclientEncoding(tc->conn),
592 tv->escape,
593 strnlen(tv->escape, tv->escape_len));
594 input_encoding0_valid = input_encoding0_validlen == strnlen(tv->escape, tv->escape_len);
595 appendPQExpBuffer(details, "#\t input encoding valid till 0: %d\n",
596 input_encoding0_valid);
597
598 appendPQExpBuffer(details, "#\t escape func: %s\n",
599 ef->name);
600
601 if (!input_encoding_valid && ef->supports_only_valid
602 && !tc->force_unsupported)
603 goto out;
604
605
606 /*
607 * Put the to-be-escaped data into a buffer, so that we
608 *
609 * a) can mark memory beyond end of the string as inaccessible when using
610 * valgrind
611 *
612 * b) can append extra data beyond the length passed to the escape
613 * function, to verify that that data is not processed.
614 *
615 * TODO: Should we instead/additionally escape twice, once with unmodified
616 * and once with appended input? That way we could compare the two.
617 */
618 appendBinaryPQExpBuffer(raw_buf, tv->escape, tv->escape_len);
619
620#define NEVER_ACCESS_STR "\xff never-to-be-touched"
621 if (ef->supports_input_length)
622 {
623 /*
624 * Append likely invalid string that does *not* contain a null byte
625 * (which'd prevent some invalid accesses to later memory).
626 */
628
630 raw_buf->len - tv->escape_len);
631 }
632 else
633 {
634 /* append invalid string, after \0 */
635 appendPQExpBufferChar(raw_buf, 0);
637
638 VALGRIND_MAKE_MEM_NOACCESS(&raw_buf->data[tv->escape_len + 1],
639 raw_buf->len - tv->escape_len - 1);
640 }
641
642 /* call the to-be-tested escape function */
643 escape_success = ef->escape(tc->conn, escape_buf,
644 raw_buf->data, tv->escape_len,
645 escape_err);
646 if (!escape_success)
647 {
648 appendPQExpBuffer(details, "#\t escape error: %s\n",
649 escape_err->data);
650 }
651
652 if (escape_buf->len > 0)
653 {
654 bool contains_never;
655
656 appendPQExpBuffer(details, "#\t escaped string: %zd bytes: ", escape_buf->len);
657 escapify(details, escape_buf->data, escape_buf->len);
658 appendPQExpBufferChar(details, '\n');
659
660 escape_encoding_length = pg_encoding_verifymbstr(PQclientEncoding(tc->conn),
661 escape_buf->data,
662 escape_buf->len);
663 escape_encoding_valid = escape_encoding_length == escape_buf->len;
664
665 appendPQExpBuffer(details, "#\t escape encoding valid: %d\n",
666 escape_encoding_valid);
667
668 /*
669 * Verify that no data beyond the end of the input is included in the
670 * escaped string. It'd be better to use something like memmem()
671 * here, but that's not available everywhere.
672 */
673 contains_never = strstr(escape_buf->data, NEVER_ACCESS_STR) == NULL;
674 report_result(tc, contains_never, testname, details,
675 "escaped data beyond end of input",
676 contains_never ? "no" : "all secrets revealed");
677 }
678 else
679 {
680 escape_encoding_length = 0;
681 escape_encoding_valid = 1;
682 }
683
684 /*
685 * If the test reports errors, and the input was invalidly encoded,
686 * escaping should fail. One edge-case that we accept for now is that the
687 * input could have an embedded null byte, which the escape functions will
688 * just treat as a shorter string. If the encoding error is after the zero
689 * byte, the output thus won't contain it.
690 */
691 if (ef->reports_errors)
692 {
693 bool ok = true;
694 const char *resdesc = "ok";
695
696 if (escape_success)
697 {
698 if (!input_encoding0_valid)
699 {
700 ok = false;
701 resdesc = "invalid input escaped successfully";
702 }
703 else if (!input_encoding_valid)
704 resdesc = "invalid input escaped successfully, due to zero byte";
705 }
706 else
707 {
708 if (input_encoding0_valid)
709 {
710 ok = false;
711 resdesc = "valid input failed to escape";
712 }
713 else if (input_encoding_valid)
714 resdesc = "valid input failed to escape, due to zero byte";
715 }
716
717 report_result(tc, ok, testname, details,
718 "input validity vs escape success",
719 resdesc);
720 }
721
722 /*
723 * If the input is invalidly encoded, the output should also be invalidly
724 * encoded. We accept the same zero-byte edge case as above.
725 */
726 {
727 bool ok = true;
728 const char *resdesc = "ok";
729
730 if (input_encoding0_valid && !input_encoding_valid && escape_encoding_valid)
731 {
732 resdesc = "invalid input produced valid output, due to zero byte";
733 }
734 else if (input_encoding0_valid && !escape_encoding_valid)
735 {
736 ok = false;
737 resdesc = "valid input produced invalid output";
738 }
739 else if (!input_encoding0_valid &&
740 (!ef->reports_errors || escape_success) &&
741 escape_encoding_valid)
742 {
743 ok = false;
744 resdesc = "invalid input produced valid output";
745 }
746
747 report_result(tc, ok, testname, details,
748 "input and escaped encoding validity",
749 resdesc);
750 }
751
752 /*
753 * Test psql parsing whenever we get any string back, even if the escape
754 * function returned a failure.
755 */
756 if (escape_buf->len > 0)
757 {
758 test_psql_parse(tc, testname,
759 escape_buf, details);
760 }
761
762out:
763 destroyPQExpBuffer(escape_err);
764 destroyPQExpBuffer(details);
765 destroyPQExpBuffer(testname);
766 destroyPQExpBuffer(escape_buf);
767 destroyPQExpBuffer(raw_buf);
768}
#define VALGRIND_MAKE_MEM_NOACCESS(addr, size)
Definition: memdebug.h:27
size_t strnlen(const char *str, size_t maxlen)
Definition: strnlen.c:26
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
const char * name
Definition: test_escape.c:40
bool supports_only_ascii_overlap
Definition: test_escape.c:58
bool(* escape)(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)
Definition: test_escape.c:65
const char * escape
Definition: test_escape.c:77
size_t escape_len
Definition: test_escape.c:76
static void escapify(PQExpBuffer buf, const char *str, size_t len)
Definition: test_escape.c:394
static void test_psql_parse(pe_test_config *tc, PQExpBuffer testname, PQExpBuffer input_buf, PQExpBuffer details)
Definition: test_escape.c:484
#define NEVER_ACCESS_STR
static bool encoding_conflicts_ascii(int encoding)
Definition: test_escape.c:450
static void report_result(pe_test_config *tc, bool success, PQExpBuffer testname, PQExpBuffer details, const char *subname, const char *resultdesc)
Definition: test_escape.c:413
int pg_encoding_verifymbstr(int encoding, const char *mbstr, int len)
Definition: wchar.c:2163

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

486{
487 PsqlScanState scan_state;
488 PsqlScanResult scan_result;
489 PQExpBuffer query_buf;
490 promptStatus_t prompt_status = PROMPT_READY;
491 int matches = 0;
492 bool test_fails;
493 const char *resdesc;
494
495 query_buf = createPQExpBuffer();
496
498
499 /*
500 * TODO: This hardcodes standard conforming strings, it would be useful to
501 * test without as well.
502 */
503 psql_scan_setup(scan_state, input_buf->data, input_buf->len,
504 PQclientEncoding(tc->conn), 1);
505
506 do
507 {
508 resetPQExpBuffer(query_buf);
509
510 scan_result = psql_scan(scan_state, query_buf,
511 &prompt_status);
512
513 appendPQExpBuffer(details,
514 "#\t\t %d: scan_result: %s prompt: %u, query_buf: ",
515 matches, scan_res_s(scan_result), prompt_status);
516 escapify(details, query_buf->data, query_buf->len);
517 appendPQExpBuffer(details, "\n");
518
519 matches++;
520 }
521 while (scan_result != PSCAN_INCOMPLETE && scan_result != PSCAN_EOL);
522
523 psql_scan_destroy(scan_state);
524 destroyPQExpBuffer(query_buf);
525
526 test_fails = matches > 1 || scan_result != PSCAN_EOL;
527
528 if (matches > 1)
529 resdesc = "more than one match";
530 else if (scan_result != PSCAN_EOL)
531 resdesc = "unexpected end state";
532 else
533 resdesc = "ok";
534
535 report_result(tc, !test_fails, testname, details,
536 "psql parse",
537 resdesc);
538}
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:462
static const PsqlScanCallbacks test_scan_callbacks
Definition: test_escape.c:84

References appendPQExpBuffer(), 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 789 of file test_escape.c.

790{
791 if (hint)
792 fprintf(stderr, "Error: %s\n\n", hint);
793
794 printf("PostgreSQL escape function test\n"
795 "\n"
796 "Usage:\n"
797 " test_escape --conninfo=CONNINFO [OPTIONS]\n"
798 "\n"
799 "Options:\n"
800 " -h, --help show this help\n"
801 " -c, --conninfo=CONNINFO connection information to use\n"
802 " -v, --verbose show test details even for successes\n"
803 " -q, --quiet only show failures\n"
804 " -f, --force-unsupported test invalid input even if unsupported\n"
805 );
806
807 if (hint)
808 exit(1);
809}

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

Referenced by test_one_vector().

◆ pe_test_vectors

pe_test_vector pe_test_vectors[]
static

Definition at line 287 of file test_escape.c.

Referenced by main().

◆ test_scan_callbacks

const PsqlScanCallbacks test_scan_callbacks
static
Initial value:
= {
NULL
}

Definition at line 84 of file test_escape.c.

Referenced by test_psql_parse().