Skip to content

Commit

Permalink
fix(bigquery): empty slice instead of nil slice for primitive repeate…
Browse files Browse the repository at this point in the history
…d fields (googleapis#7315)
  • Loading branch information
alvarowolfx committed May 17, 2024
1 parent d481e0e commit b371210
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 10 deletions.
32 changes: 32 additions & 0 deletions bigquery/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"math/big"
"net/http"
"os"
"reflect"
"sort"
"strings"
"testing"
Expand Down Expand Up @@ -2527,6 +2528,37 @@ func TestIntegration_QueryParameters(t *testing.T) {
}
}

func TestIntegration_QueryEmptyArrays(t *testing.T) {
if client == nil {
t.Skip("Integration tests skipped")
}
ctx := context.Background()

q := client.Query("SELECT ARRAY<string>[] as a, ARRAY<STRUCT<name string>>[] as b")
it, err := q.Read(ctx)
if err != nil {
t.Fatal(err)
}
for {
vals := map[string]Value{}
if err := it.Next(&vals); err != nil {
if errors.Is(err, iterator.Done) {
break
}
}

valueOfA := reflect.ValueOf(vals["a"])
if testutil.Equal(vals["a"], nil) || valueOfA.IsNil() {
t.Fatalf("expected empty string array to not return nil, but found %v %v %T", valueOfA, vals["a"], vals["a"])
}

valueOfB := reflect.ValueOf(vals["b"])
if testutil.Equal(vals["b"], nil) || valueOfB.IsNil() {
t.Fatalf("expected empty struct array to not return nil, but found %v %v %T", valueOfB, vals["b"], vals["b"])
}
}
}

// This test can be merged with the TestIntegration_QueryParameters as soon as support for explicit typed query parameter lands.
// To test timestamps with different formats, we need to be able to specify the type explicitly.
func TestIntegration_TimestampFormat(t *testing.T) {
Expand Down
8 changes: 6 additions & 2 deletions bigquery/iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,12 @@ type pageFetcher func(ctx context.Context, _ *rowSource, _ Schema, startIndex ui
// See https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#numeric-type
// for more on NUMERIC.
//
// A repeated field corresponds to a slice or array of the element type. A STRUCT
// type (RECORD or nested schema) corresponds to a nested struct or struct pointer.
// A repeated field corresponds to a slice or array of the element type. BigQuery translates
// NULL arrays into an empty array, so we follow that behavior.
// See https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#array_nulls
// for more about NULL and empty arrays.
//
// A STRUCT type (RECORD or nested schema) corresponds to a nested struct or struct pointer.
// All calls to Next on the same iterator must use the same struct type.
//
// It is an error to attempt to read a BigQuery NULL value into a struct field,
Expand Down
3 changes: 3 additions & 0 deletions bigquery/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ func loadMap(m map[string]Value, vals []Value, s Schema) {
}
v = vs
}
if f.Repeated && (v == nil || reflect.ValueOf(v).IsNil()) {
v = []Value{}
}

m[f.Name] = v
}
Expand Down
20 changes: 12 additions & 8 deletions bigquery/value_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -825,10 +825,12 @@ func TestValueMap(t *testing.T) {
{Name: "i", Type: IntegerFieldType},
{Name: "f", Type: FloatFieldType},
{Name: "b", Type: BooleanFieldType},
{Name: "n", Type: RecordFieldType, Schema: ns},
{Name: "sn", Type: StringFieldType, Repeated: true},
{Name: "r", Type: RecordFieldType, Schema: ns},
{Name: "rn", Type: RecordFieldType, Schema: ns, Repeated: true},
}
in := []Value{"x", 7, 3.14, true,
[]Value{"a", "b"},
[]Value{1, 2},
[]Value{[]Value{3, 4}, []Value{5, 6}},
}
Expand All @@ -837,11 +839,12 @@ func TestValueMap(t *testing.T) {
t.Fatal(err)
}
want := map[string]Value{
"s": "x",
"i": 7,
"f": 3.14,
"b": true,
"n": map[string]Value{"x": 1, "y": 2},
"s": "x",
"i": 7,
"f": 3.14,
"b": true,
"sn": []Value{"a", "b"},
"r": map[string]Value{"x": 1, "y": 2},
"rn": []Value{
map[string]Value{"x": 3, "y": 4},
map[string]Value{"x": 5, "y": 6},
Expand All @@ -857,8 +860,9 @@ func TestValueMap(t *testing.T) {
"i": nil,
"f": nil,
"b": nil,
"n": nil,
"rn": nil,
"sn": []Value{},
"r": nil,
"rn": []Value{},
}
var vm2 valueMap
if err := vm2.Load(in, schema); err != nil {
Expand Down

0 comments on commit b371210

Please sign in to comment.