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

Support null in Airbyte schema column type #123

Merged
merged 5 commits into from
Feb 20, 2025
Merged
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
2 changes: 1 addition & 1 deletion cmd/airbyte-source/read.go
Original file line number Diff line number Diff line change
@@ -64,7 +64,7 @@ func ReadCommand(ch *Helper) *cobra.Command {

catalog, err := readCatalog(readSourceCatalogPath)
if err != nil {
ch.Logger.Error("Unable to read catalog")
ch.Logger.Error(fmt.Sprintf("Unable to read catalog: %+v", err))
os.Exit(1)
}

4 changes: 2 additions & 2 deletions cmd/airbyte-source/read_test.go
Original file line number Diff line number Diff line change
@@ -28,7 +28,7 @@ func TestRead_StartingGtidsAndState(t *testing.T) {
Type: "object",
Properties: map[string]internal.PropertyType{
"id": {
Type: "number",
Type: []string{"number"},
AirbyteType: "integer",
},
},
@@ -55,7 +55,7 @@ func TestRead_StartingGtidsAndState(t *testing.T) {
Type: "object",
Properties: map[string]internal.PropertyType{
"id": {
Type: "number",
Type: []string{"number"},
AirbyteType: "integer",
},
},
27 changes: 18 additions & 9 deletions cmd/internal/planetscale_edge_database.go
Original file line number Diff line number Diff line change
@@ -132,28 +132,37 @@ func (p PlanetScaleEdgeDatabase) getStreamForTable(ctx context.Context, psc Plan
}

// Convert columnType to Airbyte type.
func getJsonSchemaType(mysqlType string, treatTinyIntAsBoolean bool) PropertyType {
func getJsonSchemaType(mysqlType string, treatTinyIntAsBoolean bool, nullable string) PropertyType {
// Support custom airbyte types documented here :
// https://docs.airbyte.com/understanding-airbyte/supported-data-types/#the-types
var propertyType PropertyType

switch {
case strings.HasPrefix(mysqlType, "tinyint(1)"):
if treatTinyIntAsBoolean {
return PropertyType{Type: "boolean"}
propertyType = PropertyType{Type: []string{"boolean"}}
} else {
propertyType = PropertyType{Type: []string{"number"}, AirbyteType: "integer"}
}
return PropertyType{Type: "number", AirbyteType: "integer"}
case strings.HasPrefix(mysqlType, "int"), strings.HasPrefix(mysqlType, "smallint"), strings.HasPrefix(mysqlType, "mediumint"), strings.HasPrefix(mysqlType, "bigint"), strings.HasPrefix(mysqlType, "tinyint"):
return PropertyType{Type: "number", AirbyteType: "integer"}
propertyType = PropertyType{Type: []string{"number"}, AirbyteType: "integer"}
case strings.HasPrefix(mysqlType, "decimal"), strings.HasPrefix(mysqlType, "double"), strings.HasPrefix(mysqlType, "float"):
return PropertyType{Type: "number"}
propertyType = PropertyType{Type: []string{"number"}}
case strings.HasPrefix(mysqlType, "datetime"), strings.HasPrefix(mysqlType, "timestamp"):
return PropertyType{Type: "string", CustomFormat: "date-time", AirbyteType: "timestamp_without_timezone"}
propertyType = PropertyType{Type: []string{"string"}, CustomFormat: "date-time", AirbyteType: "timestamp_without_timezone"}
case strings.HasPrefix(mysqlType, "date"):
return PropertyType{Type: "string", CustomFormat: "date", AirbyteType: "date"}
propertyType = PropertyType{Type: []string{"string"}, CustomFormat: "date", AirbyteType: "date"}
case strings.HasPrefix(mysqlType, "time"):
return PropertyType{Type: "string", CustomFormat: "time", AirbyteType: "time_without_timezone"}
propertyType = PropertyType{Type: []string{"string"}, CustomFormat: "time", AirbyteType: "time_without_timezone"}
default:
return PropertyType{Type: "string"}
propertyType = PropertyType{Type: []string{"string"}}
}

if strings.ToLower(nullable) == "yes" {
propertyType.Type = append(propertyType.Type, "null")
}

return propertyType
}

func (p PlanetScaleEdgeDatabase) Close() error {
53 changes: 30 additions & 23 deletions cmd/internal/planetscale_edge_database_test.go
Original file line number Diff line number Diff line change
@@ -359,126 +359,133 @@ func TestRead_CanPickRdonlyForShardedKeyspaces(t *testing.T) {
func TestDiscover_CanPickRightAirbyteType(t *testing.T) {
var tests = []struct {
MysqlType string
JSONSchemaType string
JSONSchemaType []string
AirbyteType string
TreatTinyIntAsBoolean bool
IsNullable string
}{
{
MysqlType: "int(11)",
JSONSchemaType: "number",
JSONSchemaType: []string{"number"},
AirbyteType: "integer",
},
{
MysqlType: "smallint(4)",
JSONSchemaType: "number",
JSONSchemaType: []string{"number"},
AirbyteType: "integer",
},
{
MysqlType: "mediumint(8)",
JSONSchemaType: "number",
JSONSchemaType: []string{"number"},
AirbyteType: "integer",
},
{
MysqlType: "tinyint",
JSONSchemaType: "number",
JSONSchemaType: []string{"number"},
AirbyteType: "integer",
TreatTinyIntAsBoolean: true,
},
{
MysqlType: "tinyint(1)",
JSONSchemaType: "boolean",
JSONSchemaType: []string{"boolean"},
AirbyteType: "",
TreatTinyIntAsBoolean: true,
},
{
MysqlType: "tinyint(1) unsigned",
JSONSchemaType: "boolean",
JSONSchemaType: []string{"boolean"},
AirbyteType: "",
TreatTinyIntAsBoolean: true,
},
{
MysqlType: "tinyint(1)",
JSONSchemaType: "number",
JSONSchemaType: []string{"number"},
AirbyteType: "integer",
TreatTinyIntAsBoolean: false,
},
{
MysqlType: "tinyint(1) unsigned",
JSONSchemaType: "number",
JSONSchemaType: []string{"number"},
AirbyteType: "integer",
TreatTinyIntAsBoolean: false,
},
{
MysqlType: "bigint(16)",
JSONSchemaType: "number",
JSONSchemaType: []string{"number"},
AirbyteType: "integer",
},
{
MysqlType: "bigint unsigned",
JSONSchemaType: "number",
JSONSchemaType: []string{"number"},
AirbyteType: "integer",
},
{
MysqlType: "bigint zerofill",
JSONSchemaType: "number",
JSONSchemaType: []string{"number"},
AirbyteType: "integer",
},
{
MysqlType: "datetime",
JSONSchemaType: "string",
JSONSchemaType: []string{"string"},
AirbyteType: "timestamp_without_timezone",
},
{
MysqlType: "datetime(6)",
JSONSchemaType: "string",
JSONSchemaType: []string{"string"},
AirbyteType: "timestamp_without_timezone",
},
{
MysqlType: "time",
JSONSchemaType: "string",
JSONSchemaType: []string{"string"},
AirbyteType: "time_without_timezone",
},
{
MysqlType: "time(6)",
JSONSchemaType: "string",
JSONSchemaType: []string{"string"},
AirbyteType: "time_without_timezone",
},
{
MysqlType: "date",
JSONSchemaType: "string",
JSONSchemaType: []string{"string"},
AirbyteType: "date",
},
{
MysqlType: "text",
JSONSchemaType: "string",
JSONSchemaType: []string{"string"},
AirbyteType: "",
},
{
MysqlType: "varchar(256)",
JSONSchemaType: "string",
JSONSchemaType: []string{"string"},
AirbyteType: "",
},
{
MysqlType: "varchar(256)",
JSONSchemaType: []string{"string", "null"},
AirbyteType: "",
IsNullable: "YES",
},
{
MysqlType: "decimal(12,5)",
JSONSchemaType: "number",
JSONSchemaType: []string{"number"},
AirbyteType: "",
},
{
MysqlType: "double",
JSONSchemaType: "number",
JSONSchemaType: []string{"number"},
AirbyteType: "",
},
{
MysqlType: "float(30)",
JSONSchemaType: "number",
JSONSchemaType: []string{"number"},
AirbyteType: "",
},
}

for _, typeTest := range tests {

t.Run(fmt.Sprintf("mysql_type_%v", typeTest.MysqlType), func(t *testing.T) {
p := getJsonSchemaType(typeTest.MysqlType, typeTest.TreatTinyIntAsBoolean)
p := getJsonSchemaType(typeTest.MysqlType, typeTest.TreatTinyIntAsBoolean, typeTest.IsNullable)
assert.Equal(t, typeTest.AirbyteType, p.AirbyteType)
assert.Equal(t, typeTest.JSONSchemaType, p.Type)
})
7 changes: 4 additions & 3 deletions cmd/internal/planetscale_edge_mysql.go
Original file line number Diff line number Diff line change
@@ -157,7 +157,7 @@ func (p planetScaleEdgeMySQLAccess) GetTableSchema(ctx context.Context, psc Plan

columnNamesQR, err := p.db.QueryContext(
ctx,
"select column_name, column_type from information_schema.columns where table_name=? AND table_schema=?;",
"select column_name, column_type, is_nullable from information_schema.columns where table_name=? AND table_schema=?;",
tableName, psc.Database,
)
if err != nil {
@@ -168,12 +168,13 @@ func (p planetScaleEdgeMySQLAccess) GetTableSchema(ctx context.Context, psc Plan
var (
name string
columnType string
nullable string
)
if err = columnNamesQR.Scan(&name, &columnType); err != nil {
if err = columnNamesQR.Scan(&name, &columnType, &nullable); err != nil {
return properties, errors.Wrapf(err, "Unable to scan row for column names & types of table %v", tableName)
}

properties[name] = getJsonSchemaType(columnType, !psc.Options.DoNotTreatTinyIntAsBoolean)
properties[name] = getJsonSchemaType(columnType, !psc.Options.DoNotTreatTinyIntAsBoolean, nullable)
}

if err := columnNamesQR.Err(); err != nil {
6 changes: 3 additions & 3 deletions cmd/internal/types.go
Original file line number Diff line number Diff line change
@@ -54,9 +54,9 @@ const (
)

type PropertyType struct {
Type string `json:"type"`
CustomFormat string `json:"format,omitempty"`
AirbyteType string `json:"airbyte_type,omitempty"`
Type []string `json:"type"`
CustomFormat string `json:"format,omitempty"`
AirbyteType string `json:"airbyte_type,omitempty"`
}

type StreamSchema struct {