Repository navigation
Conversation
|
|
SummaryThe run covers normal row-processing behavior for single- and multi-value loops, ordering, empty inputs, quoted names, and repeated calls, along with adversarial invalid targets and values. It also exercises error recovery and composite-record iteration, with the main happy paths working but weaknesses in conversion and session handling. Merge with caution — this change still has medium-severity failures in its new scalar-loop path: valid text-to-number assignments are rejected, and invalid input can terminate the database session instead of producing a recoverable error. The separate composite-record iteration defect is pre-existing and should be tracked independently, but the attributable conversion and session-safety issues warrant caution. Tests run by ItoAdditional Findings DetailsThese findings are unrelated to the current changes but were observed during testing. 🟡 FOREACH cannot read record fields
Evidence PackageTip Reply with @itoqa to send us feedback on this test run. |
There was a problem hiding this comment.
Invalid loop input closes the database connection
What failed: The invalid scalar call returned an integer conversion error, but the same connection closed immediately afterward. The later valid calls did not return their expected complete results.
Impact · Steps · Stub / mock · Analysis · Why this is likely a bug
- Severity: Medium
- Impact: An invalid value in a scalar loop closes the database connection instead of returning a recoverable error. Users lose the current session and may lose unfinished transaction work before they can reconnect.
- Steps to Reproduce:
- Create a PL/pgSQL function with an integer loop variable and a FOR loop over SELECT 'not-an-integer'::text.
- Create a second function that successfully loops over integer values and returns all of them.
- On one authenticated PostgreSQL connection, call the invalid function and then call the valid function twice.
- Observe the integer conversion error, then verify that the connection closes before either valid call returns a result.
- Stub / mock content: No stubs, mocks, or bypasses were applied for this test in the recorded run.
- Code Analysis: The PR changes server/plpgsql/interpreter_logic.go:508-515 so OpCode_ForQueryNext dispatches scalar targets to UpdateVariables and returns any assignment error directly from call. The new server/plpgsql/interpreter_stack.go:584-601 implementation calls the declared target type's Convert method at lines 595-598 and returns that conversion error without converting it into a recoverable statement-level outcome. The runtime evidence matches this path: UpdateVariables produced
integer: unhandled type: string, after which the server closed the client connection and emitted no later results. The PR also changes server/plpgsql/json.go:752-765 and server/plpgsql/statements.go:334-356 to route scalar FOR targets into this new operation metadata. A targeted fix should preserve the conversion error as a normal SQL statement error while keeping the session usable, and should avoid leaking partial scalar assignment if a multi-target row fails midway. - Why this is likely a bug: Invalid user data is an ordinary SQL failure, not a server-fatal condition. The neighboring rejection tests show that arity and conversion failures are intended to be reported as controlled errors, and the boundary test shows that a conversion error can normally be followed by a valid call on the same session. BF-FAIL-2 instead closes the connection immediately after the new scalar conversion error, preventing subsequent work and potentially discarding the session's transaction state. This is a concrete availability and session-recovery failure in the newly added scalar loop path, not merely a mismatch in error wording.
Relevant code
server/plpgsql/interpreter_logic.go:498-515
case OpCode_ForQueryNext: ... if len(operation.SecondaryData) > 0 { err = stack.UpdateVariables(ctx, operation.SecondaryData, schema, row) } ... if err != nil { return nil, err }server/plpgsql/interpreter_stack.go:584-601
func (is *InterpreterStack) UpdateVariables(...) error { ... value, _, err := iv.Type.Convert(ctx, row[i]); if err != nil { return err } ... }server/plpgsql/json.go:752-765
case stmt.Var.Variable != nil: variableNames = []string{stmt.Var.Variable.RefName}; case stmt.Var.Row != nil: ...Evidence Package
Copy prompt for an agent
Ito QA identified the following failure during automated PR testing. Please investigate and propose a fix.
**Medium severity — Invalid loop input closes the database connection**
**What failed:** The invalid scalar call returned an integer conversion error, but the same connection closed immediately afterward. The later valid calls did not return their expected complete results.
- **Impact:** An invalid value in a scalar loop closes the database connection instead of returning a recoverable error. Users lose the current session and may lose unfinished transaction work before they can reconnect.
- **Steps to reproduce:**
1. Create a PL/pgSQL function with an integer loop variable and a FOR loop over SELECT 'not-an-integer'::text.
2. Create a second function that successfully loops over integer values and returns all of them.
3. On one authenticated PostgreSQL connection, call the invalid function and then call the valid function twice.
4. Observe the integer conversion error, then verify that the connection closes before either valid call returns a result.
- **Stub / mock content:** No stubs, mocks, or bypasses were applied for this test in the recorded run.
- **Code analysis:** The PR changes server/plpgsql/interpreter_logic.go:508-515 so OpCode_ForQueryNext dispatches scalar targets to UpdateVariables and returns any assignment error directly from call. The new server/plpgsql/interpreter_stack.go:584-601 implementation calls the declared target type's Convert method at lines 595-598 and returns that conversion error without converting it into a recoverable statement-level outcome. The runtime evidence matches this path: UpdateVariables produced `integer: unhandled type: string`, after which the server closed the client connection and emitted no later results. The PR also changes server/plpgsql/json.go:752-765 and server/plpgsql/statements.go:334-356 to route scalar FOR targets into this new operation metadata. A targeted fix should preserve the conversion error as a normal SQL statement error while keeping the session usable, and should avoid leaking partial scalar assignment if a multi-target row fails midway.
- **Why this is likely a bug:** Invalid user data is an ordinary SQL failure, not a server-fatal condition. The neighboring rejection tests show that arity and conversion failures are intended to be reported as controlled errors, and the boundary test shows that a conversion error can normally be followed by a valid call on the same session. BF-FAIL-2 instead closes the connection immediately after the new scalar conversion error, preventing subsequent work and potentially discarding the session's transaction state. This is a concrete availability and session-recovery failure in the newly added scalar loop path, not merely a mismatch in error wording.
**Relevant code:**
`server/plpgsql/interpreter_logic.go:498-515`
~~~go
case OpCode_ForQueryNext: ... if len(operation.SecondaryData) > 0 { err = stack.UpdateVariables(ctx, operation.SecondaryData, schema, row) } ... if err != nil { return nil, err }
~~~
`server/plpgsql/interpreter_stack.go:584-601`
~~~go
func (is *InterpreterStack) UpdateVariables(...) error { ... value, _, err := iv.Type.Convert(ctx, row[i]); if err != nil { return err } ... }
~~~
`server/plpgsql/json.go:752-765`
~~~go
case stmt.Var.Variable != nil: variableNames = []string{stmt.Var.Variable.RefName}; case stmt.Var.Row != nil: ...
~~~| return fmt.Errorf("record variable `%s` could not be found", name) | ||
| } | ||
|
|
||
| // UpdateVariables assigns a query result row to a list of scalar variables. A FOR .. IN query LOOP |
There was a problem hiding this comment.
Scalar loop rejects a value that should become an integer
What failed: The function call returned an integer unhandled type string error instead of returning 7.
Impact · Steps · Stub / mock · Analysis · Why this is likely a bug
- Severity: Medium
- Impact: PL/pgSQL loops fail when a value such as '7' must be assigned to an integer variable, so affected database routines cannot complete normally.
- Steps to Reproduce:
- Create a PL/pgSQL function with an integer variable v and an integer accumulator.
- Inside the function, run FOR v IN SELECT '7'::text and add v to the accumulator.
- Call the function with SELECT and check the returned value.
- Stub / mock content: No stubs, mocks, or bypasses were applied for this test in the recorded run.
- Code Analysis: The PR introduces InterpreterStack.UpdateVariables in server/plpgsql/interpreter_stack.go:584-601 and invokes it for scalar FOR rows from server/plpgsql/interpreter_logic.go:506-515. UpdateVariables calls iv.Type.Convert(ctx, row[i]) at lines 595-597 before storing the result. For an integer target, DoltgresType.Convert in server/types/type.go:510-617 accepts the native int16, int32, or int64 forms at lines 548-559, but it has no conversion branch for a string value when the target is int4. The text result from SELECT '7'::text therefore reaches the fallback error at line 616 instead of being parsed as the declared integer type. The smallest practical fix is to use the existing type-assignment/cast conversion path for scalar FOR values, or add the narrow text-to-declared-numeric conversion needed by this assignment path, before assigning iv.Value.
- Why this is likely a bug: The scalar assignment helper explicitly promises conversion to each variable's declared type, and the PR routes normal scalar FOR query rows through that helper. The tested value is a valid textual representation of the declared integer, but the production conversion code only accepts an already-typed integer and returns an unhandled-type error for the text value. This blocks a normal PL/pgSQL assignment scenario and is directly caused by the new scalar path; using the established assignment conversion behavior would preserve the loop body and return 7.
Relevant code
server/plpgsql/interpreter_stack.go:584-601
func (is *InterpreterStack) UpdateVariables(ctx *sql.Context, names []string, schema sql.Schema, row sql.Row) error {
...
value, _, err := iv.Type.Convert(ctx, row[i])
if err != nil {
return err
}
iv.Value = value
}server/plpgsql/interpreter_logic.go:506-515
if len(operation.SecondaryData) > 0 {
err = stack.UpdateVariables(ctx, operation.SecondaryData, schema, row)
}
if err != nil {
return nil, err
}server/types/type.go:548-616
case "int2": ... int16 ...
case "int4": ... int32 ...
case "int8": ... int64 ...
...
return nil, sql.InRange, ErrUnhandledType.New(t.String(), v)Evidence Package
Copy prompt for an agent
Ito QA identified the following failure during automated PR testing. Please investigate and propose a fix.
**Medium severity — Scalar loop rejects a value that should become an integer**
**What failed:** The function call returned an integer unhandled type string error instead of returning 7.
- **Impact:** PL/pgSQL loops fail when a value such as '7' must be assigned to an integer variable, so affected database routines cannot complete normally.
- **Steps to reproduce:**
1. Create a PL/pgSQL function with an integer variable v and an integer accumulator.
2. Inside the function, run FOR v IN SELECT '7'::text and add v to the accumulator.
3. Call the function with SELECT and check the returned value.
- **Stub / mock content:** No stubs, mocks, or bypasses were applied for this test in the recorded run.
- **Code analysis:** The PR introduces InterpreterStack.UpdateVariables in server/plpgsql/interpreter_stack.go:584-601 and invokes it for scalar FOR rows from server/plpgsql/interpreter_logic.go:506-515. UpdateVariables calls iv.Type.Convert(ctx, row[i]) at lines 595-597 before storing the result. For an integer target, DoltgresType.Convert in server/types/type.go:510-617 accepts the native int16, int32, or int64 forms at lines 548-559, but it has no conversion branch for a string value when the target is int4. The text result from SELECT '7'::text therefore reaches the fallback error at line 616 instead of being parsed as the declared integer type. The smallest practical fix is to use the existing type-assignment/cast conversion path for scalar FOR values, or add the narrow text-to-declared-numeric conversion needed by this assignment path, before assigning iv.Value.
- **Why this is likely a bug:** The scalar assignment helper explicitly promises conversion to each variable's declared type, and the PR routes normal scalar FOR query rows through that helper. The tested value is a valid textual representation of the declared integer, but the production conversion code only accepts an already-typed integer and returns an unhandled-type error for the text value. This blocks a normal PL/pgSQL assignment scenario and is directly caused by the new scalar path; using the established assignment conversion behavior would preserve the loop body and return 7.
**Relevant code:**
`server/plpgsql/interpreter_stack.go:584-601`
~~~go
func (is *InterpreterStack) UpdateVariables(ctx *sql.Context, names []string, schema sql.Schema, row sql.Row) error {
...
value, _, err := iv.Type.Convert(ctx, row[i])
if err != nil {
return err
}
iv.Value = value
}
~~~
`server/plpgsql/interpreter_logic.go:506-515`
~~~go
if len(operation.SecondaryData) > 0 {
err = stack.UpdateVariables(ctx, operation.SecondaryData, schema, row)
}
if err != nil {
return nil, err
}
~~~
`server/types/type.go:548-616`
~~~go
case "int2": ... int16 ...
case "int4": ... int32 ...
case "int8": ... int64 ...
...
return nil, sql.InRange, ErrUnhandledType.New(t.String(), v)
~~~|
@reltuk DOLT
|
|
Diff SummaryCoverage exercises database-backed value handling across normal query and loop flows, including scalar and record assignments, type conversion, arrays, repeated reads, ordering, empty results, malformed inputs, error recovery, and concurrent sessions. It includes happy paths, boundary cases, adversarial error handling, and state-isolation checks, with the exercised behavior showing healthy results. Safe to merge — the run found no PR-attributable regressions, new failures, or previously flagged failures that remain unresolved. Previously passing areas not covered in this run are a follow-up coverage gap rather than a merge blocker. Tests run by Ito
Tip Reply with @itoqa to send us feedback on this test run. |
…sql-for-select-loop-targets # Conflicts: # server/types/type.go
|
Diff SummaryCoverage spans core array behavior such as storing, rendering, reshaping, indexing, concatenating, and decoding nested, empty, NULL-containing, and domain-based values, along with scalar loop execution and type conversion. It also exercises edge cases and recovery from malformed inputs, invalid indexes, incompatible operations, conversion errors, and session failures, with overall healthy behavior observed. Safe to merge — all exercised behaviors passed, and there are no regressions, new failures, or previously flagged failures attributable to this PR. No merge-blocking issue was identified. Tests run by Ito
Tip Reply with @itoqa to send us feedback on this test run. |


Fixes #3409.