## Observed execution ### boundary SQL ```sh CONTAINER_ID=$(docker ps -q --filter 'label=devcontainer.local_folder' | head -1) docker exec -i "$CONTAINER_ID" env PGPASSWORD='[REDACTED]' psql -h 127.0.0.1 -p 5432 -U postgres -d postgres -v ON_ERROR_STOP=0 ``` ```sql DROP TABLE IF EXISTS bf_bound_25_fixture; CREATE TABLE bf_bound_25_fixture (id integer, label text); INSERT INTO bf_bound_25_fixture VALUES (3, 'three'), (1, 'one'), (4, 'four'), (2, 'two'); SELECT id, label FROM bf_bound_25_fixture ORDER BY id LIMIT 0 OFFSET 0; SELECT id, label FROM bf_bound_25_fixture ORDER BY id LIMIT -1; SELECT id, label FROM bf_bound_25_fixture ORDER BY id LIMIT 2 OFFSET -1; SELECT id, label FROM bf_bound_25_fixture ORDER BY id LIMIT NULL OFFSET 0; ``` ### boundary results ```text id | label ----+------- (0 rows) ERROR: LIMIT must be greater than or equal to 0 ERROR: OFFSET must be greater than or equal to 0 ERROR: receiveMessage recovered panic: interface conversion: interface {} is nil, not int64: goroutine 292 [running]: ``` ### recovered stack ```text github.com/dolthub/go-mysql-server/sql/analyzer.validateOffsetAndLimit.func1(...) /go/pkg/mod/github.com/dolthub/go-mysql-server@v0.20.1-0.20261008100326-71bfcfcc3400/sql/analyzer/validation_rules.go:53 github.com/dolthub/doltgresql/server.(*DoltgresHandler).executeQuery(...) /workspaces/repo/server/doltgres_handler.go:506 ``` # The server listener recovered the panic, so the SQL session returned an internal error rather than a defined NULL-LIMIT result. ## Code Analysis ### Limit conversion leaves NULL for downstream validation `server/ast/limit.go:29-80` ```go func nodeLimit(ctx *Context, node *tree.Limit) (*vitess.Limit, error) { if node == nil || ((node.Count == nil || node.Count == tree.NullLiteral{}) && (node.Offset == nil || node.Offset == tree.NullLiteral{})) { return nil, nil } var count vitess.Expr if !node.LimitAll { var err error count, err = nodeExpr(ctx, node.Count) if err != nil { return nil, err } } // GMS is hardcoded to expect vitess.SQLVal for expressions such as `LIMIT 1 OFFSET 1`. if injectedExpr, ok := count.(vitess.InjectedExpr); ok { if literal, ok := injectedExpr.Expression.(*expression.Literal); ok { l := literal.Value() limitValue, err := int64ValueForLimit(l) if err != nil { return nil, err } if limitValue < 0 { return nil, errors.Errorf("LIMIT must be greater than or equal to 0") } count = pgexprs.ToVitessLiteral(literal) } } return &vitess.Limit{Offset: offset, Rowcount: count}, nil } ``` The source performs explicit negative-value validation for converted literals, then returns the `vitess.Limit` for downstream analysis. The captured NULL input instead reaches downstream validation, whose stack trace performs the nil-to-`int64` assertion. ### Limit literals are intentionally exempt from type sanitization `server/analyzer/type_sanitizer.go:117-126` ```go case *expression.Literal: // We want to leave limit literals alone, as they are expected to be GMS types when they appear in certain // parts of the query (subqueries in particular) // TODO: fix the limit and offset validation analysis to handle doltgres types if _, isLimit := n.(*plan.Limit); isLimit { break } if _, isOffset := n.(*plan.Offset); isOffset { break } ``` This documented TODO and the observed `validateOffsetAndLimit` panic connect the source path to the failing boundary input without claiming that unobserved side effects occurred. ### Result The captured `LIMIT NULL` query produced an internal panic instead of a defined SQL response, while the source shows the limit value reaching downstream validation through an unfinished limit/offset type path. ### Test context The fixture was local and no stubs, mocks, or bypasses were applied. The password in the captured connection command is redacted here.