Repository navigation
Return an error for a batch insert whose VALUES group never closes - #36
Merged
Merged
Conversation
…nd skip brackets inside literals and comments when expanding it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A batch insert (a slice or array argument to
NamedExec,BindNamedand the like) with an unclosedVALUES (group used to come back unexpanded, carrying every row's arguments, so the mistake only showed up when the database rejected the argument count. It now returnsmissing closing bracket in VALUES, the same wording vinovest/sqlx uses, whatever the number of rows.While there, the bracket matching that finds the VALUES group skips string literals, quoted identifiers and comments. On master:
INSERT INTO foo (a,b) VALUES (?, ')')VALUES (?, '),(?, ')')(invalid)VALUES (?, ')'),(?, ')')INSERT INTO foo (a,b) VALUES (?, '(') RETURNING idVALUES (?, '('),(?, '(') RETURNING idINSERT INTO foo (a,b) VALUES (?, /* ) */ ?)VALUES (?, /* ),(?, /* ) */ ?)(invalid)INSERT INTO foo (id) VALUES(:idVALUES(?with 2 args, no errorChanges
scan.go: the literal, quoted-identifier, comment and dollar-quote cases of the scanner move intoskipOpaque, shared with the bracket matcher. No behaviour change: a differential fuzz against master's scanner ran 51M inputs with identical tokens.named.go:matchingCloseParen(literal-aware, -1 when nothing closes) replacesfindMatchingClosingBracketIndex;valuesGrouppicks the group as before (first closed group with a bind variable, else the first closed group) and reports an unclosed group that holds bind variables;fixBounduses it;bindArrayreturns the error.TestFixBoundsrows andTestBindArrayUnclosedValues(one row, two rows, pointer to slice, bracket in a literal, both bind types; plus balanced, literal-bracket, upsertVALUES(col, no-VALUES, struct and map cases that must stay unchanged). CHANGELOG under Unreleased.Verified locally
go test -race ./...in the root; the four fuzz targets 15s each;dbtestagainst Postgres 16, MySQL 8 and SQLite: 39 pass, 0 fail, no database disabled.Note on scope. nyaruka/gocommon's
BulkSQLcallssqlx.Namedonce per struct and builds the bulk statement itself, so it does not reach this path. On the fork it already errors on an unclosed VALUES, with its own message ("error extracting VALUES from sql: ..."); vinovest raisesmissing closing bracket in VALUESfrom its named-query compiler for any argument. Matching that would mean the same check for single-struct and map queries, which this PR deliberately does not do.