Skip to content

Commit

Permalink
docs(spanner): document NULL value (#2885)
Browse files Browse the repository at this point in the history
* docs: values are zero when NULL

The contents of the value fields of the Null* structs when
Valid=false was not documented, although they would always be
the zero value of the type that they contained. This behavior
is also asserted by the tests in value_test.go.

Fixes #2879

* test: initialize to non-zero value
  • Loading branch information
olavloite committed Sep 18, 2020
1 parent 7f1057a commit 137d6d0
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 15 deletions.
28 changes: 14 additions & 14 deletions spanner/value.go
Expand Up @@ -121,8 +121,8 @@ type NullableValue interface {

// NullInt64 represents a Cloud Spanner INT64 that may be NULL.
type NullInt64 struct {
Int64 int64
Valid bool // Valid is true if Int64 is not NULL.
Int64 int64 // Int64 contains the value when it is non-NULL, and zero when NULL.
Valid bool // Valid is true if Int64 is not NULL.
}

// IsNull implements NullableValue.IsNull for NullInt64.
Expand Down Expand Up @@ -167,8 +167,8 @@ func (n *NullInt64) UnmarshalJSON(payload []byte) error {

// NullString represents a Cloud Spanner STRING that may be NULL.
type NullString struct {
StringVal string
Valid bool // Valid is true if StringVal is not NULL.
StringVal string // StringVal contains the value when it is non-NULL, and an empty string when NULL.
Valid bool // Valid is true if StringVal is not NULL.
}

// IsNull implements NullableValue.IsNull for NullString.
Expand Down Expand Up @@ -213,8 +213,8 @@ func (n *NullString) UnmarshalJSON(payload []byte) error {

// NullFloat64 represents a Cloud Spanner FLOAT64 that may be NULL.
type NullFloat64 struct {
Float64 float64
Valid bool // Valid is true if Float64 is not NULL.
Float64 float64 // Float64 contains the value when it is non-NULL, and zero when NULL.
Valid bool // Valid is true if Float64 is not NULL.
}

// IsNull implements NullableValue.IsNull for NullFloat64.
Expand Down Expand Up @@ -259,7 +259,7 @@ func (n *NullFloat64) UnmarshalJSON(payload []byte) error {

// NullBool represents a Cloud Spanner BOOL that may be NULL.
type NullBool struct {
Bool bool
Bool bool // Bool contains the value when it is non-NULL, and false when NULL.
Valid bool // Valid is true if Bool is not NULL.
}

Expand Down Expand Up @@ -305,8 +305,8 @@ func (n *NullBool) UnmarshalJSON(payload []byte) error {

// NullTime represents a Cloud Spanner TIMESTAMP that may be null.
type NullTime struct {
Time time.Time
Valid bool // Valid is true if Time is not NULL.
Time time.Time // Time contains the value when it is non-NULL, and a zero time.Time when NULL.
Valid bool // Valid is true if Time is not NULL.
}

// IsNull implements NullableValue.IsNull for NullTime.
Expand Down Expand Up @@ -356,8 +356,8 @@ func (n *NullTime) UnmarshalJSON(payload []byte) error {

// NullDate represents a Cloud Spanner DATE that may be null.
type NullDate struct {
Date civil.Date
Valid bool // Valid is true if Date is not NULL.
Date civil.Date // Date contains the value when it is non-NULL, and a zero civil.Date when NULL.
Valid bool // Valid is true if Date is not NULL.
}

// IsNull implements NullableValue.IsNull for NullDate.
Expand Down Expand Up @@ -407,8 +407,8 @@ func (n *NullDate) UnmarshalJSON(payload []byte) error {

// NullNumeric represents a Cloud Spanner Numeric that may be NULL.
type NullNumeric struct {
Numeric big.Rat
Valid bool // Valid is true if Numeric is not NULL.
Numeric big.Rat // Numeric contains the value when it is non-NULL, and a zero big.Rat when NULL.
Valid bool // Valid is true if Numeric is not NULL.
}

// IsNull implements NullableValue.IsNull for NullNumeric.
Expand Down Expand Up @@ -460,7 +460,7 @@ func (n *NullNumeric) UnmarshalJSON(payload []byte) error {
// See also the document for Row.
// Note that NullRow is not a valid Cloud Spanner column Type.
type NullRow struct {
Row Row
Row Row // Row contains the value when it is non-NULL, and a zero Row when NULL.
Valid bool // Valid is true if Row is not NULL.
}

Expand Down
21 changes: 20 additions & 1 deletion spanner/value_test.go
Expand Up @@ -1634,7 +1634,26 @@ func TestDecodeValue(t *testing.T) {
{desc: "decode DATE to CustomStructToDate", proto: dateProto(d1), protoType: dateType(), want: customStructToDate{"A", "B"}},
} {
gotp := reflect.New(reflect.TypeOf(test.want))
err := decodeValue(test.proto, test.protoType, gotp.Interface())
v := gotp.Interface()
// Initialize the input to a non-zero value to ensure that the decode
// method will override this with the actual value, or a zero value in
// case of a NULL.
switch nullValue := v.(type) {
case *NullString:
nullValue.StringVal = "foo"
case *NullInt64:
nullValue.Int64 = -100
case *NullFloat64:
nullValue.Float64 = 3.14
case *NullBool:
nullValue.Bool = true
case *NullTime:
nullValue.Time = time.Unix(100, 100)
case *NullDate:
nullValue.Date = civil.DateOf(time.Unix(100, 200))
default:
}
err := decodeValue(test.proto, test.protoType, v)
if test.wantErr {
if err == nil {
t.Errorf("%s: missing expected decode failure for %v(%v)", test.desc, test.proto, test.protoType)
Expand Down

0 comments on commit 137d6d0

Please sign in to comment.