Tweak MySQL type fit check

Make the check respect signed & unsigned as well as decimal and
varchar sizes.
This commit is contained in:
Tomaž Jerman
2023-10-26 17:09:59 +02:00
parent d6c669cec6
commit f733788323
3 changed files with 207 additions and 17 deletions
+18
View File
@@ -0,0 +1,18 @@
package ddl
import "strings"
func ParseColumnTypes(c *Column) (original, name string, meta []string) {
original = strings.ToLower(c.Type.Name)
pp := strings.Split(original, "(")
name = pp[0]
if len(pp) > 1 {
meta = strings.Split(strings.TrimRight(pp[1], ")"), ",")
for i, m := range meta {
meta[i] = strings.TrimSpace(m)
}
}
return
}
@@ -234,28 +234,29 @@ func (mysqlDialect) AttributeToColumn(attr *dal.Attribute) (col *ddl.Column, err
}
func (mysqlDialect) ColumnFits(target, assert *ddl.Column) bool {
targetType := strings.ToLower(target.Type.Name)
assertType := strings.ToLower(assert.Type.Name)
if targetType == "bigint unsigned" {
targetType = "bigint"
}
if assertType == "bigint unsigned" {
assertType = "bigint"
}
targetType = strings.Split(targetType, "(")[0]
assertType = strings.Split(assertType, "(")[0]
targetType, targetName, targetMeta := ddl.ParseColumnTypes(target)
assertType, assertName, assertMeta := ddl.ParseColumnTypes(assert)
// If everything matches up perfectly use that
if assertType == targetType {
return true
}
// @todo check varchar sizes
// @todo signed & unsigned
// See if we can guess it
// [the type of the target column][what types fit the target col. type]
return map[string]map[string]bool{
matches := map[string]map[string]bool{
"bigint unsigned": {
"varchar": true,
"text": true,
"decimal": true,
},
"bigint signed": {
"varchar": true,
"text": true,
"decimal": true,
},
"bigint": {
"varchar": true,
"text": true,
@@ -300,7 +301,22 @@ func (mysqlDialect) ColumnFits(target, assert *ddl.Column) bool {
"varchar": true,
"text": true,
},
}[assertType][targetType]
}
baseMatch := matches[assertName][targetName]
// Special cases
switch {
case assertName == "varchar" && targetName == "varchar":
// Check varchar size
return baseMatch && assertMeta[0] <= targetMeta[0]
case assertName == "decimal" && targetName == "decimal":
// Check decimal size and precision
return baseMatch && assertMeta[0] <= targetMeta[0] && assertMeta[1] <= targetMeta[1]
}
return baseMatch
}
func (d mysqlDialect) ExprHandler(n *ql.ASTNode, args ...exp.Expression) (expr exp.Expression, err error) {
@@ -0,0 +1,156 @@
package mysql
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: "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: "text",
},
},
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: "sneaking unsigned into signed",
target: &ddl.Column{
Type: &ddl.ColumnType{
Name: "bigint signed",
},
},
assert: &ddl.Column{
Type: &ddl.ColumnType{
Name: "bigint unsigned",
},
},
expected: false,
},
{
name: "sneaking signed into unsigned",
target: &ddl.Column{
Type: &ddl.ColumnType{
Name: "bigint unsigned",
},
},
assert: &ddl.Column{
Type: &ddl.ColumnType{
Name: "bigint signed",
},
},
expected: false,
},
}
d := mysqlDialect{}
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)
})
}
}