Skip to content

Commit 502d8d4

Browse files
committed
Add function to normalize only utility statements
1 parent fdbe27d commit 502d8d4

File tree

6 files changed

+93
-2
lines changed

6 files changed

+93
-2
lines changed

Diff for: Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ benchmark:
1616

1717
# --- Below only needed for releasing new versions
1818

19-
LIB_PG_QUERY_TAG = 16-5.1.0
19+
LIB_PG_QUERY_TAG = c492b8f6fa54811fb194b6964055bb7d480f8c91
2020

2121
root_dir := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
2222
LIB_TMPDIR = $(root_dir)/tmp

Diff for: normalize_test.go

+26
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,29 @@ func TestNormalizeError(t *testing.T) {
6868
}
6969
}
7070
}
71+
72+
var normalizeUtilityTests = []struct {
73+
input string
74+
expected string
75+
}{
76+
{
77+
"SELECT 1",
78+
"SELECT 1",
79+
},
80+
{
81+
"CREATE ROLE postgres PASSWORD 'xyz'",
82+
"CREATE ROLE postgres PASSWORD $1",
83+
},
84+
}
85+
86+
func TestNormalizeUtility(t *testing.T) {
87+
for _, test := range normalizeUtilityTests {
88+
actual, err := pg_query.NormalizeUtility(test.input)
89+
90+
if err != nil {
91+
t.Errorf("Normalize(%s)\nerror %s\n\n", test.input, err)
92+
} else if !reflect.DeepEqual(actual, test.expected) {
93+
t.Errorf("Normalize(%s)\nexpected %s\nactual %s\n\n", test.input, test.expected, actual)
94+
}
95+
}
96+
}

Diff for: parser/include/pg_query.h

