From 93cdd753a6ada4235b1f37fed863e685671bee55 Mon Sep 17 00:00:00 2001 From: Ben Vezzani Date: Sun, 29 Nov 2020 20:15:43 -0500 Subject: [PATCH] Task/datatype refactor (#4) * Decouple DBMS Type Support metadata Moves the DBMS-specific type support information away from the core Datatype constants. DBMS support information should only be added to DBMS-specific concretions. * Tests for new datatypes logic --- internal/datatype/datatypes.go | 43 ++++++++++-------------- internal/datatype/datatypes_test.go | 8 ++--- internal/dbms/base/dialect_test.go | 4 +-- internal/dbms/mysql/generator.go | 1 - internal/dbms/mysql/migrator.go | 4 ++- internal/dbms/mysql/validator.go | 24 +++++++++++++ internal/dbms/mysql/validator_test.go | 38 +++++++++++++++++++++ internal/dbms/postgres/migrator.go | 4 ++- internal/dbms/postgres/validator.go | 28 +++++++++++++++ internal/dbms/postgres/validator_test.go | 37 ++++++++++++++++++++ internal/validation/dialect.go | 7 ++++ 11 files changed, 164 insertions(+), 34 deletions(-) create mode 100644 internal/dbms/mysql/validator.go create mode 100644 internal/dbms/mysql/validator_test.go create mode 100644 internal/dbms/postgres/validator.go create mode 100644 internal/dbms/postgres/validator_test.go create mode 100644 internal/validation/dialect.go diff --git a/internal/datatype/datatypes.go b/internal/datatype/datatypes.go index 03461ca..137d8b6 100644 --- a/internal/datatype/datatypes.go +++ b/internal/datatype/datatypes.go @@ -4,28 +4,28 @@ import "strings" // Datatype is used to encode information about types for use in generation or validation // The least-significant 8 bits are reserved for general metadata -// The next 16 bits are reserved for DBMS support metadata -// The next 8 bits are reserved for unique identification +// The next 16 bits are not currently used. They were historically reserved for DBMS support in the early concept stage. +// The next 8 bits are reserved for unique type identification // The last 32 bits are not currently used type Datatype uint64 -// These are the actual Datatypes with all the metadata and unique identifiers encoded into them +// These are the actual Datatype constants with all the metadata and unique identifiers encoded into them const ( - Integer = idInteger | MySQL | PostgreSQL | metaNumeric | metaInteger | metaSignable - TinyInt = idTinyInt | MySQL | metaNumeric | metaSignable - SmallInt = idSmallInt | MySQL | PostgreSQL | metaNumeric | metaInteger | metaSignable - MediumInt = idMediumInt | MySQL | PostgreSQL | metaNumeric | metaInteger | metaSignable - BigInt = idBigInt | MySQL | PostgreSQL | metaNumeric | metaInteger | metaSignable - Decimal = idDecimal | MySQL | PostgreSQL | metaNumeric | metaSignable | metaRequiresScale - Varchar = idVarchar | MySQL | PostgreSQL | metaString - Text = idText | MySQL | PostgreSQL | metaString - TinyText = idTinyText | PostgreSQL | metaString - MediumText = idMediumText | PostgreSQL | metaString - LongText = idLongText | PostgreSQL | metaString - Char = idChar | PostgreSQL | metaString - Blob = idBlob | MySQL | PostgreSQL | metaBinary - Enum = idEnum | MySQL | PostgreSQL | metaString | metaRequiresScale - Boolean = idBoolean | PostgreSQL + Integer = idInteger | metaNumeric | metaInteger | metaSignable + TinyInt = idTinyInt | metaNumeric | metaSignable + SmallInt = idSmallInt | metaNumeric | metaInteger | metaSignable + MediumInt = idMediumInt | metaNumeric | metaInteger | metaSignable + BigInt = idBigInt | metaNumeric | metaInteger | metaSignable + Decimal = idDecimal | metaNumeric | metaSignable | metaRequiresScale + Varchar = idVarchar | metaString + Text = idText | metaString + TinyText = idTinyText | metaString + MediumText = idMediumText | metaString + LongText = idLongText | metaString + Char = idChar | metaString + Blob = idBlob | metaBinary + Enum = idEnum | metaString | metaRequiresScale + Boolean = idBoolean ) // These are the string representations of datatypes @@ -183,13 +183,6 @@ const ( metaRequiresScale ) -// These metadata describe the support across different databases -// 16 bits are reserved for this -const ( - MySQL Datatype = 1 << iota << 8 - PostgreSQL -) - // These are the unique type identifiers // Unlike the others, these are not single-bit flags const ( diff --git a/internal/datatype/datatypes_test.go b/internal/datatype/datatypes_test.go index 2e0df20..8d28944 100644 --- a/internal/datatype/datatypes_test.go +++ b/internal/datatype/datatypes_test.go @@ -373,11 +373,11 @@ func TestDatatype_IsSignable(t *testing.T) { func TestDatatype_MarshalYAML(t *testing.T) { tests := []struct { - dt Datatype - want string + dt Datatype + want string }{ { - dt: Integer, + dt: Integer, want: "integer", }, } @@ -389,4 +389,4 @@ func TestDatatype_MarshalYAML(t *testing.T) { } }) } -} \ No newline at end of file +} diff --git a/internal/dbms/base/dialect_test.go b/internal/dbms/base/dialect_test.go index ccc39bb..74598b4 100644 --- a/internal/dbms/base/dialect_test.go +++ b/internal/dbms/base/dialect_test.go @@ -25,7 +25,7 @@ func TestBase_TypeString(t *testing.T) { args: args{ dt: datatype.Integer, }, - want: "INTEGER", + want: "INTEGER", wantErr: false, }, { @@ -35,7 +35,7 @@ func TestBase_TypeString(t *testing.T) { args: args{ dt: 0, }, - want: "", + want: "", wantErr: true, }, } diff --git a/internal/dbms/mysql/generator.go b/internal/dbms/mysql/generator.go index cc2b146..b084302 100644 --- a/internal/dbms/mysql/generator.go +++ b/internal/dbms/mysql/generator.go @@ -1,2 +1 @@ package mysql - diff --git a/internal/dbms/mysql/migrator.go b/internal/dbms/mysql/migrator.go index e42a285..6e49e33 100644 --- a/internal/dbms/mysql/migrator.go +++ b/internal/dbms/mysql/migrator.go @@ -15,15 +15,17 @@ func NewMigrator() *migrator { Base: base.Base{ Dialect: dialect.MySQL, }, + validator: validator{}, } } type migrator struct { base.Base + validator } func (d *migrator) TypeString(dt datatype.Datatype) (s string, err error) { - if dt&datatype.MySQL != datatype.MySQL { + if !d.SupportsDatatype(dt) { return "", errors.New("unsupported datatype") } switch dt { diff --git a/internal/dbms/mysql/validator.go b/internal/dbms/mysql/validator.go new file mode 100644 index 0000000..03cbb82 --- /dev/null +++ b/internal/dbms/mysql/validator.go @@ -0,0 +1,24 @@ +package mysql + +import "github.com/dotvezz/yoyo/internal/datatype" + +type validator struct { +} + +func (_ *validator) SupportsDatatype(dt datatype.Datatype) bool { + switch dt { + case datatype.Integer, + datatype.TinyInt, + datatype.SmallInt, + datatype.MediumInt, + datatype.BigInt, + datatype.Decimal, + datatype.Varchar, + datatype.Text, + datatype.Blob, + datatype.Enum: + return true + } + + return false +} diff --git a/internal/dbms/mysql/validator_test.go b/internal/dbms/mysql/validator_test.go new file mode 100644 index 0000000..fb888ec --- /dev/null +++ b/internal/dbms/mysql/validator_test.go @@ -0,0 +1,38 @@ +package mysql + +import ( + "github.com/dotvezz/yoyo/internal/datatype" + "testing" +) + +func Test_validator_SupportsDatatype(t *testing.T) { + type args struct { + dt datatype.Datatype + } + tests := []struct { + args args + want bool + }{ + { + args: args{dt: datatype.Boolean}, + want: false, + }, + { + args: args{dt: datatype.Integer}, + want: true, + }, + { + args: args{dt: 0}, + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.args.dt.String(), func(t *testing.T) { + va := &validator{} + if got := va.SupportsDatatype(tt.args.dt); got != tt.want { + t.Errorf("SupportsDatatype() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/dbms/postgres/migrator.go b/internal/dbms/postgres/migrator.go index 4a54b6b..6292ad5 100644 --- a/internal/dbms/postgres/migrator.go +++ b/internal/dbms/postgres/migrator.go @@ -12,15 +12,17 @@ func NewMigrator() *postgres { Base: base.Base{ Dialect: dialect.PostgreSQL, }, + validator: validator{}, } } type postgres struct { base.Base + validator } func (d *postgres) TypeString(dt datatype.Datatype) (s string, err error) { - if dt&datatype.PostgreSQL != datatype.PostgreSQL { + if !d.SupportsDatatype(dt) { return "", fmt.Errorf("datatype %s is not supported in postgresql", dt) } switch dt { diff --git a/internal/dbms/postgres/validator.go b/internal/dbms/postgres/validator.go new file mode 100644 index 0000000..e088883 --- /dev/null +++ b/internal/dbms/postgres/validator.go @@ -0,0 +1,28 @@ +package postgres + +import "github.com/dotvezz/yoyo/internal/datatype" + +type validator struct { +} + +func (_ *validator) SupportsDatatype(dt datatype.Datatype) bool { + switch dt { + case datatype.Integer, + datatype.SmallInt, + datatype.MediumInt, + datatype.BigInt, + datatype.Decimal, + datatype.Varchar, + datatype.Text, + datatype.TinyText, + datatype.MediumText, + datatype.LongText, + datatype.Char, + datatype.Blob, + datatype.Enum, + datatype.Boolean: + return true + } + + return false +} diff --git a/internal/dbms/postgres/validator_test.go b/internal/dbms/postgres/validator_test.go new file mode 100644 index 0000000..a1a0965 --- /dev/null +++ b/internal/dbms/postgres/validator_test.go @@ -0,0 +1,37 @@ +package postgres + +import ( + "github.com/dotvezz/yoyo/internal/datatype" + "testing" +) + +func Test_validator_SupportsDatatype(t *testing.T) { + type args struct { + dt datatype.Datatype + } + tests := []struct { + args args + want bool + }{ + { + args: args{dt: datatype.Boolean}, + want: true, + }, + { + args: args{dt: datatype.Integer}, + want: true, + }, + { + args: args{dt: 0}, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.args.dt.String(), func(t *testing.T) { + va := &validator{} + if got := va.SupportsDatatype(tt.args.dt); got != tt.want { + t.Errorf("SupportsDatatype() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/validation/dialect.go b/internal/validation/dialect.go new file mode 100644 index 0000000..3a0c7e0 --- /dev/null +++ b/internal/validation/dialect.go @@ -0,0 +1,7 @@ +package validation + +import "github.com/dotvezz/yoyo/internal/datatype" + +type Validator interface { + SupportsDatatype(datatype datatype.Datatype) bool +}