Skip to content

Commit

Permalink
fix: support repeated named parameters
Browse files Browse the repository at this point in the history
This fixes `got %v argument values, but found %v parameters in the sql string`
error when using repeated named arguments.

The "workaround" is to specify as many parameters
as observed. The last one will win due to
`prepareSpannerStmt` using map keyed by name.

Due to this change the `Bad` examples will
start to return an error.

Example:

```golang
// Bad: @name will be "value2"
s.db.QueryRow(
  "SELECT * FROM users WHERE (first_name=@name OR last_name=@name)",
  sql.Named("name", "value"),
  sql.Named("name", "value2"),
)

// Bad: @name will be "value2"
s.db.QueryRow(
  "SELECT * FROM users WHERE (first_name=@name OR last_name=@name)",
  "value",
  "value2",
)

// Good: works, ideal usage
s.db.QueryRow(
  "SELECT * FROM users WHERE (first_name=@name OR last_name=@name)",
  sql.Named("name", "value"),
)

// Good: not ideal, inferred positional arguments
s.db.QueryRow(
  "SELECT * FROM users WHERE (first_name=@name OR last_name=@name)",
  "value",
)
```

Signed-off-by: Kamil Trzciński <[email protected]>
  • Loading branch information
ayufan committed May 20, 2024
1 parent ad95d85 commit 90cff3d
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 2 deletions.
14 changes: 12 additions & 2 deletions statement_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ func union(m1 map[string]bool, m2 map[string]bool) map[string]bool {
return res
}

// Use slices with Go 1.21
func appendOnce(slice []string, item string) []string {
for _, s := range slice {
if s == item {
return slice
}
}
return append(slice, item)
}

// parseParameters returns the parameters in the given sql string, if the input
// sql contains positional parameters it returns the converted sql string with
// all positional parameters replaced with named parameters.
Expand Down Expand Up @@ -249,13 +259,13 @@ func findParams(positionalParamChar rune, sql string) (string, []string, error)
for index < len(runes) {
if !(unicode.IsLetter(runes[index]) || unicode.IsDigit(runes[index]) || runes[index] == '_') {
hasNamedParameter = true
namedParams = append(namedParams, string(runes[startIndex:index]))
namedParams = appendOnce(namedParams, string(runes[startIndex:index]))
parsedSQL.WriteRune(runes[index])
break
}
if index == len(runes)-1 {
hasNamedParameter = true
namedParams = append(namedParams, string(runes[startIndex:]))
namedParams = appendOnce(namedParams, string(runes[startIndex:]))
parsedSQL.WriteRune(runes[index])
break
}
Expand Down
5 changes: 5 additions & 0 deletions statement_parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,11 @@ func TestFindParams(t *testing.T) {
wantSQL: "SELECT * FROM PersonsTable @{FORCE_INDEX=my_index} WHERE id=@id AND name=@name",
want: []string{"id", "name"},
},
{
input: "SELECT * FROM PersonsTable@{FORCE_INDEX=`my_index`} WHERE id=@id AND (first_name=@name OR last_name=@name)",
wantSQL: "SELECT * FROM PersonsTable@{FORCE_INDEX=`my_index`} WHERE id=@id AND (first_name=@name OR last_name=@name)",
want: []string{"id", "name"},
},
{
input: `SELECT * FROM PersonsTable WHERE id=?`,
wantSQL: `SELECT * FROM PersonsTable WHERE id=@p1`,
Expand Down

0 comments on commit 90cff3d

Please sign in to comment.