+1
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ extern "C" {
9696
#endif
9797

9898
PgQueryNormalizeResult pg_query_normalize(const char* input);
99+
PgQueryNormalizeResult pg_query_normalize_utility(const char* input);
99100
PgQueryScanResult pg_query_scan(const char* input);
100101
PgQueryParseResult pg_query_parse(const char* input);
101102
PgQueryParseResult pg_query_parse_opts(const char* input, int parser_options);

Diff for: parser/parser.go

+18
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,24 @@ func Normalize(input string) (result string, err error) {
175175
return
176176
}
177177

178+
// Normalize the passed utility statement to replace constant values with ? characters
179+
func NormalizeUtility(input string) (result string, err error) {
180+
inputC := C.CString(input)
181+
defer C.free(unsafe.Pointer(inputC))
182+
183+
resultC := C.pg_query_normalize_utility(inputC)
184+
defer C.pg_query_free_normalize_result(resultC)
185+
186+
if resultC.error != nil {
187+
err = newPgQueryError(resultC.error)
188+
return
189+
}
190+
191+
result = C.GoString(resultC.normalized_query)
192+
193+
return
194+
}
195+
178196
func SplitWithScanner(input string, trimSpace bool) (result []string, err error) {
179197
inputC := C.CString(input)
180198
defer C.free(unsafe.Pointer(inputC))

Diff for: parser/pg_query_normalize.c

+42-1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ typedef struct pgssConstLocations
4848
int *param_refs;
4949
int param_refs_buf_size;
5050
int param_refs_count;
51+
52+
/* Should only utility statements be normalized? Set by pg_query_normalize_utility */
53+
bool normalize_utility_only;
5154
} pgssConstLocations;
5255

5356
/*
@@ -398,8 +401,10 @@ static bool const_record_walker(Node *node, pgssConstLocations *jstate)
398401
case T_RawStmt:
399402
return const_record_walker((Node *) ((RawStmt *) node)->stmt, jstate);
400403
case T_VariableSetStmt:
404+
if (jstate->normalize_utility_only) return false;
401405
return const_record_walker((Node *) ((VariableSetStmt *) node)->args, jstate);
402406
case T_CopyStmt:
407+
if (jstate->normalize_utility_only) return false;
403408
return const_record_walker((Node *) ((CopyStmt *) node)->query, jstate);
404409
case T_ExplainStmt:
405410
return const_record_walker((Node *) ((ExplainStmt *) node)->query, jstate);
@@ -408,10 +413,13 @@ static bool const_record_walker(Node *node, pgssConstLocations *jstate)
408413
case T_AlterRoleStmt:
409414
return const_record_walker((Node *) ((AlterRoleStmt *) node)->options, jstate);
410415
case T_DeclareCursorStmt:
416+
if (jstate->normalize_utility_only) return false;
411417
return const_record_walker((Node *) ((DeclareCursorStmt *) node)->query, jstate);
412418
case T_CreateFunctionStmt:
419+
if (jstate->normalize_utility_only) return false;
413420
return const_record_walker((Node *) ((CreateFunctionStmt *) node)->options, jstate);
414421
case T_DoStmt:
422+
if (jstate->normalize_utility_only) return false;
415423
return const_record_walker((Node *) ((DoStmt *) node)->args, jstate);
416424
case T_CreateSubscriptionStmt:
417425
record_matching_string(jstate, ((CreateSubscriptionStmt *) node)->conninfo);
@@ -428,6 +436,7 @@ static bool const_record_walker(Node *node, pgssConstLocations *jstate)
428436
return false;
429437
case T_SelectStmt:
430438
{
439+
if (jstate->normalize_utility_only) return false;
431440
SelectStmt *stmt = (SelectStmt *) node;
432441
ListCell *lc;
433442
List *fp_and_param_refs_list = NIL;
@@ -540,6 +549,26 @@ static bool const_record_walker(Node *node, pgssConstLocations *jstate)
540549

541550
return false;
542551
}
552+
case T_MergeStmt:
553+
{
554+
if (jstate->normalize_utility_only) return false;
555+
return raw_expression_tree_walker(node, const_record_walker, (void*) jstate);
556+
}
557+
case T_InsertStmt:
558+
{
559+
if (jstate->normalize_utility_only) return false;
560+
return raw_expression_tree_walker(node, const_record_walker, (void*) jstate);
561+
}
562+
case T_UpdateStmt:
563+
{
564+
if (jstate->normalize_utility_only) return false;
565+
return raw_expression_tree_walker(node, const_record_walker, (void*) jstate);
566+
}
567+
case T_DeleteStmt:
568+
{
569+
if (jstate->normalize_utility_only) return false;
570+
return raw_expression_tree_walker(node, const_record_walker, (void*) jstate);
571+
}
543572
default:
544573
{
545574
PG_TRY();
@@ -558,7 +587,7 @@ static bool const_record_walker(Node *node, pgssConstLocations *jstate)
558587
return false;
559588
}
560589

561-
PgQueryNormalizeResult pg_query_normalize(const char* input)
590+
PgQueryNormalizeResult pg_query_normalize_ext(const char* input, bool normalize_utility_only)
562591
{
563592
MemoryContext ctx = NULL;
564593
PgQueryNormalizeResult result = {0};
@@ -588,6 +617,7 @@ PgQueryNormalizeResult pg_query_normalize(const char* input)
588617
jstate.param_refs = NULL;
589618
jstate.param_refs_buf_size = 0;
590619
jstate.param_refs_count = 0;
620+
jstate.normalize_utility_only = normalize_utility_only;
591621

592622
/* Walk tree and record const locations */
593623
const_record_walker((Node *) tree, &jstate);
@@ -621,6 +651,17 @@ PgQueryNormalizeResult pg_query_normalize(const char* input)
621651
return result;
622652
}
623653

654+
PgQueryNormalizeResult pg_query_normalize(const char* input)
655+
{
656+
return pg_query_normalize_ext(input, false);
657+
}
658+
659+
660+
PgQueryNormalizeResult pg_query_normalize_utility(const char* input)
661+
{
662+
return pg_query_normalize_ext(input, true);
663+
}
664+
624665
void pg_query_free_normalize_result(PgQueryNormalizeResult result)
625666
{
626667
if (result.error) {

Diff for: pg_query.go

+5
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ func Normalize(input string) (result string, err error) {
5757
return parser.Normalize(input)
5858
}
5959

60+
// Normalize the passed utility statement to replace constant values with $n parameter references
61+
func NormalizeUtility(input string) (result string, err error) {
62+
return parser.NormalizeUtility(input)
63+
}
64+
6065
// Fingerprint - Fingerprint the passed SQL statement to a hex string
6166
func Fingerprint(input string) (result string, err error) {
6267
return parser.FingerprintToHexStr(input)

0 commit comments

Comments
 (0)