1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
|
#include "pg_query.h"
#include "pg_query_internal.h"
#include "parser/gramparse.h"
#include "lib/stringinfo.h"
#include "protobuf/pg_query.pb-c.h"
#include <unistd.h>
#include <fcntl.h>
/* This is ugly. We need to access yyleng outside of scan.l, and casting yyscanner
to this internal struct seemed like one way to do it... */
struct yyguts_t
{
void *yyextra_r;
FILE *yyin_r, *yyout_r;
size_t yy_buffer_stack_top; /**< index of top of stack. */
size_t yy_buffer_stack_max; /**< capacity of stack. */
struct yy_buffer_state *yy_buffer_stack; /**< Stack as an array. */
char yy_hold_char;
size_t yy_n_chars;
size_t yyleng_r;
};
PgQueryScanResult pg_query_scan(const char* input)
{
MemoryContext ctx = NULL;
PgQueryScanResult result = {0};
core_yyscan_t yyscanner;
core_yy_extra_type yyextra;
core_YYSTYPE yylval;
YYLTYPE yylloc;
PgQuery__ScanResult scan_result = PG_QUERY__SCAN_RESULT__INIT;
PgQuery__ScanToken **output_tokens;
size_t token_count = 0;
size_t i;
ctx = pg_query_enter_memory_context();
MemoryContext parse_context = CurrentMemoryContext;
char stderr_buffer[STDERR_BUFFER_LEN + 1] = {0};
#ifndef DEBUG
int stderr_global;
int stderr_pipe[2];
#endif
#ifndef DEBUG
// Setup pipe for stderr redirection
if (pipe(stderr_pipe) != 0) {
PgQueryError* error = malloc(sizeof(PgQueryError));
error->message = strdup("Failed to open pipe, too many open file descriptors")
result.error = error;
return result;
}
fcntl(stderr_pipe[0], F_SETFL, fcntl(stderr_pipe[0], F_GETFL) | O_NONBLOCK);
// Redirect stderr to the pipe
stderr_global = dup(STDERR_FILENO);
dup2(stderr_pipe[1], STDERR_FILENO);
close(stderr_pipe[1]);
#endif
PG_TRY();
{
// Really this is stupid, we only run twice so we can pre-allocate the output array correctly
yyscanner = scanner_init(input, &yyextra, &ScanKeywords, ScanKeywordTokens);
for (;; token_count++)
{
if (core_yylex(&yylval, &yylloc, yyscanner) == 0) break;
}
scanner_finish(yyscanner);
output_tokens = malloc(sizeof(PgQuery__ScanToken *) * token_count);
/* initialize the flex scanner --- should match raw_parser() */
yyscanner = scanner_init(input, &yyextra, &ScanKeywords, ScanKeywordTokens);
/* Lex tokens */
for (i = 0; ; i++)
{
int tok;
int keyword;
tok = core_yylex(&yylval, &yylloc, yyscanner);
if (tok == 0) break;
output_tokens[i] = malloc(sizeof(PgQuery__ScanToken));
pg_query__scan_token__init(output_tokens[i]);
output_tokens[i]->start = yylloc;
if (tok == SCONST || tok == BCONST || tok == XCONST || tok == IDENT || tok == C_COMMENT) {
output_tokens[i]->end = yyextra.yyllocend;
} else {
output_tokens[i]->end = yylloc + ((struct yyguts_t*) yyscanner)->yyleng_r;
}
output_tokens[i]->token = tok;
switch (tok) {
#define PG_KEYWORD(a,b,c) case b: output_tokens[i]->keyword_kind = c + 1; break;
#include "parser/kwlist.h"
default: output_tokens[i]->keyword_kind = 0;
}
}
scanner_finish(yyscanner);
scan_result.version = PG_VERSION_NUM;
scan_result.n_tokens = token_count;
scan_result.tokens = output_tokens;
result.pbuf.len = pg_query__scan_result__get_packed_size(&scan_result);
result.pbuf.data = malloc(result.pbuf.len);
pg_query__scan_result__pack(&scan_result, (void*) result.pbuf.data);
for (i = 0; i < token_count; i++) {
free(output_tokens[i]);
}
free(output_tokens);
#ifndef DEBUG
// Save stderr for result
read(stderr_pipe[0], stderr_buffer, STDERR_BUFFER_LEN);
#endif
result.stderr_buffer = strdup(stderr_buffer);
}
PG_CATCH();
{
ErrorData* error_data;
PgQueryError* error;
MemoryContextSwitchTo(parse_context);
error_data = CopyErrorData();
// Note: This is intentionally malloc so exiting the memory context doesn't free this
error = malloc(sizeof(PgQueryError));
error->message = strdup(error_data->message);
error->filename = strdup(error_data->filename);
error->funcname = strdup(error_data->funcname);
error->context = NULL;
error->lineno = error_data->lineno;
error->cursorpos = error_data->cursorpos;
result.error = error;
FlushErrorState();
}
PG_END_TRY();
#ifndef DEBUG
// Restore stderr, close pipe
dup2(stderr_global, STDERR_FILENO);
close(stderr_pipe[0]);
close(stderr_global);
#endif
pg_query_exit_memory_context(ctx);
return result;
}
void pg_query_free_scan_result(PgQueryScanResult result)
{
if (result.error) {
pg_query_free_error(result.error);
}
free(result.pbuf.data);
free(result.stderr_buffer);
}
|