#include "pg_query.h" #include "pg_query_internal.h" #include "pg_query_outfuncs.h" #include "parser/parser.h" #include "parser/scanner.h" #include "parser/scansup.h" #include #include PgQueryInternalParsetreeAndError pg_query_raw_parse(const char* input, int parser_options) { PgQueryInternalParsetreeAndError result = {0}; 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(); { RawParseMode rawParseMode = RAW_PARSE_DEFAULT; switch (parser_options & PG_QUERY_PARSE_MODE_BITMASK) { case PG_QUERY_PARSE_TYPE_NAME: rawParseMode = RAW_PARSE_TYPE_NAME; break; case PG_QUERY_PARSE_PLPGSQL_EXPR: rawParseMode = RAW_PARSE_PLPGSQL_EXPR; break; case PG_QUERY_PARSE_PLPGSQL_ASSIGN1: rawParseMode = RAW_PARSE_PLPGSQL_ASSIGN1; break; case PG_QUERY_PARSE_PLPGSQL_ASSIGN2: rawParseMode = RAW_PARSE_PLPGSQL_ASSIGN2; break; case PG_QUERY_PARSE_PLPGSQL_ASSIGN3: rawParseMode = RAW_PARSE_PLPGSQL_ASSIGN3; break; } if ((parser_options & PG_QUERY_DISABLE_BACKSLASH_QUOTE) == PG_QUERY_DISABLE_BACKSLASH_QUOTE) { backslash_quote = BACKSLASH_QUOTE_OFF; } else { backslash_quote = BACKSLASH_QUOTE_SAFE_ENCODING; } standard_conforming_strings = !((parser_options & PG_QUERY_DISABLE_STANDARD_CONFORMING_STRINGS) == PG_QUERY_DISABLE_STANDARD_CONFORMING_STRINGS); escape_string_warning = !((parser_options & PG_QUERY_DISABLE_ESCAPE_STRING_WARNING) == PG_QUERY_DISABLE_ESCAPE_STRING_WARNING); result.tree = raw_parser(input, rawParseMode); backslash_quote = BACKSLASH_QUOTE_SAFE_ENCODING; standard_conforming_strings = true; escape_string_warning = true; #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 return result; } PgQueryParseResult pg_query_parse(const char* input) { return pg_query_parse_opts(input, PG_QUERY_PARSE_DEFAULT); } PgQueryParseResult pg_query_parse_opts(const char* input, int parser_options) { MemoryContext ctx = NULL; PgQueryInternalParsetreeAndError parsetree_and_error; PgQueryParseResult result = {0}; char *tree_json = NULL; ctx = pg_query_enter_memory_context(); parsetree_and_error = pg_query_raw_parse(input, parser_options); // These are all malloc-ed and will survive exiting the memory context, the caller is responsible to free them now result.stderr_buffer = parsetree_and_error.stderr_buffer; result.error = parsetree_and_error.error; tree_json = pg_query_nodes_to_json(parsetree_and_error.tree); result.parse_tree = strdup(tree_json); pfree(tree_json); pg_query_exit_memory_context(ctx); return result; } PgQueryProtobufParseResult pg_query_parse_protobuf(const char* input) { return pg_query_parse_protobuf_opts(input, PG_QUERY_PARSE_DEFAULT); } PgQueryProtobufParseResult pg_query_parse_protobuf_opts(const char* input, int parser_options) { MemoryContext ctx = NULL; PgQueryInternalParsetreeAndError parsetree_and_error; PgQueryProtobufParseResult result = {0}; ctx = pg_query_enter_memory_context(); parsetree_and_error = pg_query_raw_parse(input, parser_options); // These are all malloc-ed and will survive exiting the memory context, the caller is responsible to free them now result.stderr_buffer = parsetree_and_error.stderr_buffer; result.error = parsetree_and_error.error; result.parse_tree = pg_query_nodes_to_protobuf(parsetree_and_error.tree); pg_query_exit_memory_context(ctx); return result; } void pg_query_free_parse_result(PgQueryParseResult result) { if (result.error) { pg_query_free_error(result.error); } free(result.parse_tree); free(result.stderr_buffer); } void pg_query_free_protobuf_parse_result(PgQueryProtobufParseResult result) { if (result.error) { pg_query_free_error(result.error); } free(result.parse_tree.data); free(result.stderr_buffer); }