Fix INTO semantics in plpgsql - #3237
Conversation
An INTO clause without STRICT has three behaviors that Doltgres got wrong. A query matching no rows raised instead of setting every target to NULL. A query matching several rows raised instead of keeping the first and discarding the rest. And a target had to have exactly the type the query column produced, rather than accepting anything with an assignment cast to it. An INTO naming several variables also assigned only the first of them, because the loop that walked the targets was indexed by row rather than by target. QueryRowReturn replaces the two paths that INTO used to take, so a single target and several targets are now handled the same way, and reports whether the query produced a row at all. The casting that QuerySingleReturn did inline is extracted as castQueryValue so both share it.
Footnotes
|
|
SummaryThe run covers core database function behavior across ordinary assignments, NULL and empty-result handling, type conversion, multi-value targeting, repeated queries, dynamic queries, and invalid input shapes. It also exercises adversarial error paths and strict result-count behavior, providing broad business-logic and edge-case coverage. Merge with caution — this PR still has a medium-severity correctness issue in strict query cardinality handling, allowing invalid zero- or multi-row results to succeed instead of raising errors. A separate high-severity parameter-expression and NULL-condition issue is not attributable to this PR and remains a flag for later. Tests run by ItoAdditional Findings DetailsThese findings are unrelated to the current changes but were observed during testing. 🟠 PL/pgSQL parameters fail inside expressions
Evidence PackageTip Reply with @itoqa to send us feedback on this test run. |
|
@reltuk DOLT
|

An INTO clause without STRICT has three behaviors that Doltgres got wrong. A query matching no rows raised instead of setting every target to NULL. A query matching several rows raised instead of keeping the first and discarding the rest. And a target had to have exactly the type the query column produced, rather than accepting anything with an assignment cast to it.
An INTO naming several variables also assigned only the first of them, because the loop that walked the targets was indexed by row rather than by target.
QueryRowReturn replaces the two paths that INTO used to take, so a single target and several targets are now handled the same way, and reports whether the query produced a row at all. The casting that QuerySingleReturn did inline is extracted as castQueryValue so both share it.