issue-59: db_query returns undefined content text for multi-statement SQL, breaking the session #60

Owner

Root cause

node-postgres resolves multi-statement SQL (simple query protocol) with an array of Result objects instead of a single result. In that shape result.rows is undefined, so:

  • db_query: JSON.stringify(result.rows) → JS undefined → tool returns { content: [{ type: "text", text: undefined }] }. That malformed block is persisted into session history, and every subsequent LLM call throws Cannot read properties of undefined (reading 'length') in pi-ai's token estimation (estimateMessageTokens reads block.text.length unguarded) — the session appears hung until restarted.
  • db_query_one: result.rows.length === 0 threw the same error directly (caught by the tool runner, so recoverable, but still broken on legitimate input).

Fix

  • Added collectRows(result) / collectRowCount(result) helpers that normalize both single-result and array-of-results shapes; rows are flattened across statements and text is always a JSON string.
  • All four tools now use them (db_query, db_query_one, plus defensive normalization in db_list_tables / db_table_schema).
  • Extracted tool registration into an exported registerDbTools(pi, pool) so tests can exercise real tool execution with a fake pool (no live DB, no module mocking).

Tests

New extensions/postgres/multi-statement.test.ts: helper unit tests + tool-execution regression tests covering the array-of-results shape, single-result behavior, and the no-rows placeholder. All 42 tests pass (bun test in extensions/postgres). Also verified live against a local postgres: multi-statement DDL+INSERT and two-SELECT queries now return proper string payloads with flattened rows.

Closes #59

## Root cause node-postgres resolves **multi-statement** SQL (simple query protocol) with an *array* of Result objects instead of a single result. In that shape `result.rows` is `undefined`, so: - `db_query`: `JSON.stringify(result.rows)` → JS `undefined` → tool returns `{ content: [{ type: "text", text: undefined }] }`. That malformed block is persisted into session history, and every subsequent LLM call throws `Cannot read properties of undefined (reading 'length')` in pi-ai's token estimation (`estimateMessageTokens` reads `block.text.length` unguarded) — the session appears hung until restarted. - `db_query_one`: `result.rows.length === 0` threw the same error directly (caught by the tool runner, so recoverable, but still broken on legitimate input). ## Fix - Added `collectRows(result)` / `collectRowCount(result)` helpers that normalize both single-result and array-of-results shapes; rows are flattened across statements and text is always a JSON string. - All four tools now use them (`db_query`, `db_query_one`, plus defensive normalization in `db_list_tables` / `db_table_schema`). - Extracted tool registration into an exported `registerDbTools(pi, pool)` so tests can exercise real tool execution with a fake pool (no live DB, no module mocking). ## Tests New `extensions/postgres/multi-statement.test.ts`: helper unit tests + tool-execution regression tests covering the array-of-results shape, single-result behavior, and the no-rows placeholder. All 42 tests pass (`bun test` in `extensions/postgres`). Also verified live against a local postgres: multi-statement DDL+INSERT and two-SELECT queries now return proper string payloads with flattened rows. Closes #59
david merged commit d2b9708efe into main 2026-08-20 17:45:03 +00:00
david deleted branch bug/issue-59/db-query-returns-undefined-content-text-for-multi-statement-sql-breaking-the-session 2026-08-20 17:45:04 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
david/pi-extensions-and-skills!60
No description provided.