From 1e10db1e954ca2e61145d53eab85bf8a3d48b025 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toma=C5=BE=20Jerman?= Date: Sun, 25 Jun 2023 16:06:47 +0200 Subject: [PATCH] Add tests & tweaks for RDBMS dialect column fit checks --- .../adapters/rdbms/drivers/mssql/dialect.go | 19 ++- .../rdbms/drivers/mssql/dialect_test.go | 136 +++++++++++++++ .../adapters/rdbms/drivers/mysql/dialect.go | 2 +- .../rdbms/drivers/postgres/dialect.go | 4 +- .../rdbms/drivers/postgres/dialect_test.go | 156 ++++++++++++++++++ 5 files changed, 309 insertions(+), 8 deletions(-) create mode 100644 server/store/adapters/rdbms/drivers/mssql/dialect_test.go create mode 100644 server/store/adapters/rdbms/drivers/postgres/dialect_test.go diff --git a/server/store/adapters/rdbms/drivers/mssql/dialect.go b/server/store/adapters/rdbms/drivers/mssql/dialect.go index e93f28b64..ccd2f53a3 100644 --- a/server/store/adapters/rdbms/drivers/mssql/dialect.go +++ b/server/store/adapters/rdbms/drivers/mssql/dialect.go @@ -273,27 +273,34 @@ func (mssqlDialect) ColumnFits(target, assert *ddl.Column) bool { // Special cases switch { case assertName == "varchar" && targetName == "varchar": + // @note mssql represents max as -1 so we're just going with this + if assertMeta[0] == "max" { + assertMeta[0] = "-1" + } + if targetMeta[0] == "max" { + targetMeta[0] = "-1" + } + // Check varchar size for i := len(assertMeta); i < 1; i++ { assertMeta = append(assertMeta, "0") } - if assertMeta[0] == "max" { - assertMeta[0] = "-1" - } assertA := cast.ToInt(assertMeta[0]) for i := len(targetMeta); i < 1; i++ { targetMeta = append(targetMeta, "0") } - if targetMeta[0] == "max" { - targetMeta[0] = "-1" - } targetA := cast.ToInt(targetMeta[0]) // -1 means no limit so it can fit any length + // - if target is max, any varchar fits if targetA == -1 { return baseMatch } + // - if assert is max, only max fits + if assertA == -1 { + return baseMatch && targetA == -1 + } return baseMatch && assertA <= targetA diff --git a/server/store/adapters/rdbms/drivers/mssql/dialect_test.go b/server/store/adapters/rdbms/drivers/mssql/dialect_test.go new file mode 100644 index 000000000..dbcae93d0 --- /dev/null +++ b/server/store/adapters/rdbms/drivers/mssql/dialect_test.go @@ -0,0 +1,136 @@ +package mssql + +import ( + "testing" + + "github.com/cortezaproject/corteza/server/store/adapters/rdbms/ddl" + "github.com/stretchr/testify/assert" +) + +func TestColumnFits(t *testing.T) { + tcc := []struct { + name string + target *ddl.Column + assert *ddl.Column + expected bool + }{ + { + name: "exact match (varchar)", + target: &ddl.Column{ + Type: &ddl.ColumnType{ + Name: "varchar", + }, + }, + assert: &ddl.Column{ + Type: &ddl.ColumnType{ + Name: "varchar", + }, + }, + expected: true, + }, + { + name: "fits somewhere", + target: &ddl.Column{ + Type: &ddl.ColumnType{ + Name: "varchar(max)", + }, + }, + assert: &ddl.Column{ + Type: &ddl.ColumnType{ + Name: "decimal(1,2)", + }, + }, + expected: true, + }, + { + name: "doesn't fit", + target: &ddl.Column{ + Type: &ddl.ColumnType{ + Name: "decimal(1,2)", + }, + }, + assert: &ddl.Column{ + Type: &ddl.ColumnType{ + Name: "varchar(max)", + }, + }, + expected: false, + }, + + { + name: "decimal fits", + target: &ddl.Column{ + Type: &ddl.ColumnType{Name: "decimal(1,2)"}, + }, + assert: &ddl.Column{ + Type: &ddl.ColumnType{Name: "decimal(1,2)"}, + }, + expected: true, + }, + + { + name: "decimal doesn't fit", + target: &ddl.Column{ + Type: &ddl.ColumnType{Name: "decimal(1,2)"}, + }, + assert: &ddl.Column{ + Type: &ddl.ColumnType{Name: "decimal(2,3)"}, + }, + expected: false, + }, + + { + name: "varchar fits", + target: &ddl.Column{ + Type: &ddl.ColumnType{Name: "varchar(42)"}, + }, + assert: &ddl.Column{ + Type: &ddl.ColumnType{Name: "varchar(42)"}, + }, + expected: true, + }, + + { + name: "varchar doesn't fit", + target: &ddl.Column{ + Type: &ddl.ColumnType{Name: "varchar(42)"}, + }, + assert: &ddl.Column{ + Type: &ddl.ColumnType{Name: "varchar(84)"}, + }, + expected: false, + }, + + { + name: "varchar(max) fits", + target: &ddl.Column{ + Type: &ddl.ColumnType{Name: "varchar(max)"}, + }, + assert: &ddl.Column{ + Type: &ddl.ColumnType{Name: "varchar(42)"}, + }, + expected: true, + }, + + { + name: "varchar(max) doesn't fit", + target: &ddl.Column{ + Type: &ddl.ColumnType{Name: "varchar(42)"}, + }, + assert: &ddl.Column{ + Type: &ddl.ColumnType{Name: "varchar(max)"}, + }, + expected: false, + }, + } + + d := mssqlDialect{} + + for _, c := range tcc { + t.Run(c.name, func(t *testing.T) { + out := d.ColumnFits(c.target, c.assert) + assert.Equal(t, c.expected, out) + }) + } + +} diff --git a/server/store/adapters/rdbms/drivers/mysql/dialect.go b/server/store/adapters/rdbms/drivers/mysql/dialect.go index cce0eced9..cae435a3a 100644 --- a/server/store/adapters/rdbms/drivers/mysql/dialect.go +++ b/server/store/adapters/rdbms/drivers/mysql/dialect.go @@ -310,7 +310,7 @@ func (mysqlDialect) ColumnFits(target, assert *ddl.Column) bool { switch { case assertName == "varchar" && targetName == "varchar": // Check varchar size - return baseMatch && assertMeta[0] <= targetMeta[0] + return baseMatch && cast.ToInt(assertMeta[0]) <= cast.ToInt(targetMeta[0]) case assertName == "decimal" && targetName == "decimal": // Check numeric size and precision diff --git a/server/store/adapters/rdbms/drivers/postgres/dialect.go b/server/store/adapters/rdbms/drivers/postgres/dialect.go index f9e046e03..340adda3f 100644 --- a/server/store/adapters/rdbms/drivers/postgres/dialect.go +++ b/server/store/adapters/rdbms/drivers/postgres/dialect.go @@ -282,6 +282,8 @@ func (postgresDialect) ColumnFits(target, assert *ddl.Column) bool { targetMeta = append(targetMeta, "0") } + return baseMatch && cast.ToInt(assertMeta[0]) <= cast.ToInt(targetMeta[0]) + case assertName == "numeric" && targetName == "numeric": // Check numeric size and precision for i := len(assertMeta); i < 2; i++ { @@ -291,7 +293,7 @@ func (postgresDialect) ColumnFits(target, assert *ddl.Column) bool { targetMeta = append(targetMeta, "0") } - return baseMatch && assertMeta[0] <= targetMeta[0] && assertMeta[1] <= targetMeta[1] + return baseMatch && cast.ToInt(assertMeta[0]) <= cast.ToInt(targetMeta[0]) && cast.ToInt(assertMeta[1]) <= cast.ToInt(targetMeta[1]) } return baseMatch diff --git a/server/store/adapters/rdbms/drivers/postgres/dialect_test.go b/server/store/adapters/rdbms/drivers/postgres/dialect_test.go new file mode 100644 index 000000000..d72375477 --- /dev/null +++ b/server/store/adapters/rdbms/drivers/postgres/dialect_test.go @@ -0,0 +1,156 @@ +package postgres + +import ( + "testing" + + "github.com/cortezaproject/corteza/server/store/adapters/rdbms/ddl" + "github.com/stretchr/testify/assert" +) + +func TestColumnFits(t *testing.T) { + tcc := []struct { + name string + target *ddl.Column + assert *ddl.Column + expected bool + }{ + { + name: "exact match (text)", + target: &ddl.Column{ + Type: &ddl.ColumnType{ + Name: "text", + }, + }, + assert: &ddl.Column{ + Type: &ddl.ColumnType{ + Name: "text", + }, + }, + expected: true, + }, + { + name: "fits somewhere", + target: &ddl.Column{ + Type: &ddl.ColumnType{ + Name: "text", + }, + }, + assert: &ddl.Column{ + Type: &ddl.ColumnType{ + Name: "numeric(1,2)", + }, + }, + expected: true, + }, + { + name: "doesn't fit", + target: &ddl.Column{ + Type: &ddl.ColumnType{ + Name: "numeric(1,2)", + }, + }, + assert: &ddl.Column{ + Type: &ddl.ColumnType{ + Name: "text", + }, + }, + expected: false, + }, + + { + name: "numeric fits", + target: &ddl.Column{ + Type: &ddl.ColumnType{Name: "numeric(1,2)"}, + }, + assert: &ddl.Column{ + Type: &ddl.ColumnType{Name: "numeric(1,2)"}, + }, + expected: true, + }, + + { + name: "numeric doesn't fit", + target: &ddl.Column{ + Type: &ddl.ColumnType{Name: "numeric(1,2)"}, + }, + assert: &ddl.Column{ + Type: &ddl.ColumnType{Name: "numeric(2,3)"}, + }, + expected: false, + }, + + { + name: "varchar fits", + target: &ddl.Column{ + Type: &ddl.ColumnType{Name: "varchar(42)"}, + }, + assert: &ddl.Column{ + Type: &ddl.ColumnType{Name: "varchar(42)"}, + }, + expected: true, + }, + + { + name: "varchar doesn't fit", + target: &ddl.Column{ + Type: &ddl.ColumnType{Name: "varchar(42)"}, + }, + assert: &ddl.Column{ + Type: &ddl.ColumnType{Name: "varchar(84)"}, + }, + expected: false, + }, + + { + name: "timestamp fits into timestamptz", + target: &ddl.Column{ + Type: &ddl.ColumnType{Name: "timestamptz"}, + }, + assert: &ddl.Column{ + Type: &ddl.ColumnType{Name: "timestamp"}, + }, + expected: true, + }, + { + name: "timestamptz doesn't fit into timestamp", + target: &ddl.Column{ + Type: &ddl.ColumnType{Name: "timestamp"}, + }, + assert: &ddl.Column{ + Type: &ddl.ColumnType{Name: "timestamptz"}, + }, + expected: false, + }, + + { + name: "time fits into timetz", + target: &ddl.Column{ + Type: &ddl.ColumnType{Name: "timetz"}, + }, + assert: &ddl.Column{ + Type: &ddl.ColumnType{Name: "time"}, + }, + expected: true, + }, + { + name: "timetz doesn't fit into time", + target: &ddl.Column{ + Type: &ddl.ColumnType{Name: "time"}, + }, + assert: &ddl.Column{ + Type: &ddl.ColumnType{Name: "timetz"}, + }, + expected: false, + }, + } + + d := postgresDialect{} + + for _, c := range tcc { + t.Run(c.name, func(t *testing.T) { + out := d.ColumnFits(c.target, c.assert) + assert.Equal(t, c.expected, out) + }) + } + +}