Skip to content

Commit 1915589

Browse files
authored
Add pg_query_is_utility_stmt() (#313)
1 parent 693209e commit 1915589

File tree

5 files changed

+217
-2
lines changed

5 files changed

+217
-2
lines changed

Makefile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,14 +237,15 @@ examples/normalize_error: examples/normalize_error.c $(ARLIB)
237237
examples/simple_plpgsql: examples/simple_plpgsql.c $(ARLIB)
238238
$(CC) $(TEST_CFLAGS) -o $@ -g examples/simple_plpgsql.c $(ARLIB) $(TEST_LDFLAGS)
239239

240-
TESTS = test/complex test/concurrency test/deparse test/fingerprint test/fingerprint_opts test/normalize test/normalize_utility test/parse test/parse_opts test/parse_protobuf test/parse_protobuf_opts test/parse_plpgsql test/scan test/split test/summary test/summary_truncate
240+
TESTS = test/complex test/concurrency test/deparse test/fingerprint test/fingerprint_opts test/is_utility_stmt test/normalize test/normalize_utility test/parse test/parse_opts test/parse_protobuf test/parse_protobuf_opts test/parse_plpgsql test/scan test/split test/summary test/summary_truncate
241241
test: $(TESTS)
242242
ifeq ($(VALGRIND),1)
243243
$(VALGRIND_MEMCHECK) test/complex || (cat test/valgrind.log && false)
244244
$(VALGRIND_MEMCHECK) test/concurrency || (cat test/valgrind.log && false)
245245
$(VALGRIND_MEMCHECK) test/deparse || (cat test/valgrind.log && false)
246246
$(VALGRIND_MEMCHECK) test/fingerprint || (cat test/valgrind.log && false)
247247
$(VALGRIND_MEMCHECK) test/fingerprint_opts || (cat test/valgrind.log && false)
248+
$(VALGRIND_MEMCHECK) test/is_utility_stmt || (cat test/valgrind.log && false)
248249
$(VALGRIND_MEMCHECK) test/normalize || (cat test/valgrind.log && false)
249250
$(VALGRIND_MEMCHECK) test/normalize_utility || (cat test/valgrind.log && false)
250251
$(VALGRIND_MEMCHECK) test/parse || (cat test/valgrind.log && false)
@@ -264,6 +265,7 @@ else
264265
test/deparse
265266
test/fingerprint
266267
test/fingerprint_opts
268+
test/is_utility_stmt
267269
test/normalize
268270
test/normalize_utility
269271
test/parse
@@ -297,6 +299,10 @@ test/fingerprint_opts: test/fingerprint_opts.c test/fingerprint_opts_tests.c $(A
297299
# We have "-Isrc/" because this test uses pg_query_fingerprint_with_opts
298300
$(CC) $(TEST_CFLAGS) -o $@ -Isrc/ test/fingerprint_opts.c $(ARLIB) $(TEST_LDFLAGS)
299301

302+
test/is_utility_stmt: test/framework/main.c test/is_utility_stmt.c $(ARLIB)
303+
# We have "-Isrc/postgres/include" because this test uses pg_query_summary_direct
304+
$(CC) $(TEST_CFLAGS) -o $@ -Isrc/postgres/include test/framework/main.c test/is_utility_stmt.c $(ARLIB) $(TEST_LDFLAGS)
305+
300306
test/normalize: test/normalize.c test/normalize_tests.c $(ARLIB)
301307
$(CC) $(TEST_CFLAGS) -o $@ test/normalize.c $(ARLIB) $(TEST_LDFLAGS)
302308

Makefile.msvc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,12 @@ examples/normalize_error: examples/normalize_error.c $(ARLIB)
5050
examples/simple_plpgsql: examples/simple_plpgsql.c $(ARLIB)
5151
$(CC) $(CFLAGS) -o $@ examples/simple_plpgsql.c $(ARLIB)
5252

53-
TESTS = test/deparse test/fingerprint test/fingerprint_opts test/normalize test/parse test/parse_opts test/parse_protobuf test/parse_protobuf_opts test/parse_plpgsql test/scan test/split test/summary test/summary_truncate
53+
TESTS = test/deparse test/fingerprint test/fingerprint_opts test/is_utility_stmt test/normalize test/parse test/parse_opts test/parse_protobuf test/parse_protobuf_opts test/parse_plpgsql test/scan test/split test/summary test/summary_truncate
5454
test: $(TESTS)
5555
.\test\deparse
5656
.\test\fingerprint
5757
.\test\fingerprint_opts
58+
.\test\is_utility_stmt
5859
.\test\normalize
5960
.\test\parse
6061
.\test\parse_opts
@@ -85,6 +86,9 @@ test/fingerprint_opts: test/fingerprint_opts.c test/fingerprint_opts_tests.c $(A
8586
# We have "-Isrc/" because this test uses pg_query_fingerprint_with_opts
8687
$(CC) $(CFLAGS) -o $@ -Isrc/ test/fingerprint_opts.c $(ARLIB)
8788

89+
test/is_utility_stmt: test/framework/main.c test/is_utility_stmt.c $(ARLIB)
90+
$(CC) $(CFLAGS) -o $@ test/framework/main.c test/is_utility_stmt.c $(ARLIB)
91+
8892
test/normalize: test/normalize.c test/normalize_tests.c $(ARLIB)
8993
$(CC) $(CFLAGS) -o $@ test/normalize.c $(ARLIB)
9094

pg_query.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#ifndef PG_QUERY_H
22
#define PG_QUERY_H
33

4+
#include <stdbool.h>
45
#include <stdint.h>
56
#include <sys/types.h>
67

@@ -15,6 +16,12 @@ typedef struct {
1516
char* context; // additional context (optional, can be NULL)
1617
} PgQueryError;
1718

19+
typedef struct {
20+
int length;
21+
bool *items;
22+
PgQueryError* error;
23+
} PgQueryIsUtilityResult;
24+
1825
typedef struct {
1926
size_t len;
2027
char* data;
@@ -135,6 +142,8 @@ PgQueryDeparseResult pg_query_deparse_protobuf(PgQueryProtobuf parse_tree);
135142
PgQueryDeparseResult pg_query_deparse_protobuf_opts(PgQueryProtobuf parse_tree, struct PostgresDeparseOpts opts);
136143
PgQueryDeparseCommentsResult pg_query_deparse_comments_for_query(const char *query);
137144

145+
PgQueryIsUtilityResult pg_query_is_utility_stmt(const char *query);
146+
138147
PgQuerySummaryParseResult pg_query_summary(const char* input, int parser_options, int truncate_limit);
139148

140149
void pg_query_free_normalize_result(PgQueryNormalizeResult result);
@@ -146,6 +155,7 @@ void pg_query_free_deparse_comments_result(PgQueryDeparseCommentsResult result);
146155
void pg_query_free_protobuf_parse_result(PgQueryProtobufParseResult result);
147156
void pg_query_free_plpgsql_parse_result(PgQueryPlpgsqlParseResult result);
148157
void pg_query_free_fingerprint_result(PgQueryFingerprintResult result);
158+
void pg_query_free_is_utility_result(PgQueryIsUtilityResult result);
149159
void pg_query_free_summary_parse_result(PgQuerySummaryParseResult result);
150160

151161
// Optional, cleans up the top-level memory context (automatically done for threads that exit)

src/pg_query_is_utility_stmt.c

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#include <stdbool.h>
2+
#include <stddef.h>
3+
#include <stdlib.h>
4+
5+
#include "pg_query.h"
6+
#include "pg_query_internal.h"
7+
#include <nodes/nodes.h>
8+
#include <nodes/nodeFuncs.h>
9+
10+
static
11+
bool
12+
is_utility_stmt_actual(RawStmt *raw_stmt)
13+
{
14+
switch (nodeTag(raw_stmt->stmt))
15+
{
16+
case T_SelectStmt:
17+
case T_InsertStmt:
18+
case T_UpdateStmt:
19+
case T_DeleteStmt:
20+
case T_MergeStmt:
21+
return false;
22+
23+
default:
24+
return true;
25+
}
26+
}
27+
28+
PgQueryIsUtilityResult
29+
pg_query_is_utility_stmt(const char *query)
30+
{
31+
PgQueryIsUtilityResult result = {0};
32+
MemoryContext ctx = pg_query_enter_memory_context();
33+
34+
PgQueryInternalParsetreeAndError parsetree_and_error = pg_query_raw_parse(query, 0);
35+
36+
if (parsetree_and_error.error)
37+
{
38+
result.error = parsetree_and_error.error;
39+
}
40+
else
41+
{
42+
ListCell *lc;
43+
44+
result.length = list_length(parsetree_and_error.tree);
45+
result.items = malloc(sizeof(bool) * result.length);
46+
47+
foreach(lc, parsetree_and_error.tree)
48+
{
49+
RawStmt *raw_stmt = lfirst_node(RawStmt, lc);
50+
51+
result.items[foreach_current_index(lc)] = is_utility_stmt_actual(raw_stmt);
52+
}
53+
}
54+
55+
if (parsetree_and_error.stderr_buffer)
56+
free(parsetree_and_error.stderr_buffer);
57+
58+
pg_query_exit_memory_context(ctx);
59+
60+
return result;
61+
}
62+
63+
void
64+
pg_query_free_is_utility_result(PgQueryIsUtilityResult result)
65+
{
66+
if (result.error)
67+
pg_query_free_error(result.error);
68+
69+
free(result.items);
70+
}

test/is_utility_stmt.c

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#include <pg_query.h>
2+
#include <stdbool.h>
3+
#include <stdio.h>
4+
#include <stdlib.h>
5+
6+
#include "test/framework/main.h"
7+
8+
struct LastResult {
9+
bool has_result;
10+
PgQueryIsUtilityResult result;
11+
};
12+
static struct LastResult last_result = {false, {0}};
13+
14+
static PgQueryIsUtilityResult
15+
is_utility_stmt(const char *msg)
16+
{
17+
last_result.has_result = true;
18+
last_result.result = pg_query_is_utility_stmt(msg);
19+
20+
return last_result.result;
21+
}
22+
23+
void cleanup()
24+
{
25+
if (!last_result.has_result)
26+
return;
27+
28+
last_result.has_result = false;
29+
pg_query_free_is_utility_result(last_result.result);
30+
}
31+
32+
#define assert_result_equal(actual, expected_len, expected) assert_result_equal_impl(test_state, #actual, actual, expected_len, expected)
33+
static void
34+
assert_result_equal_impl(TestState * test_state, char *actual_str, PgQueryIsUtilityResult actual, size_t expected_len, bool expected[])
35+
{
36+
bool all_match = (actual.length == expected_len);
37+
38+
for (size_t i = 0; (i < expected_len) && (i < actual.length) && all_match; i++) {
39+
if (actual.items[i] != expected[i])
40+
all_match = false;
41+
}
42+
43+
if (all_match)
44+
TEST_PASS();
45+
else {
46+
TEST_FAIL("Expected `actual` to equal `expected`, where\n");
47+
printf(" actual: ");
48+
for (size_t i = 0; i < actual.length; i++)
49+
printf("%s ", actual.items[i] ? "true " : "false");
50+
51+
printf("\n expected: ");
52+
for (size_t i = 0; i < expected_len; i++)
53+
printf("%s ", expected[i] ? "true " : "false");
54+
55+
puts("\n");
56+
}
57+
}
58+
59+
static void select_test(TestState* test_state)
60+
{
61+
TEST_INIT();
62+
63+
assert_result_equal(is_utility_stmt("SELECT 1"), 1, (bool[]){false});
64+
}
65+
66+
static void insert_test(TestState* test_state)
67+
{
68+
TEST_INIT();
69+
70+
assert_result_equal(is_utility_stmt("INSERT INTO my_table VALUES(123)"), 1, (bool[]){false});
71+
}
72+
73+
static void update_test(TestState* test_state)
74+
{
75+
TEST_INIT();
76+
assert_result_equal(is_utility_stmt("UPDATE my_table SET foo = 123"), 1, (bool[]){false});
77+
}
78+
79+
static void delete_test(TestState* test_state)
80+
{
81+
TEST_INIT();
82+
assert_result_equal(is_utility_stmt("DELETE FROM my_table"), 1, (bool[]){false});
83+
}
84+
85+
static void show_test(TestState* test_state)
86+
{
87+
TEST_INIT();
88+
assert_result_equal(is_utility_stmt("SHOW fsync"), 1, (bool[]){true});
89+
}
90+
91+
static void set_test(TestState* test_state)
92+
{
93+
TEST_INIT();
94+
assert_result_equal(is_utility_stmt("SET fsync = off"), 1, (bool[]){true});
95+
}
96+
97+
static void select2_test(TestState* test_state)
98+
{
99+
TEST_INIT();
100+
assert_result_equal(is_utility_stmt("SELECT 1; SELECT 2;"), 2, ((bool[]){false, false}));
101+
}
102+
103+
static void select_show_test(TestState* test_state)
104+
{
105+
TEST_INIT();
106+
assert_result_equal(is_utility_stmt("SELECT 1; SHOW fsync;"), 2, ((bool[]){false, true}));
107+
}
108+
109+
int
110+
main(int argc, char *argv[])
111+
{
112+
TestFn *tests[] = {
113+
&select_test,
114+
&insert_test,
115+
&update_test,
116+
&delete_test,
117+
&show_test,
118+
&set_test,
119+
&select2_test,
120+
&select_show_test,
121+
NULL
122+
};
123+
124+
return test_run(argc, argv, tests, &cleanup);
125+
}

0 commit comments

Comments
 (0)