Coverage Report

Created: 2026-05-30 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}