Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Typing] Support different Redshift Integers #926

Merged
merged 5 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 30 additions & 5 deletions clients/redshift/dialect/dialect.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,20 @@ func (RedshiftDialect) EscapeStruct(value string) string {
func (RedshiftDialect) DataTypeForKind(kd typing.KindDetails, _ bool) string {
switch kd.Kind {
case typing.Integer.Kind:
// int4 is 2^31, whereas int8 is 2^63.
// we're using a larger data type to not have an integer overflow.
if kd.OptionalIntegerKind != nil {
switch *kd.OptionalIntegerKind {
case typing.SmallIntegerKind:
return "INT2"
case typing.IntegerKind:
return "INT4"
case typing.NotSpecifiedKind, typing.BigIntegerKind:
fallthrough
default:
// By default, we are using a larger data type to avoid the possibility of an integer overflow.
return "INT8"
}
}

return "INT8"
case typing.Struct.Kind:
return "SUPER"
Expand Down Expand Up @@ -87,8 +99,21 @@ func (RedshiftDialect) KindForDataType(rawType string, stringPrecision string) (
switch rawType {
case "super":
return typing.Struct, nil
case "smallint", "integer", "bigint":
return typing.Integer, nil
case "smallint":
return typing.KindDetails{
Kind: typing.Integer.Kind,
OptionalIntegerKind: typing.ToPtr(typing.SmallIntegerKind),
}, nil
case "integer":
return typing.KindDetails{
Kind: typing.Integer.Kind,
OptionalIntegerKind: typing.ToPtr(typing.IntegerKind),
}, nil
case "bigint":
return typing.KindDetails{
Kind: typing.Integer.Kind,
OptionalIntegerKind: typing.ToPtr(typing.BigIntegerKind),
}, nil
case "double precision":
return typing.Float, nil
case "timestamp with time zone", "timestamp without time zone":
Expand All @@ -101,7 +126,7 @@ func (RedshiftDialect) KindForDataType(rawType string, stringPrecision string) (
return typing.Boolean, nil
}

return typing.Invalid, fmt.Errorf("unsupported data type: %s", rawType)
return typing.Invalid, fmt.Errorf("unsupported data type: %q", rawType)
}

func (RedshiftDialect) IsColumnAlreadyExistsErr(err error) bool {
Expand Down
50 changes: 43 additions & 7 deletions clients/redshift/dialect/dialect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,62 @@ func TestRedshiftDialect_DataTypeForKind(t *testing.T) {
assert.Equal(t, "VARCHAR(12345)", RedshiftDialect{}.DataTypeForKind(typing.KindDetails{Kind: typing.String.Kind, OptionalStringPrecision: typing.ToPtr(int32(12345))}, false))
}
}
{
// Integers
{
// Small int
assert.Equal(t, "INT2", RedshiftDialect{}.DataTypeForKind(typing.KindDetails{Kind: typing.Integer.Kind, OptionalIntegerKind: typing.ToPtr(typing.SmallIntegerKind)}, false))
}
{
// Integer
assert.Equal(t, "INT4", RedshiftDialect{}.DataTypeForKind(typing.KindDetails{Kind: typing.Integer.Kind, OptionalIntegerKind: typing.ToPtr(typing.IntegerKind)}, false))
}
{
// Big integer
assert.Equal(t, "INT8", RedshiftDialect{}.DataTypeForKind(typing.KindDetails{Kind: typing.Integer.Kind, OptionalIntegerKind: typing.ToPtr(typing.BigIntegerKind)}, false))
}
{
// Not specified
{
// Literal
assert.Equal(t, "INT8", RedshiftDialect{}.DataTypeForKind(typing.KindDetails{Kind: typing.Integer.Kind, OptionalIntegerKind: typing.ToPtr(typing.NotSpecifiedKind)}, false))
}
{
assert.Equal(t, "INT8", RedshiftDialect{}.DataTypeForKind(typing.Integer, false))
}
}
}
}

func TestRedshiftDialect_KindForDataType(t *testing.T) {
dialect := RedshiftDialect{}
{
// Integers
{
kd, err := dialect.KindForDataType("integer", "")
// Small integer
kd, err := dialect.KindForDataType("smallint", "")
assert.NoError(t, err)
assert.Equal(t, typing.Integer, kd)
assert.Equal(t, typing.KindDetails{Kind: typing.Integer.Kind, OptionalIntegerKind: typing.ToPtr(typing.SmallIntegerKind)}, kd)
}
{
kd, err := dialect.KindForDataType("bigint", "")
assert.NoError(t, err)
assert.Equal(t, typing.Integer, kd)
{
// Regular integers (upper)
kd, err := dialect.KindForDataType("INTEGER", "")
assert.NoError(t, err)
assert.Equal(t, typing.KindDetails{Kind: typing.Integer.Kind, OptionalIntegerKind: typing.ToPtr(typing.IntegerKind)}, kd)
}
{
// Regular integers (lower)
kd, err := dialect.KindForDataType("integer", "")
assert.NoError(t, err)
assert.Equal(t, typing.KindDetails{Kind: typing.Integer.Kind, OptionalIntegerKind: typing.ToPtr(typing.IntegerKind)}, kd)
}
}
{
kd, err := dialect.KindForDataType("INTEGER", "")
// Big integer
kd, err := dialect.KindForDataType("bigint", "")
assert.NoError(t, err)
assert.Equal(t, typing.Integer, kd)
assert.Equal(t, typing.KindDetails{Kind: typing.Integer.Kind, OptionalIntegerKind: typing.ToPtr(typing.BigIntegerKind)}, kd)
}
}
{
Expand Down
5 changes: 5 additions & 0 deletions lib/optimization/table_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,11 @@ func (t *TableData) MergeColumnsFromDestination(destCols ...columns.Column) erro
inMemoryCol.KindDetails.OptionalStringPrecision = foundColumn.KindDetails.OptionalStringPrecision
}

// Copy over integer kind, if exists.
if foundColumn.KindDetails.OptionalIntegerKind != nil {
inMemoryCol.KindDetails.OptionalIntegerKind = foundColumn.KindDetails.OptionalIntegerKind
}

// Copy over the time details
if foundColumn.KindDetails.ExtendedTimeDetails != nil {
if inMemoryCol.KindDetails.ExtendedTimeDetails == nil {
Expand Down
15 changes: 12 additions & 3 deletions lib/typing/typing.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,23 @@ import (
"github.com/artie-labs/transfer/lib/typing/ext"
)

type OptionalIntegerKind int

const (
NotSpecifiedKind OptionalIntegerKind = iota
SmallIntegerKind
IntegerKind
BigIntegerKind
)

type KindDetails struct {
Kind string
ExtendedTimeDetails *ext.NestedKind
ExtendedDecimalDetails *decimal.Details

// Optional kind details metadata
OptionalStringPrecision *int32
OptionalIntegerKind *OptionalIntegerKind
}

func (k *KindDetails) EnsureExtendedTimeDetails() error {
Expand All @@ -26,8 +36,6 @@ func (k *KindDetails) EnsureExtendedTimeDetails() error {
return nil
}

// Summarized this from Snowflake + Reflect.
// In the future, we can support Geo objects.
var (
Invalid = KindDetails{
Kind: "invalid",
Expand All @@ -38,7 +46,8 @@ var (
}

Integer = KindDetails{
Kind: "int",
Kind: "int",
OptionalIntegerKind: ToPtr(NotSpecifiedKind),
}

EDecimal = KindDetails{
Expand Down