/src/libpg_query/src/pg_query_parse.c
Line | Count | Source |
1 | | #include "pg_query.h" |
2 | | #include "pg_query_internal.h" |
3 | | #include "pg_query_outfuncs.h" |
4 | | |
5 | | #include "parser/parser.h" |
6 | | #include "parser/scanner.h" |
7 | | #include "parser/scansup.h" |
8 | | |
9 | | #include <unistd.h> |
10 | | #include <fcntl.h> |
11 | | |
12 | | PgQueryInternalParsetreeAndError pg_query_raw_parse(const char* input, int parser_options) |
13 | 13 | { |
14 | 13 | PgQueryInternalParsetreeAndError result = {0}; |
15 | 13 | MemoryContext parse_context = CurrentMemoryContext; |
16 | | |
17 | 13 | char stderr_buffer[STDERR_BUFFER_LEN + 1] = {0}; |
18 | | #ifndef DEBUG |
19 | | int stderr_global; |
20 | | int stderr_pipe[2]; |
21 | | #endif |
22 | | |
23 | | #ifndef DEBUG |
24 | | // Setup pipe for stderr redirection |
25 | | if (pipe(stderr_pipe) != 0) { |
26 | | PgQueryError* error = malloc(sizeof(PgQueryError)); |
27 | | |
28 | | error->message = strdup("Failed to open pipe, too many open file descriptors") |
29 | | |
30 | | result.error = error; |
31 | | |
32 | | return result; |
33 | | } |
34 | | |
35 | | fcntl(stderr_pipe[0], F_SETFL, fcntl(stderr_pipe[0], F_GETFL) | O_NONBLOCK); |
36 | | |
37 | | // Redirect stderr to the pipe |
38 | | stderr_global = dup(STDERR_FILENO); |
39 | | dup2(stderr_pipe[1], STDERR_FILENO); |
40 | | close(stderr_pipe[1]); |
41 | | #endif |
42 | | |
43 | 13 | PG_TRY(); |
44 | 13 | { |
45 | 13 | RawParseMode rawParseMode = RAW_PARSE_DEFAULT; |
46 | 13 | switch (parser_options & PG_QUERY_PARSE_MODE_BITMASK) |
47 | 13 | { |
48 | 0 | case PG_QUERY_PARSE_TYPE_NAME: |
49 | 0 | rawParseMode = RAW_PARSE_TYPE_NAME; |
50 | 0 | break; |
51 | 0 | case PG_QUERY_PARSE_PLPGSQL_EXPR: |
52 | 0 | rawParseMode = RAW_PARSE_PLPGSQL_EXPR; |
53 | 0 | break; |
54 | 0 | case PG_QUERY_PARSE_PLPGSQL_ASSIGN1: |
55 | 0 | rawParseMode = RAW_PARSE_PLPGSQL_ASSIGN1; |
56 | 0 | break; |
57 | 0 | case PG_QUERY_PARSE_PLPGSQL_ASSIGN2: |
58 | 0 | rawParseMode = RAW_PARSE_PLPGSQL_ASSIGN2; |
59 | 0 | break; |
60 | 0 | case PG_QUERY_PARSE_PLPGSQL_ASSIGN3: |
61 | 0 | rawParseMode = RAW_PARSE_PLPGSQL_ASSIGN3; |
62 | 0 | break; |
63 | 13 | } |
64 | | |
65 | 13 | if ((parser_options & PG_QUERY_DISABLE_BACKSLASH_QUOTE) == PG_QUERY_DISABLE_BACKSLASH_QUOTE) { |
66 | 0 | backslash_quote = BACKSLASH_QUOTE_OFF; |
67 | 13 | } else { |
68 | 13 | backslash_quote = BACKSLASH_QUOTE_SAFE_ENCODING; |
69 | 13 | } |
70 | 13 | standard_conforming_strings = !((parser_options & PG_QUERY_DISABLE_STANDARD_CONFORMING_STRINGS) == PG_QUERY_DISABLE_STANDARD_CONFORMING_STRINGS); |
71 | 13 | escape_string_warning = !((parser_options & PG_QUERY_DISABLE_ESCAPE_STRING_WARNING) == PG_QUERY_DISABLE_ESCAPE_STRING_WARNING); |
72 | | |
73 | 13 | result.tree = raw_parser(input, rawParseMode); |
74 | | |
75 | 13 | backslash_quote = BACKSLASH_QUOTE_SAFE_ENCODING; |
76 | 13 | standard_conforming_strings = true; |
77 | 13 | escape_string_warning = true; |
78 | | |
79 | | #ifndef DEBUG |
80 | | // Save stderr for result |
81 | | read(stderr_pipe[0], stderr_buffer, STDERR_BUFFER_LEN); |
82 | | #endif |
83 | | |
84 | 13 | result.stderr_buffer = strdup(stderr_buffer); |
85 | 13 | } |
86 | 13 | PG_CATCH(); |
87 | 0 | { |
88 | 0 | ErrorData* error_data; |
89 | 0 | PgQueryError* error; |
90 | |
|
91 | 0 | MemoryContextSwitchTo(parse_context); |
92 | 0 | error_data = CopyErrorData(); |
93 | | |
94 | | // Note: This is intentionally malloc so exiting the memory context doesn't free this |
95 | 0 | error = malloc(sizeof(PgQueryError)); |
96 | 0 | error->message = strdup(error_data->message); |
97 | 0 | error->filename = strdup(error_data->filename); |
98 | 0 | error->funcname = strdup(error_data->funcname); |
99 | 0 | error->context = NULL; |
100 | 0 | error->lineno = error_data->lineno; |
101 | 0 | error->cursorpos = error_data->cursorpos; |
102 | |
|
103 | 0 | result.error = error; |
104 | 0 | FlushErrorState(); |
105 | 0 | } |
106 | 0 | PG_END_TRY(); |
107 | | |
108 | | #ifndef DEBUG |
109 | | // Restore stderr, close pipe |
110 | | dup2(stderr_global, STDERR_FILENO); |
111 | | close(stderr_pipe[0]); |
112 | | close(stderr_global); |
113 | | #endif |
114 | | |
115 | 13 | return result; |
116 | 13 | } |
117 | | |
118 | | PgQueryParseResult pg_query_parse(const char* input) |
119 | 13 | { |
120 | 13 | return pg_query_parse_opts(input, PG_QUERY_PARSE_DEFAULT); |
121 | 13 | } |
122 | | |
123 | | PgQueryParseResult pg_query_parse_opts(const char* input, int parser_options) |
124 | 13 | { |
125 | 13 | MemoryContext ctx = NULL; |
126 | 13 | PgQueryInternalParsetreeAndError parsetree_and_error; |
127 | 13 | PgQueryParseResult result = {0}; |
128 | 13 | char *tree_json = NULL; |
129 | | |
130 | 13 | ctx = pg_query_enter_memory_context(); |
131 | | |
132 | 13 | parsetree_and_error = pg_query_raw_parse(input, parser_options); |
133 | | |
134 | | // These are all malloc-ed and will survive exiting the memory context, the caller is responsible to free them now |
135 | 13 | result.stderr_buffer = parsetree_and_error.stderr_buffer; |
136 | 13 | result.error = parsetree_and_error.error; |
137 | | |
138 | 13 | tree_json = pg_query_nodes_to_json(parsetree_and_error.tree); |
139 | 13 | result.parse_tree = strdup(tree_json); |
140 | 13 | pfree(tree_json); |
141 | | |
142 | 13 | pg_query_exit_memory_context(ctx); |
143 | | |
144 | 13 | return result; |
145 | 13 | } |
146 | | |
147 | | PgQueryProtobufParseResult pg_query_parse_protobuf(const char* input) |
148 | 0 | { |
149 | 0 | return pg_query_parse_protobuf_opts(input, PG_QUERY_PARSE_DEFAULT); |
150 | 0 | } |
151 | | |
152 | | PgQueryProtobufParseResult pg_query_parse_protobuf_opts(const char* input, int parser_options) |
153 | 0 | { |
154 | 0 | MemoryContext ctx = NULL; |
155 | 0 | PgQueryInternalParsetreeAndError parsetree_and_error; |
156 | 0 | PgQueryProtobufParseResult result = {0}; |
157 | |
|
158 | 0 | ctx = pg_query_enter_memory_context(); |
159 | |
|
160 | 0 | parsetree_and_error = pg_query_raw_parse(input, parser_options); |
161 | | |
162 | | // These are all malloc-ed and will survive exiting the memory context, the caller is responsible to free them now |
163 | 0 | result.stderr_buffer = parsetree_and_error.stderr_buffer; |
164 | 0 | result.error = parsetree_and_error.error; |
165 | 0 | result.parse_tree = pg_query_nodes_to_protobuf(parsetree_and_error.tree); |
166 | |
|
167 | 0 | pg_query_exit_memory_context(ctx); |
168 | |
|
169 | 0 | return result; |
170 | 0 | } |
171 | | |
172 | | void pg_query_free_parse_result(PgQueryParseResult result) |
173 | 13 | { |
174 | 13 | if (result.error) { |
175 | 8 | pg_query_free_error(result.error); |
176 | 8 | } |
177 | | |
178 | 13 | free(result.parse_tree); |
179 | 13 | free(result.stderr_buffer); |
180 | 13 | } |
181 | | |
182 | | void pg_query_free_protobuf_parse_result(PgQueryProtobufParseResult result) |
183 | 0 | { |
184 | 0 | if (result.error) { |
185 | 0 | pg_query_free_error(result.error); |
186 | 0 | } |
187 | |
|
188 | 0 | free(result.parse_tree.data); |
189 | 0 | free(result.stderr_buffer); |
190 | 0 | } |