From 7931af898f4ca9c53259b9fc7b7919456af2f3fd Mon Sep 17 00:00:00 2001 From: maishivamhoo123 Date: Tue, 20 Jan 2026 19:24:21 +0530 Subject: [PATCH 1/2] fix: validate named parameters are present in arguments --- driver_test.go | 39 +++++++++++++++++++++++++++++++++++++++ go.sum | 8 -------- stmt.go | 3 ++- 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/driver_test.go b/driver_test.go index 1b2dd10d..23dfed31 100644 --- a/driver_test.go +++ b/driver_test.go @@ -23,6 +23,7 @@ import ( "io" "net" "reflect" + "strings" "testing" "time" @@ -1017,6 +1018,44 @@ func TestConnectorConfigJson(t *testing.T) { t.Fatalf("failed to marshal ConnectorConfig: %v", err) } } +func TestQuery_NamedParameterValidation(t *testing.T) { + // 1. Use the repo's internal helper to get a working DB and Mock Server + db, _, teardown := setupTestDBConnection(t) + defer teardown() + + ctx := context.Background() + + tests := []struct { + name string + query string + args []any + wantWarn string + }{ + { + name: "Missing named parameter", + query: "SELECT * FROM my_table WHERE id = @my_id", + args: []any{sql.Named("id", 1)}, // User gave 'id', SQL wants 'my_id' + wantWarn: "parameter @my_id not found", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // Execute query + _, err := db.QueryContext(ctx, tc.query, tc.args...) + + // Before your fix, err will be nil (the bug) + // After your fix, err will contain "parameter @target_id not found" + if err == nil { + t.Errorf("%s: expected error containing %q, but got nil", tc.name, tc.wantWarn) + } else if !strings.Contains(err.Error(), "not found") { + t.Errorf("%s: expected error containing %q, but got %v", tc.name, tc.wantWarn, err) + } else { + t.Logf("Successfully caught expected error: %v", err) + } + }) + } +} // ValuerPerson implements driver.Valuer type ValuerPerson struct { diff --git a/go.sum b/go.sum index 43c0ba62..612bc26d 100644 --- a/go.sum +++ b/go.sum @@ -319,8 +319,6 @@ cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGE cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= -cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8= -cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE= cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc= cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU= cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= @@ -380,8 +378,6 @@ cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhI cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= -cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM= -cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U= cloud.google.com/go/monitoring v1.24.3 h1:dde+gMNc0UhPZD1Azu6at2e79bfdztVDS5lvhOdsgaE= cloud.google.com/go/monitoring v1.24.3/go.mod h1:nYP6W0tm3N9H/bOw8am7t62YTzZY+zUeQ+Bi6+2eonI= cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= @@ -530,8 +526,6 @@ cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+ cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= -cloud.google.com/go/spanner v1.86.1 h1:lSeVPwUotuKTpf8K6BPitzneQfGu73QcDFIca2lshG8= -cloud.google.com/go/spanner v1.86.1/go.mod h1:bbwCXbM+zljwSPLZ44wZOdzcdmy89hbUGmM/r9sD0ws= cloud.google.com/go/spanner v1.87.0 h1:M9RGcj/4gJk6yY1lRLOz1Ze+5ufoWhbIiurzXLOOfcw= cloud.google.com/go/spanner v1.87.0/go.mod h1:tcj735Y2aqphB6/l+X5MmwG4NnV+X1NJIbFSZGaHYXw= cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= @@ -1518,8 +1512,6 @@ google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOl google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/genproto v0.0.0-20250804133106-a7a43d27e69b h1:eZTgydvqZO44zyTZAvMaSyAxccZZdraiSAGvqOczVvk= google.golang.org/genproto v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:suyz2QBHQKlGIF92HEEsCfO1SwxXdk7PFLz+Zd9Uah4= -google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4= -google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo= google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba h1:B14OtaXuMaCQsl2deSvNkyPKIzq3BjfxQp8d00QyWx4= google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:G5IanEx8/PgI9w6CFcYQf7jMtHQhZruvfM1i3qOqk5U= google.golang.org/genproto/googleapis/rpc v0.0.0-20251213004720-97cd9d5aeac2 h1:2I6GHUeJ/4shcDpoUlLs/2WPnhg7yJwvXtqcMJt9liA= diff --git a/stmt.go b/stmt.go index 81a4e832..a936c101 100644 --- a/stmt.go +++ b/stmt.go @@ -18,6 +18,7 @@ import ( "context" "database/sql" "database/sql/driver" + "fmt" "cloud.google.com/go/spanner" "github.com/googleapis/go-sql-spanner/connectionstate" @@ -116,7 +117,7 @@ func prepareSpannerStmt(state *connectionstate.ConnectionState, parser *parser.S // Verify that all parameters have a value. for _, name := range names { if _, ok := ss.Params[name]; !ok { - return spanner.Statement{}, spanner.ToSpannerError(status.Errorf(codes.InvalidArgument, "missing value for query parameter %v", name)) + return spanner.Statement{}, fmt.Errorf("parameter @%s not found in the provided arguments", name) } } return ss, nil From 05a734ca95e8dce114cb760d93d1c5c66a0cfe79 Mon Sep 17 00:00:00 2001 From: maishivamhoo123 Date: Tue, 20 Jan 2026 19:41:39 +0530 Subject: [PATCH 2/2] chore: use spanner.ToSpannerError for parameter validation --- stmt.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stmt.go b/stmt.go index a936c101..2b33fac5 100644 --- a/stmt.go +++ b/stmt.go @@ -18,7 +18,6 @@ import ( "context" "database/sql" "database/sql/driver" - "fmt" "cloud.google.com/go/spanner" "github.com/googleapis/go-sql-spanner/connectionstate" @@ -117,7 +116,7 @@ func prepareSpannerStmt(state *connectionstate.ConnectionState, parser *parser.S // Verify that all parameters have a value. for _, name := range names { if _, ok := ss.Params[name]; !ok { - return spanner.Statement{}, fmt.Errorf("parameter @%s not found in the provided arguments", name) + return spanner.Statement{}, spanner.ToSpannerError(status.Errorf(codes.InvalidArgument, "parameter @%s not found in the provided arguments", name)) } } return ss, nil