Repository navigation
Report out of memory from pg_query_scan instead of writing through NULL - #12
pyramation wants to merge 1 commit into
Conversation
|
I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".
|
|
Review complete. 🔴 1 critical · 🟡 1 medium 💬 Inline comments (1)
📍 Findings outside the diff (1) — 🔴 1 critical — defects on lines GitHub can't attach comments to🔴 Critical — Missing semicolon breaks compilation of // src/pg_query_scan.c
63 error->message = strdup("Failed to open pipe, too many open file descriptors")The 🧹 Nitpicks (2) — 🟢 2 low
This PR introduces a tokenizing entry point in
Risk: high — the module does not compile as written, and the lexer error paths leak malloc-allocated resources. Reviewed commit: 8076f09 |
There was a problem hiding this comment.
Adds a new pg_query_scan API that tokenizes SQL with the core scanner and an OOM regression test, but a missing semicolon currently breaks compilation and error paths leak scanner resources.
Key findings
- 🔴 Missing semicolon breaks compilation of
pg_query_scan.c— pg_query_scan.c:63 - 🟡 Error paths leak the token array and
yyscanner— pg_query_scan.c:104
| output_tokens[i] = malloc(sizeof(PgQuery__ScanToken)); | ||
| if (output_tokens[i] == NULL) | ||
| { | ||
| free_scan_tokens(output_tokens, i); | ||
| ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"))); | ||
| } |
There was a problem hiding this comment.
🟡 bug · medium
Error paths leak the token array and yyscanner
When core_yylex raises an error mid-loop — invalid byte sequence, unterminated string, or overlong identifier — the ereport(ERROR) longjmps to PG_CATCH(), which never frees the partially built output_tokens array or finishes the second yyscanner (src/pg_query_scan.c:104-109). In the per-token OOM path the tokens are freed but the flex scanner opened at line 93 is likewise left un-finalized. Because flex scanner buffers and the token array are plain malloc allocations outside the PostgreSQL memory context, pg_query_exit_memory_context cannot reclaim them, so every failed scan leaks the token array plus scanner buffers.
📋 Prompt for AI Agents
In src/pg_query_scan.c, move the PgQuery__ScanToken **output_tokens declaration above PG_TRY() and initialize it to NULL. In the PG_CATCH() block (around lines 151-170), after MemoryContextSwitchTo(parse_context), add a guarded cleanup: if output_tokens != NULL, call free_scan_tokens(output_tokens, token_count) and set output_tokens = NULL; if yyscanner != NULL, call scanner_finish(yyscanner) and set yyscanner = NULL. On the success path set output_tokens = NULL and yyscanner = NULL right after the existing free_scan_tokens and scanner_finish calls (lines 127 and 142) so the catch-block cleanup cannot double-free. This closes the leak where an ereport(ERROR) from core_yylex or the per-token malloc failure longjmps past cleanup, leaking the malloc-allocated token array and the flex scanner buffers.
|
Folded into #13 |
Summary
pg_query_scannever checked its threemallocs: the token pointer array, eachPgQuery__ScanToken, andresult.pbuf.data. When one fails, the next write goes throughNULL:pg_query__scan_result__packwrites the protobuf at address 0 and overwrites static data, so the module later aborts withAssertion failed: <protobuf bytes>ortable index is out of bounds. This shows up at about 50 MB of short statements.Now each failure frees what was allocated so far and raises
ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"))). The existingPG_CATCHturns that intoresult.error, with the same messagepallocuses. On apbuf.datafailure,pbuf.lenis reset to 0.Test
test/scan.caddstest_scan_out_of_memory()(Linux only, skipped underUSE_VALGRIND). It capsRLIMIT_ASat the current size plus 128 MiB, scans"SELECT 1;\n"× 2,000,000 (6M tokens), and expectsresult.error->message == "out of memory".test/scansegfaults (exit 139).make testexits 0.make test VALGRIND=1:test/scanand every other test is clean excepttest/parse_plpgsql. That one still reports a 192-byte still-reachable block frompg_query_plpgsql_record_alias(pg_query_json_plpgsql.c:826). This predates the PR: it is red on18-constructivealready and is fixed in Make check_stack_depth() effective for embedders; guard recursive output paths #10.Not changed:
pg_query_nodes_to_protobufhas the same uncheckedmallocforprotobuf.data, but it runs outsidePG_TRY(it is called frompg_query_parse_protobuf_opts), soereportwould not be caught there. It needs a separate error path.Merge with a merge commit (not squash): constructive-io/libpg-query-node#188 pins this branch's head commit
8076f09.Link to Devin session: https://app.devin.ai/sessions/68876ce060664609ab6b63c8bed91e78
Open in Devin Desktop: https://app.devin.ai/desktop/session/68876ce060664609ab6b63c8bed91e78?variant=devin
Requested by: @pyramation