From fe65afeb7f9d18aab37a4361fda00c3276d5e539 Mon Sep 17 00:00:00 2001 From: Lukas Gruber <72391764+gruberlu@users.noreply.github.com> Date: Wed, 9 Oct 2024 17:52:00 +0200 Subject: [PATCH 1/3] Recognise empty structs/arrays --- internal/contentdata/repository.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/internal/contentdata/repository.go b/internal/contentdata/repository.go index 7be8c3bc7..36432a038 100644 --- a/internal/contentdata/repository.go +++ b/internal/contentdata/repository.go @@ -152,9 +152,18 @@ func (r *Repository) Query(ctx context.Context, tx *connection.Tx, projectID, da values := []interface{}{} for _, param := range params { - value, err := r.queryParameterValueToGoValue(param.ParameterValue) - if err != nil { - return nil, err + var value interface{} + switch { + case param.ParameterType.Type == "ARRAY" && len(param.ParameterValue.ArrayValues) == 0: + value = []interface{}{} + case param.ParameterType.Type == "STRUCT" && len(param.ParameterValue.StructValues) == 0: + value = map[string]interface{}{} + default: + var err error + value, err = r.queryParameterValueToGoValue(param.ParameterValue) + if err != nil { + return nil, err + } } if param.Name != "" { values = append(values, sql.Named(param.Name, value)) From 165032110977a7973a17f80078913aa4dc97eb35 Mon Sep 17 00:00:00 2001 From: Lukas Gruber <72391764+gruberlu@users.noreply.github.com> Date: Thu, 10 Oct 2024 01:29:04 +0200 Subject: [PATCH 2/3] add test --- internal/contentdata/repository.go | 2 - server/server_test.go | 102 +++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 2 deletions(-) diff --git a/internal/contentdata/repository.go b/internal/contentdata/repository.go index 36432a038..e39c351a6 100644 --- a/internal/contentdata/repository.go +++ b/internal/contentdata/repository.go @@ -156,8 +156,6 @@ func (r *Repository) Query(ctx context.Context, tx *connection.Tx, projectID, da switch { case param.ParameterType.Type == "ARRAY" && len(param.ParameterValue.ArrayValues) == 0: value = []interface{}{} - case param.ParameterType.Type == "STRUCT" && len(param.ParameterValue.StructValues) == 0: - value = map[string]interface{}{} default: var err error value, err = r.queryParameterValueToGoValue(param.ParameterValue) diff --git a/server/server_test.go b/server/server_test.go index f75b3241b..d65165388 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -2344,6 +2344,108 @@ func TestExportToGCS(t *testing.T) { }) } +func TestQueryWithArrayParam(t *testing.T) { + const ( + projectID = "test" + datasetID = "test_dataset" + tableID = "test_table" + ) + + ctx := context.Background() + + bqServer, err := server.New(server.TempStorage) + if err != nil { + t.Fatal(err) + } + project := types.NewProject( + projectID, + types.NewDataset( + datasetID, + types.NewTable( + tableID, + []*types.Column{ + types.NewColumn("item", types.STRING), + }, + types.Data{ + { + "item": "something", + }, + }, + ), + ), + ) + if err := bqServer.Load(server.StructSource(project)); err != nil { + t.Fatal(err) + } + + testServer := bqServer.TestServer() + defer func() { + testServer.Close() + bqServer.Close() + }() + + client, err := bigquery.NewClient( + ctx, + projectID, + option.WithEndpoint(testServer.URL), + option.WithoutAuthentication(), + ) + if err != nil { + t.Fatal(err) + } + defer client.Close() + query := client.Query(` +SELECT + item +FROM test_dataset.test_table +WHERE + item IN UNNEST(@items) + OR item in UNNEST(@empty);`) + query.Parameters = []bigquery.QueryParameter{ + { + Name: "items", + Value: []string{"something"}, + }, + { + Name: "empty", + Value: []string{}, + }, + } + + job, err := query.Run(ctx) + if err != nil { + t.Fatal(err) + } + status, err := job.Wait(ctx) + if err != nil { + t.Fatal(err) + } + if err := status.Err(); err != nil { + t.Fatal(err) + } + it, err := job.Read(ctx) + if err != nil { + t.Fatal(err) + } + if it.TotalRows != 1 { + t.Fatal("expected 1 row, got", it.TotalRows) + } + var rs []bigquery.Value + for { + if err := it.Next(&rs); err != nil { + if err == iterator.Done { + break + } + if err != nil { + t.Fatal(err) + } + } + } + if rs[0] != "something" { + t.Fatal("expected 'something', got", rs) + } +} + func TestQueryWithNamedParams(t *testing.T) { const ( projectID = "test" From c46dbf0fdc53f23d5270658c6c188774ea2b3b7e Mon Sep 17 00:00:00 2001 From: Lukas Gruber <72391764+gruberlu@users.noreply.github.com> Date: Thu, 10 Oct 2024 01:29:46 +0200 Subject: [PATCH 3/3] fix behaviour --- internal/contentdata/repository.go | 2 +- server/server_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/contentdata/repository.go b/internal/contentdata/repository.go index e39c351a6..4deb7c830 100644 --- a/internal/contentdata/repository.go +++ b/internal/contentdata/repository.go @@ -155,7 +155,7 @@ func (r *Repository) Query(ctx context.Context, tx *connection.Tx, projectID, da var value interface{} switch { case param.ParameterType.Type == "ARRAY" && len(param.ParameterValue.ArrayValues) == 0: - value = []interface{}{} + value = nil default: var err error value, err = r.queryParameterValueToGoValue(param.ParameterValue) diff --git a/server/server_test.go b/server/server_test.go index d65165388..3d1483564 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -2400,7 +2400,7 @@ SELECT FROM test_dataset.test_table WHERE item IN UNNEST(@items) - OR item in UNNEST(@empty);`) + AND @empty IS NULL;`) query.Parameters = []bigquery.QueryParameter{ { Name: "items",