Skip to content

Commit

Permalink
Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
sfc-gh-pfus committed Dec 5, 2024
1 parent 44119de commit 1ca7cc3
Show file tree
Hide file tree
Showing 5 changed files with 26 additions and 31 deletions.
3 changes: 3 additions & 0 deletions converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,9 @@ func valueToString(v driver.Value, tsmode snowflakeType, params map[string]*stri
if value, err := valuer.Value(); err == nil && value != nil {
// if the output value is a valid string, return that
if strVal, ok := value.(string); ok {
if tsmode == objectType || tsmode == arrayType || tsmode == sliceType {
return bindingValue{&strVal, jsonFormatStr, nil}, nil
}
return bindingValue{&strVal, "", nil}, nil
}
}
Expand Down
13 changes: 6 additions & 7 deletions driver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,9 @@ func newTestUUID() testUUID {
}

func parseTestUUID(str string) testUUID {
if str == "" {
return testUUID{}
}
return testUUID{ParseUUID(str)}
}

Expand Down Expand Up @@ -926,19 +929,15 @@ func (uuid testUUID) Value() (driver.Value, error) {
}

func TestUUID(t *testing.T) {
t.Run("Basic Type", func(t *testing.T) {
testUuid(t, false, false)
})

t.Run("JSON", func(t *testing.T) {
testUuid(t, true, false)
testUUIDWithFormat(t, true, false)
})
t.Run("Arrow", func(t *testing.T) {
testUuid(t, false, true)
testUUIDWithFormat(t, false, true)
})
}

func testUuid(t *testing.T, json, arrow bool) {
func testUUIDWithFormat(t *testing.T, json, arrow bool) {
runDBTest(t, func(dbt *DBTest) {
if json {
dbt.mustExec(forceJSON)
Expand Down
5 changes: 5 additions & 0 deletions structured_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package gosnowflake
import (
"context"
"database/sql"
"database/sql/driver"
"encoding/hex"
"encoding/json"
"errors"
Expand Down Expand Up @@ -450,6 +451,10 @@ func buildSowcFromType(params map[string]*string, typ reflect.Type) (*structured
if err := childSowc.WriteNullableStruct(fieldName, nil, field.Type); err != nil {
return nil, err
}
} else if t.Implements(reflect.TypeOf((*driver.Valuer)(nil)).Elem()) {
if err := childSowc.WriteNullString(fieldName, sql.NullString{}); err != nil {
return nil, err
}

Check warning on line 457 in structured_type.go

View check run for this annotation

Codecov / codecov/patch

structured_type.go#L456-L457

Added lines #L456 - L457 were not covered by tests
} else {
return nil, fmt.Errorf("field %s has unsupported type", field.Name)
}
Expand Down
20 changes: 8 additions & 12 deletions structured_type_read_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ func TestObjectWithAllTypesNullable(t *testing.T) {
t.Run("null", func(t *testing.T) {
rows := dbt.mustQueryContextT(ctx, t, "select null, object_construct_keep_null('s', null, 'b', null, 'i16', null, 'i32', null, 'i64', null, 'f64', null, 'bo', null, 'bi', null, 'date', null, 'time', null, 'ltz', null, 'tz', null, 'ntz', null, 'so', null, 'sArr', null, 'f64Arr', null, 'someMap', null, 'uuid', null)::OBJECT(s VARCHAR, b TINYINT, i16 SMALLINT, i32 INTEGER, i64 BIGINT, f64 DOUBLE, bo BOOLEAN, bi BINARY, date DATE, time TIME, ltz TIMESTAMP_LTZ, tz TIMESTAMP_TZ, ntz TIMESTAMP_NTZ, so OBJECT(s VARCHAR, i INTEGER), sArr ARRAY(VARCHAR), f64Arr ARRAY(DOUBLE), someMap MAP(VARCHAR, BOOLEAN), uuid VARCHAR)")
defer rows.Close()
rows.Next()
assertTrueF(t, rows.Next())
var ignore sql.NullInt32
var res objectWithAllTypesNullable
err := rows.Scan(&ignore, &res)
Expand All @@ -501,10 +501,11 @@ func TestObjectWithAllTypesNullable(t *testing.T) {
assertEqualE(t, res.ntz, sql.NullTime{Valid: false})
var so *simpleObject
assertDeepEqualE(t, res.so, so)
assertEqualE(t, res.uuid, testUUID{})
})
t.Run("not null", func(t *testing.T) {
uid := newTestUUID()
rows := dbt.mustQueryContextT(ctx, t, fmt.Sprintf("select 1, object_construct_keep_null('s', 'abc', 'b', 1, 'i16', 2, 'i32', 3, 'i64', 9223372036854775807, 'f64', 2.2, 'bo', true, 'bi', TO_BINARY('616263', 'HEX'), 'date', '2024-03-21'::DATE, 'time', '13:03:02'::TIME, 'ltz', '2021-07-21 11:22:33'::TIMESTAMP_LTZ, 'tz', '2022-08-31 13:43:22 +0200'::TIMESTAMP_TZ, 'ntz', '2023-05-22 01:17:19'::TIMESTAMP_NTZ, 'so', {'s': 'child', 'i': 9}::OBJECT, 'sArr', ARRAY_CONSTRUCT('x', 'y', 'z'), 'f64Arr', ARRAY_CONSTRUCT(1.1, 2.2, 3.3), 'someMap', {'x': true, 'y': false}, 'uuid', '%s')::OBJECT(s VARCHAR, b TINYINT, i16 SMALLINT, i32 INTEGER, i64 BIGINT, f64 DOUBLE, bo BOOLEAN, bi BINARY, date DATE, time TIME, ltz TIMESTAMP_LTZ, tz TIMESTAMP_TZ, ntz TIMESTAMP_NTZ, so OBJECT(s VARCHAR, i INTEGER), sArr ARRAY(VARCHAR), f64Arr ARRAY(DOUBLE), someMap MAP(VARCHAR, BOOLEAN), uuid VARCHAR)", uid))
uuid := newTestUUID()
rows := dbt.mustQueryContextT(ctx, t, fmt.Sprintf("select 1, object_construct_keep_null('s', 'abc', 'b', 1, 'i16', 2, 'i32', 3, 'i64', 9223372036854775807, 'f64', 2.2, 'bo', true, 'bi', TO_BINARY('616263', 'HEX'), 'date', '2024-03-21'::DATE, 'time', '13:03:02'::TIME, 'ltz', '2021-07-21 11:22:33'::TIMESTAMP_LTZ, 'tz', '2022-08-31 13:43:22 +0200'::TIMESTAMP_TZ, 'ntz', '2023-05-22 01:17:19'::TIMESTAMP_NTZ, 'so', {'s': 'child', 'i': 9}::OBJECT, 'sArr', ARRAY_CONSTRUCT('x', 'y', 'z'), 'f64Arr', ARRAY_CONSTRUCT(1.1, 2.2, 3.3), 'someMap', {'x': true, 'y': false}, 'uuid', '%s')::OBJECT(s VARCHAR, b TINYINT, i16 SMALLINT, i32 INTEGER, i64 BIGINT, f64 DOUBLE, bo BOOLEAN, bi BINARY, date DATE, time TIME, ltz TIMESTAMP_LTZ, tz TIMESTAMP_TZ, ntz TIMESTAMP_NTZ, so OBJECT(s VARCHAR, i INTEGER), sArr ARRAY(VARCHAR), f64Arr ARRAY(DOUBLE), someMap MAP(VARCHAR, BOOLEAN), uuid VARCHAR)", uuid))
defer rows.Close()
rows.Next()
var ignore sql.NullInt32
Expand Down Expand Up @@ -535,7 +536,7 @@ func TestObjectWithAllTypesNullable(t *testing.T) {
assertDeepEqualE(t, res.sArr, []string{"x", "y", "z"})
assertDeepEqualE(t, res.f64Arr, []float64{1.1, 2.2, 3.3})
assertDeepEqualE(t, res.someMap, map[string]bool{"x": true, "y": false})
assertEqualE(t, res.uuid.String(), uid.String())
assertEqualE(t, res.uuid.String(), uuid.String())
})
})
})
Expand All @@ -561,7 +562,6 @@ type objectWithAllTypesSimpleScan struct {
SArr []string
F64Arr []float64
SomeMap map[string]bool
UUID testUUID
}

func (so *objectWithAllTypesSimpleScan) Scan(val any) error {
Expand Down Expand Up @@ -613,7 +613,6 @@ func TestObjectWithAllTypesSimpleScan(t *testing.T) {
assertDeepEqualE(t, res.SArr, []string{"x", "y", "z"})
assertDeepEqualE(t, res.F64Arr, []float64{1.1, 2.2, 3.3})
assertDeepEqualE(t, res.SomeMap, map[string]bool{"x": true, "y": false})
assertEqualE(t, res.UUID.String(), uid.String())
})
})
}
Expand Down Expand Up @@ -663,7 +662,6 @@ type objectWithAllTypesNullableSimpleScan struct {
SArr []string
F64Arr []float64
SomeMap map[string]bool
UUID testUUID
}

func (o *objectWithAllTypesNullableSimpleScan) Scan(val any) error {
Expand All @@ -687,7 +685,7 @@ func TestObjectWithAllTypesSimpleScanNullable(t *testing.T) {
dbt.mustExec("ALTER SESSION SET TIMEZONE = 'Europe/Warsaw'")
forAllStructureTypeFormats(dbt, func(t *testing.T, format string) {
t.Run("null", func(t *testing.T) {
rows := dbt.mustQueryContextT(ctx, t, "select null, object_construct_keep_null('s', null, 'b', null, 'i16', null, 'i32', null, 'i64', null, 'f64', null, 'bo', null, 'bi', null, 'date', null, 'time', null, 'ltz', null, 'tz', null, 'ntz', null, 'so', null, 'sArr', null, 'f64Arr', null, 'someMap', null, 'uuid', null)::OBJECT(s VARCHAR, b TINYINT, i16 SMALLINT, i32 INTEGER, i64 BIGINT, f64 DOUBLE, bo BOOLEAN, bi BINARY, date DATE, time TIME, ltz TIMESTAMP_LTZ, tz TIMESTAMP_TZ, ntz TIMESTAMP_NTZ, so OBJECT(s VARCHAR, i INTEGER), sArr ARRAY(VARCHAR), f64Arr ARRAY(DOUBLE), someMap MAP(VARCHAR, BOOLEAN), uuid VARCHAR)")
rows := dbt.mustQueryContextT(ctx, t, "select null, object_construct_keep_null('s', null, 'b', null, 'i16', null, 'i32', null, 'i64', null, 'f64', null, 'bo', null, 'bi', null, 'date', null, 'time', null, 'ltz', null, 'tz', null, 'ntz', null, 'so', null, 'sArr', null, 'f64Arr', null, 'someMap', null)::OBJECT(s VARCHAR, b TINYINT, i16 SMALLINT, i32 INTEGER, i64 BIGINT, f64 DOUBLE, bo BOOLEAN, bi BINARY, date DATE, time TIME, ltz TIMESTAMP_LTZ, tz TIMESTAMP_TZ, ntz TIMESTAMP_NTZ, so OBJECT(s VARCHAR, i INTEGER), sArr ARRAY(VARCHAR), f64Arr ARRAY(DOUBLE), someMap MAP(VARCHAR, BOOLEAN))")
defer rows.Close()
rows.Next()
var ignore sql.NullInt32
Expand All @@ -710,11 +708,10 @@ func TestObjectWithAllTypesSimpleScanNullable(t *testing.T) {
assertEqualE(t, res.Ntz, sql.NullTime{Valid: false})
var so *simpleObject
assertDeepEqualE(t, res.So, so)
assertEqualE(t, res.UUID, []string(nil))
})
t.Run("not null", func(t *testing.T) {
uid := newTestUUID()
rows := dbt.mustQueryContextT(ctx, t, fmt.Sprintf("select 1, object_construct_keep_null('s', 'abc', 'b', 1, 'i16', 2, 'i32', 3, 'i64', 9223372036854775807, 'f64', 2.2, 'bo', true, 'bi', TO_BINARY('616263', 'HEX'), 'date', '2024-03-21'::DATE, 'time', '13:03:02'::TIME, 'ltz', '2021-07-21 11:22:33'::TIMESTAMP_LTZ, 'tz', '2022-08-31 13:43:22 +0200'::TIMESTAMP_TZ, 'ntz', '2023-05-22 01:17:19'::TIMESTAMP_NTZ, 'so', {'s': 'child', 'i': 9}::OBJECT, 'sArr', ARRAY_CONSTRUCT('x', 'y', 'z'), 'f64Arr', ARRAY_CONSTRUCT(1.1, 2.2, 3.3), 'someMap', {'x': true, 'y': false}, 'uuid', '%s')::OBJECT(s VARCHAR, b TINYINT, i16 SMALLINT, i32 INTEGER, i64 BIGINT, f64 DOUBLE, bo BOOLEAN, bi BINARY, date DATE, time TIME, ltz TIMESTAMP_LTZ, tz TIMESTAMP_TZ, ntz TIMESTAMP_NTZ, so OBJECT(s VARCHAR, i INTEGER), sArr ARRAY(VARCHAR), f64Arr ARRAY(DOUBLE), someMap MAP(VARCHAR, BOOLEAN), uuid VARCHAR)", uid))
uuid := newTestUUID()
rows := dbt.mustQueryContextT(ctx, t, fmt.Sprintf("select 1, object_construct_keep_null('s', 'abc', 'b', 1, 'i16', 2, 'i32', 3, 'i64', 9223372036854775807, 'f64', 2.2, 'bo', true, 'bi', TO_BINARY('616263', 'HEX'), 'date', '2024-03-21'::DATE, 'time', '13:03:02'::TIME, 'ltz', '2021-07-21 11:22:33'::TIMESTAMP_LTZ, 'tz', '2022-08-31 13:43:22 +0200'::TIMESTAMP_TZ, 'ntz', '2023-05-22 01:17:19'::TIMESTAMP_NTZ, 'so', {'s': 'child', 'i': 9}::OBJECT, 'sArr', ARRAY_CONSTRUCT('x', 'y', 'z'), 'f64Arr', ARRAY_CONSTRUCT(1.1, 2.2, 3.3), 'someMap', {'x': true, 'y': false}, 'uuid', '%s')::OBJECT(s VARCHAR, b TINYINT, i16 SMALLINT, i32 INTEGER, i64 BIGINT, f64 DOUBLE, bo BOOLEAN, bi BINARY, date DATE, time TIME, ltz TIMESTAMP_LTZ, tz TIMESTAMP_TZ, ntz TIMESTAMP_NTZ, so OBJECT(s VARCHAR, i INTEGER), sArr ARRAY(VARCHAR), f64Arr ARRAY(DOUBLE), someMap MAP(VARCHAR, BOOLEAN), uuid VARCHAR)", uuid))
defer rows.Close()
rows.Next()
var ignore sql.NullInt32
Expand Down Expand Up @@ -745,7 +742,6 @@ func TestObjectWithAllTypesSimpleScanNullable(t *testing.T) {
assertDeepEqualE(t, res.SArr, []string{"x", "y", "z"})
assertDeepEqualE(t, res.F64Arr, []float64{1.1, 2.2, 3.3})
assertDeepEqualE(t, res.SomeMap, map[string]bool{"x": true, "y": false})
assertEqualE(t, res.UUID.String(), uid.String())
})
})
})
Expand Down
16 changes: 4 additions & 12 deletions structured_type_write_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ func TestBindingObjectWithSchema(t *testing.T) {
assertDeepEqualE(t, res.sArr, o.sArr)
assertDeepEqualE(t, res.f64Arr, o.f64Arr)
assertDeepEqualE(t, res.someMap, o.someMap)
assertEqualE(t, res.uuid, o.uuid)
assertEqualE(t, res.uuid.String(), o.uuid.String())
})
}

Expand Down Expand Up @@ -254,7 +254,7 @@ func TestBindingObjectWithNullableFieldsWithSchema(t *testing.T) {
assertDeepEqualE(t, res.sArr, o.sArr)
assertDeepEqualE(t, res.f64Arr, o.f64Arr)
assertDeepEqualE(t, res.someMap, o.someMap)
assertEqualE(t, res.uuid, o.uuid)
assertEqualE(t, res.uuid.String(), o.uuid.String())
})
t.Run("null", func(t *testing.T) {
o := &objectWithAllTypesNullable{
Expand All @@ -275,7 +275,6 @@ func TestBindingObjectWithNullableFieldsWithSchema(t *testing.T) {
sArr: nil,
f64Arr: nil,
someMap: nil,
uuid: testUUID{},
}
dbt.mustExecT(t, "INSERT INTO test_object_binding SELECT (?)", o)
rows := dbt.mustQueryContextT(ctx, t, "SELECT * FROM test_object_binding WHERE obj = ?", o)
Expand Down Expand Up @@ -304,7 +303,6 @@ func TestBindingObjectWithNullableFieldsWithSchema(t *testing.T) {
assertDeepEqualE(t, res.sArr, o.sArr)
assertDeepEqualE(t, res.f64Arr, o.f64Arr)
assertDeepEqualE(t, res.someMap, o.someMap)
assertEqualE(t, res.uuid, o.uuid)
})
})
}
Expand All @@ -314,7 +312,7 @@ func TestBindingObjectWithSchemaSimpleWrite(t *testing.T) {
assertNilF(t, err)
ctx := WithStructuredTypesEnabled(context.Background())
runDBTest(t, func(dbt *DBTest) {
dbt.mustExec("CREATE OR REPLACE TABLE test_object_binding (obj OBJECT(s VARCHAR, b TINYINT, i16 SMALLINT, i32 INTEGER, i64 BIGINT, f32 FLOAT, f64 DOUBLE, nfraction NUMBER(38, 9), bo BOOLEAN, bi BINARY, date DATE, time TIME, ltz TIMESTAMP_LTZ, tz TIMESTAMP_TZ, ntz TIMESTAMP_NTZ, so OBJECT(s VARCHAR, i INTEGER), sArr ARRAY(VARCHAR), f64Arr ARRAY(DOUBLE), someMap MAP(VARCHAR, BOOLEAN), uuid VARCHAR))")
dbt.mustExec("CREATE OR REPLACE TABLE test_object_binding (obj OBJECT(s VARCHAR, b TINYINT, i16 SMALLINT, i32 INTEGER, i64 BIGINT, f32 FLOAT, f64 DOUBLE, nfraction NUMBER(38, 9), bo BOOLEAN, bi BINARY, date DATE, time TIME, ltz TIMESTAMP_LTZ, tz TIMESTAMP_TZ, ntz TIMESTAMP_NTZ, so OBJECT(s VARCHAR, i INTEGER), sArr ARRAY(VARCHAR), f64Arr ARRAY(DOUBLE), someMap MAP(VARCHAR, BOOLEAN)))")
defer func() {
dbt.mustExec("DROP TABLE IF EXISTS test_object_binding")
}()
Expand All @@ -341,7 +339,6 @@ func TestBindingObjectWithSchemaSimpleWrite(t *testing.T) {
SArr: []string{"a", "b"},
F64Arr: []float64{1.1, 2.2},
SomeMap: map[string]bool{"a": true, "b": false},
UUID: newTestUUID(),
}
dbt.mustExecT(t, "INSERT INTO test_object_binding SELECT (?)", o)
rows := dbt.mustQueryContextT(ctx, t, "SELECT * FROM test_object_binding WHERE obj = ?", o)
Expand Down Expand Up @@ -372,7 +369,6 @@ func TestBindingObjectWithSchemaSimpleWrite(t *testing.T) {
assertDeepEqualE(t, res.SArr, o.SArr)
assertDeepEqualE(t, res.F64Arr, o.F64Arr)
assertDeepEqualE(t, res.SomeMap, o.SomeMap)
assertEqualE(t, res.UUID, o.UUID)
})
}

Expand All @@ -382,7 +378,7 @@ func TestBindingObjectWithNullableFieldsWithSchemaSimpleWrite(t *testing.T) {
ctx := WithStructuredTypesEnabled(context.Background())
runDBTest(t, func(dbt *DBTest) {
dbt.forceJSON()
dbt.mustExec("CREATE OR REPLACE TABLE test_object_binding (obj OBJECT(s VARCHAR, b TINYINT, i16 SMALLINT, i32 INTEGER, i64 BIGINT, f64 DOUBLE, bo boolean, bi BINARY, date DATE, time TIME, ltz TIMESTAMPLTZ, tz TIMESTAMPTZ, ntz TIMESTAMPNTZ, so OBJECT(s VARCHAR, i INTEGER), sArr ARRAY(VARCHAR), f64Arr ARRAY(DOUBLE), someMap MAP(VARCHAR, BOOLEAN), uuid VARCHAR))")
dbt.mustExec("CREATE OR REPLACE TABLE test_object_binding (obj OBJECT(s VARCHAR, b TINYINT, i16 SMALLINT, i32 INTEGER, i64 BIGINT, f64 DOUBLE, bo boolean, bi BINARY, date DATE, time TIME, ltz TIMESTAMPLTZ, tz TIMESTAMPTZ, ntz TIMESTAMPNTZ, so OBJECT(s VARCHAR, i INTEGER), sArr ARRAY(VARCHAR), f64Arr ARRAY(DOUBLE), someMap MAP(VARCHAR, BOOLEAN)))")
defer func() {
dbt.mustExec("DROP TABLE IF EXISTS test_object_binding")
}()
Expand All @@ -408,7 +404,6 @@ func TestBindingObjectWithNullableFieldsWithSchemaSimpleWrite(t *testing.T) {
SArr: []string{"a", "b"},
F64Arr: []float64{1.1, 2.2},
SomeMap: map[string]bool{"a": true, "b": false},
UUID: newTestUUID(),
}
dbt.mustExecT(t, "INSERT INTO test_object_binding SELECT (?)", o)
rows := dbt.mustQueryContextT(ctx, t, "SELECT * FROM test_object_binding WHERE obj = ?", o)
Expand Down Expand Up @@ -437,7 +432,6 @@ func TestBindingObjectWithNullableFieldsWithSchemaSimpleWrite(t *testing.T) {
assertDeepEqualE(t, res.SArr, o.SArr)
assertDeepEqualE(t, res.F64Arr, o.F64Arr)
assertDeepEqualE(t, res.SomeMap, o.SomeMap)
assertEqualE(t, res.UUID, o.UUID)
})
t.Run("null", func(t *testing.T) {
o := &objectWithAllTypesNullableSimpleScan{
Expand All @@ -458,7 +452,6 @@ func TestBindingObjectWithNullableFieldsWithSchemaSimpleWrite(t *testing.T) {
SArr: nil,
F64Arr: nil,
SomeMap: nil,
UUID: testUUID{},
}
dbt.mustExecT(t, "INSERT INTO test_object_binding SELECT (?)", o)
rows := dbt.mustQueryContextT(ctx, t, "SELECT * FROM test_object_binding WHERE obj = ?", o)
Expand Down Expand Up @@ -487,7 +480,6 @@ func TestBindingObjectWithNullableFieldsWithSchemaSimpleWrite(t *testing.T) {
assertDeepEqualE(t, res.SArr, o.SArr)
assertDeepEqualE(t, res.F64Arr, o.F64Arr)
assertDeepEqualE(t, res.SomeMap, o.SomeMap)
assertEqualE(t, res.UUID, o.UUID)
})
})
}
Expand Down

0 comments on commit 1ca7cc3

Please sign in to comment.