@@ -4143,3 +4143,207 @@ func TestViewSchemaHydration(t *testing.T) {
41434143 }
41444144 }
41454145}
4146+
4147+ // TestQueryWithPositionalParameters tests issue #69: https://github.com/Recidiviz/bigquery-emulator/issues/69
4148+ // Verifies that positional query parameters (?) work correctly and are not broken by allow_undeclared_parameters mode.
4149+ // The issue reports that v0.6.6-recidiviz.3.5 broke positional parameters because allow_undeclared_parameters was
4150+ // enabled globally. According to ZetaSQL docs: "When allow_undeclared_parameters is true, no positional parameters may be provided."
4151+ func TestQueryWithPositionalParameters (t * testing.T ) {
4152+ const (
4153+ projectID = "test"
4154+ datasetID = "test_dataset"
4155+ tableID = "test_table"
4156+ )
4157+
4158+ ctx := context .Background ()
4159+
4160+ bqServer , err := server .New (server .TempStorage )
4161+ if err != nil {
4162+ t .Fatal (err )
4163+ }
4164+
4165+ // Create test table with sample data for testing positional parameters
4166+ project := types .NewProject (
4167+ projectID ,
4168+ types .NewDataset (
4169+ datasetID ,
4170+ types .NewTable (
4171+ tableID ,
4172+ []* types.Column {
4173+ types .NewColumn ("id" , types .INTEGER ),
4174+ types .NewColumn ("name" , types .STRING ),
4175+ types .NewColumn ("value" , types .FLOAT ),
4176+ types .NewColumn ("active" , types .BOOLEAN ),
4177+ },
4178+ types.Data {
4179+ {"id" : 1 , "name" : "Alice" , "value" : 10.5 , "active" : true },
4180+ {"id" : 2 , "name" : "Bob" , "value" : 20.7 , "active" : false },
4181+ {"id" : 3 , "name" : "Charlie" , "value" : 30.2 , "active" : true },
4182+ {"id" : 4 , "name" : "David" , "value" : 40.9 , "active" : false },
4183+ {"id" : 5 , "name" : "Eve" , "value" : 50.1 , "active" : true },
4184+ },
4185+ ),
4186+ ),
4187+ )
4188+ if err := bqServer .Load (server .StructSource (project )); err != nil {
4189+ t .Fatal (err )
4190+ }
4191+
4192+ testServer := bqServer .TestServer ()
4193+ defer func () {
4194+ testServer .Close ()
4195+ bqServer .Close ()
4196+ }()
4197+
4198+ client , err := bigquery .NewClient (
4199+ ctx ,
4200+ projectID ,
4201+ option .WithEndpoint (testServer .URL ),
4202+ option .WithoutAuthentication (),
4203+ )
4204+ if err != nil {
4205+ t .Fatal (err )
4206+ }
4207+ defer client .Close ()
4208+
4209+ t .Run ("Multiple positional parameters" , func (t * testing.T ) {
4210+ query := client .Query (fmt .Sprintf ("SELECT id, name, value FROM `%s.%s.%s` WHERE id >= ? AND id <= ? ORDER BY id" , projectID , datasetID , tableID ))
4211+ query .Parameters = []bigquery.QueryParameter {
4212+ {Value : 2 },
4213+ {Value : 4 },
4214+ }
4215+
4216+ it , err := query .Read (ctx )
4217+ if err != nil {
4218+ t .Fatalf ("Query failed: %v" , err )
4219+ }
4220+
4221+ var rows [][]bigquery.Value
4222+ for {
4223+ var row []bigquery.Value
4224+ if err := it .Next (& row ); err != nil {
4225+ if err == iterator .Done {
4226+ break
4227+ }
4228+ t .Fatal (err )
4229+ }
4230+ rows = append (rows , row )
4231+ }
4232+
4233+ if len (rows ) != 3 {
4234+ t .Fatalf ("Expected 3 rows, got %d" , len (rows ))
4235+ }
4236+ // Verify we got ids 2, 3, 4
4237+ expectedIDs := []int64 {2 , 3 , 4 }
4238+ for i , row := range rows {
4239+ if row [0 ].(int64 ) != expectedIDs [i ] {
4240+ t .Errorf ("Row %d: expected id=%d, got %v" , i , expectedIDs [i ], row [0 ])
4241+ }
4242+ }
4243+ })
4244+
4245+ t .Run ("Positional parameters with different types" , func (t * testing.T ) {
4246+ query := client .Query (fmt .Sprintf ("SELECT id, name FROM `%s.%s.%s` WHERE value > ? AND active = ? ORDER BY id" , projectID , datasetID , tableID ))
4247+ query .Parameters = []bigquery.QueryParameter {
4248+ {Value : 25.0 }, // Float
4249+ {Value : true }, // Boolean
4250+ }
4251+
4252+ it , err := query .Read (ctx )
4253+ if err != nil {
4254+ t .Fatalf ("Query failed: %v" , err )
4255+ }
4256+
4257+ var rows [][]bigquery.Value
4258+ for {
4259+ var row []bigquery.Value
4260+ if err := it .Next (& row ); err != nil {
4261+ if err == iterator .Done {
4262+ break
4263+ }
4264+ t .Fatal (err )
4265+ }
4266+ rows = append (rows , row )
4267+ }
4268+
4269+ // Should get Charlie (value=30.2, active=true) and Eve (value=50.1, active=true)
4270+ if len (rows ) != 2 {
4271+ t .Fatalf ("Expected 2 rows, got %d" , len (rows ))
4272+ }
4273+ expectedNames := []string {"Charlie" , "Eve" }
4274+ for i , row := range rows {
4275+ if row [1 ].(string ) != expectedNames [i ] {
4276+ t .Errorf ("Row %d: expected name='%s', got %v" , i , expectedNames [i ], row [1 ])
4277+ }
4278+ }
4279+ })
4280+
4281+ t .Run ("Positional parameter in LIMIT clause" , func (t * testing.T ) {
4282+ query := client .Query (fmt .Sprintf ("SELECT id, name FROM `%s.%s.%s` ORDER BY id LIMIT ?" , projectID , datasetID , tableID ))
4283+ query .Parameters = []bigquery.QueryParameter {
4284+ {Value : 2 },
4285+ }
4286+
4287+ it , err := query .Read (ctx )
4288+ if err != nil {
4289+ t .Fatalf ("Query failed: %v" , err )
4290+ }
4291+
4292+ var rows [][]bigquery.Value
4293+ for {
4294+ var row []bigquery.Value
4295+ if err := it .Next (& row ); err != nil {
4296+ if err == iterator .Done {
4297+ break
4298+ }
4299+ t .Fatal (err )
4300+ }
4301+ rows = append (rows , row )
4302+ }
4303+
4304+ if len (rows ) != 2 {
4305+ t .Fatalf ("Expected 2 rows, got %d" , len (rows ))
4306+ }
4307+ // Should get first 2 rows: Alice and Bob
4308+ if rows [0 ][1 ].(string ) != "Alice" {
4309+ t .Errorf ("Expected first row name='Alice', got %v" , rows [0 ][1 ])
4310+ }
4311+ if rows [1 ][1 ].(string ) != "Bob" {
4312+ t .Errorf ("Expected second row name='Bob', got %v" , rows [1 ][1 ])
4313+ }
4314+ })
4315+
4316+ t .Run ("Positional parameter with string type" , func (t * testing.T ) {
4317+ query := client .Query (fmt .Sprintf ("SELECT id, name FROM `%s.%s.%s` WHERE name = ? ORDER BY id" , projectID , datasetID , tableID ))
4318+ query .Parameters = []bigquery.QueryParameter {
4319+ {Value : "Bob" },
4320+ }
4321+
4322+ it , err := query .Read (ctx )
4323+ if err != nil {
4324+ t .Fatalf ("Query failed: %v" , err )
4325+ }
4326+
4327+ var rows [][]bigquery.Value
4328+ for {
4329+ var row []bigquery.Value
4330+ if err := it .Next (& row ); err != nil {
4331+ if err == iterator .Done {
4332+ break
4333+ }
4334+ t .Fatal (err )
4335+ }
4336+ rows = append (rows , row )
4337+ }
4338+
4339+ if len (rows ) != 1 {
4340+ t .Fatalf ("Expected 1 row, got %d" , len (rows ))
4341+ }
4342+ if rows [0 ][0 ].(int64 ) != 2 {
4343+ t .Errorf ("Expected id=2, got %v" , rows [0 ][0 ])
4344+ }
4345+ if rows [0 ][1 ].(string ) != "Bob" {
4346+ t .Errorf ("Expected name='Bob', got %v" , rows [0 ][1 ])
4347+ }
4348+ })
4349+ }
0 commit comments