Extend RDBMS DAL tests and fix issues with bool & numeric types

This commit is contained in:
Denis Arh
2022-11-07 18:54:40 +01:00
parent 65fa23425e
commit 3bf030685b
5 changed files with 173 additions and 13 deletions
+60 -1
View File
@@ -18,7 +18,8 @@ import (
)
type (
kv map[string]any
kv map[string]any
kvv map[string][]any
)
var (
@@ -92,6 +93,64 @@ func (r kv) String() string {
return out
}
func (r kvv) Set(k string, v ...any) kvv {
r[k] = v
return r
}
func (r kvv) CountValues() map[string]uint {
out := make(map[string]uint)
for k := range r {
out[k] = uint(len(r[k]))
}
return out
}
func (r kvv) GetValue(k string, p uint) (any, error) {
if r[k] == nil || len(r[k]) <= int(p) {
return nil, fmt.Errorf("kvv: out of bounds")
}
return r[k][p], nil
}
func (r kvv) SetValue(k string, p uint, v any) error {
if r[k] == nil {
r[k] = make([]any, 0, 1)
} else if len(r[k]) < int(p) {
r[k][p] = v
} else {
r[k] = append(r[k], v)
}
return nil
}
// String function returns string representation of the kv with sorted keys
func (r kvv) String() string {
// sort keys from map
keys := make([]string, 0, len(r))
for k := range r {
keys = append(keys, k)
}
sort.Strings(keys)
// build string by iterating over sorted keys and appending values
var out string
for i, k := range keys {
if i > 0 {
out += " "
}
out += fmt.Sprintf("%s=%v", k, r[k])
}
return out
}
func qlParse(req *require.Assertions, q string) *ql.ASTNode {
n, err := ql.NewParser().Parse(q)
req.NoError(err)
+7 -2
View File
@@ -573,9 +573,14 @@ func (d *model) aggregateSql(f filter.Filter, groupBy []dal.AggregateAttr, out [
converter = ql.Converter(
ql.SymHandler(func(node *ql.ASTNode) (exp.Expression, error) {
sym := dal.NormalizeAttrNames(node.Symbol)
if a2expr[sym] != nil {
// is aliased expression?
return a2expr[sym], nil
if d.dialect.Nuances().HavingClauseMustUseAlias {
// is aliased expression?
return a2expr[sym], nil
} else {
return exp.NewIdentifierExpression("", "", sym), nil
}
}
// if not, use the default handler
+99 -4
View File
@@ -93,6 +93,105 @@ func TestModel_Search(t *testing.T) {
req.Equal("group=g0 item=i1000 price=1000 published=1", rows[0].String())
}
// Should be part of general DAL testing (not only RDBMS)
func TestModel_Search2(t *testing.T) {
var (
req = require.New(t)
ctx = logger.ContextWithValue(context.Background(), logger.MakeDebugLogger())
baseModel = &dal.Model{
Ident: t.Name(),
Attributes: func() (aa []*dal.Attribute) {
s := &dal.CodecRecordValueSetJSON{Ident: "values"}
aa = []*dal.Attribute{
{Ident: "phyTxt", Type: &dal.TypeText{}},
{Ident: "phyNum", Type: &dal.TypeNumber{}},
{Ident: "phyBool", Type: &dal.TypeBoolean{}},
{Ident: "jsonEncTxt", Type: &dal.TypeText{}, Store: s},
{Ident: "jsonEncTxtMV", Type: &dal.TypeText{}, Store: s, MultiValue: true},
{Ident: "jsonEncNum", Type: &dal.TypeNumber{}, Store: s},
{Ident: "jsonEncNumMV", Type: &dal.TypeNumber{}, Store: s, MultiValue: true},
{Ident: "jsonEncBool", Type: &dal.TypeBoolean{}, Store: s},
{Ident: "jsonEncBoolMV", Type: &dal.TypeBoolean{}, Store: s, MultiValue: true},
}
// iterate through all attributsand set Filterable flag to true
for _, a := range aa {
a.Filterable = true
a.Sortable = true
}
return
}(),
}
m = Model(baseModel, s.DB, s.Dialect)
search = func(t *testing.T, f filter.Filter) []kv {
req := require.New(t)
i, err := m.Search(f)
req.NoError(err)
req.NotNil(i)
defer req.NoError(i.Close())
ctx = logger.ContextWithValue(context.Background(), logger.MakeDebugLogger())
rows := make([]kv, 0, 5)
for i.Next(ctx) {
row := kv{}
req.NoError(i.Scan(row))
rows = append(rows, row)
}
req.NoError(i.Err())
return rows
}
)
table, err := s.DataDefiner.ConvertModel(baseModel)
req.NoError(err)
t.Logf("Creating temporary table %q", table.Ident)
table.Temporary = true
req.NoError(s.DataDefiner.TableCreate(ctx, table))
{
noLogCtx := context.Background() // no need to log inserts
req.NoError(m.Create(noLogCtx, (&kvv{}).
Set("phyTxt", "bar").
Set("phyNum", 42).
Set("phyBool", false).
Set("jsonEncTxt", "bar").
Set("jsonEncTxtMV", "bar", "foo").
Set("jsonEncNum", 42).
Set("jsonEncNumMV", 21, 42).
Set("jsonEncBool", false).
Set("jsonEncBoolMV", false, true),
))
}
req.Len(search(t, filter.Generic(filter.WithExpression("phyTxt = 'bar'"))), 1)
req.Len(search(t, filter.Generic(filter.WithExpression("phyTxt = 'baz'"))), 0)
req.Len(search(t, filter.Generic(filter.WithExpression("phyNum = 42"))), 1)
req.Len(search(t, filter.Generic(filter.WithExpression("phyNum = 21"))), 0)
req.Len(search(t, filter.Generic(filter.WithExpression("!phyBool"))), 1)
req.Len(search(t, filter.Generic(filter.WithExpression("phyBool"))), 0)
req.Len(search(t, filter.Generic(filter.WithExpression("jsonEncTxt = 'bar'"))), 1)
req.Len(search(t, filter.Generic(filter.WithExpression("jsonEncTxt = 'baz'"))), 0)
req.Len(search(t, filter.Generic(filter.WithExpression("jsonEncTxtMV = 'bar'"))), 1)
req.Len(search(t, filter.Generic(filter.WithExpression("jsonEncTxtMV = 'baz'"))), 0)
req.Len(search(t, filter.Generic(filter.WithExpression("jsonEncTxtMV = 'foo'"))), 0, "should not match the second value")
req.Len(search(t, filter.Generic(filter.WithExpression("jsonEncNum = 42"))), 1)
req.Len(search(t, filter.Generic(filter.WithExpression("jsonEncNum = 21"))), 0)
req.Len(search(t, filter.Generic(filter.WithExpression("jsonEncNumMV = 21"))), 1)
req.Len(search(t, filter.Generic(filter.WithExpression("jsonEncNumMV = 22"))), 0)
req.Len(search(t, filter.Generic(filter.WithExpression("jsonEncNumMV = 42"))), 0, "should not match the second value")
req.Len(search(t, filter.Generic(filter.WithExpression("jsonEncBoolMV"))), 0, "should not match the second value")
}
func TestModel_Aggregate(t *testing.T) {
_ = logger.Default()
@@ -349,7 +448,6 @@ func TestModel_AggregateWithHaving(t *testing.T) {
func TestModel_AggregateHavingGroup(t *testing.T) {
_ = logger.Default()
var (
req = require.New(t)
@@ -412,9 +510,6 @@ func TestModel_AggregateHavingGroup(t *testing.T) {
row = kv{}
req.NoError(i.Scan(row))
// due to difference of number of decimal digits in different DBs, we need to do this
// to make sure we get the same result
rows = append(rows, row)
}
@@ -34,7 +34,7 @@ var (
customDriver = &sqlite3.SQLiteDriver{
ConnectHook: func(conn *sqlite3.SQLiteConn) (err error) {
// register regexp function and use Go's regexp fn
if err = conn.RegisterFunc("regexp", regexp.MatchString, true); err != nil {
if err = conn.RegisterFunc("regexp", fnRegExp, true); err != nil {
return
}
@@ -47,6 +47,11 @@ var (
}
)
func fnRegExp(pattern string, s string) (matched bool, err error) {
matched, err = regexp.MatchString(pattern, s)
return
}
func init() {
// register alter driver
sql.Register(altSchema, customDriver)
@@ -127,11 +127,7 @@ func (sqliteDialect) AttributeCast(attr *dal.Attribute, val exp.Expression) (exp
c = exp.NewSQLFunctionExpression("strftime", "%Y-%m-%d", val)
case *dal.TypeNumber:
ce := exp.NewCaseExpression().
When(drivers.RegexpLike(drivers.CheckNumber, val), val).
Else(drivers.LiteralNULL)
c = exp.NewCastExpression(ce, "NUMERIC")
c = exp.NewCastExpression(val, "NUMERIC")
default:
return drivers.AttributeCast(attr, val)