diff --git a/store/rdbms/ast_transformer.go b/store/rdbms/ast_transformer.go index 17a481cf5..8ddc5af57 100644 --- a/store/rdbms/ast_transformer.go +++ b/store/rdbms/ast_transformer.go @@ -127,6 +127,14 @@ var ( Handler: makeGenericCompHandler("/"), }, + // - strings + "concat": { + Args: collectParams(true, "String"), + RArgs: true, + Result: wrapRes("String"), + Handler: makeGenericFncHandler("CONCAT"), + }, + // @todo better negation? "like": { Args: collectParams(true, "String", "String"), @@ -240,6 +248,13 @@ var ( Result: wrapRes("Float"), Handler: makeGenericTypecastHandler("SIGNED"), }, + // @todo some better way of casting to string? + // MySQL does not support casting to longtext so I decided on doing this for now + "string": { + Args: collectParams(true, "Any"), + Result: wrapRes("String"), + Handler: makeGenericTypecastHandler("CHAR(8192)"), + }, } ) diff --git a/store/rdbms/ast_transformer_handlers.go b/store/rdbms/ast_transformer_handlers.go index a9a3f9918..790949325 100644 --- a/store/rdbms/ast_transformer_handlers.go +++ b/store/rdbms/ast_transformer_handlers.go @@ -131,6 +131,21 @@ func makeGenericAggFncHandler(fnc string) HandlerSig { } } +func makeGenericFncHandler(fnc string) HandlerSig { + return func(aa ...FormattedASTArgs) (out string, args []interface{}, selfEnclosed bool, err error) { + selfEnclosed = true + + params := make([]string, len(aa)) + for i, a := range aa { + params[i] = a.S + args = append(args, a.Args...) + } + + out = fmt.Sprintf("%s(%s)", fnc, strings.Join(params, ", ")) + return + } +} + func makeGenericTypecastHandler(t string) HandlerSig { return func(aa ...FormattedASTArgs) (out string, args []interface{}, selfEnclosed bool, err error) { selfEnclosed = true diff --git a/store/sqlite3/sql_functions.go b/store/sqlite3/sql_functions.go index 2de1567cf..f3d799021 100644 --- a/store/sqlite3/sql_functions.go +++ b/store/sqlite3/sql_functions.go @@ -63,6 +63,20 @@ var ( return }, + // - strings + "concat": func(aa ...rdbms.FormattedASTArgs) (out string, args []interface{}, selfEnclosed bool, err error) { + selfEnclosed = true + + params := make([]string, len(aa)) + for i, a := range aa { + params[i] = a.S + args = append(args, a.Args...) + } + + out = fmt.Sprintf("(%s)", strings.Join(params, "||")) + return + }, + // - typecast "float": func(aa ...rdbms.FormattedASTArgs) (out string, args []interface{}, selfEnclosed bool, err error) { selfEnclosed = true @@ -76,6 +90,18 @@ var ( args = aa[0].Args return }, + "string": func(aa ...rdbms.FormattedASTArgs) (out string, args []interface{}, selfEnclosed bool, err error) { + selfEnclosed = true + + if len(aa) != 1 { + err = fmt.Errorf("expecting 1 argument, got %d", len(aa)) + return + } + + out = fmt.Sprintf("CAST(%s AS TEXT)", aa[0].S) + args = aa[0].Args + return + }, } ) diff --git a/tests/reporter/group_date_composition_test.go b/tests/reporter/group_date_composition_test.go new file mode 100644 index 000000000..4856cc835 --- /dev/null +++ b/tests/reporter/group_date_composition_test.go @@ -0,0 +1,27 @@ +package reporter + +import ( + "testing" + + "github.com/cortezaproject/corteza-server/pkg/report" +) + +func Test_group_date_composition(t *testing.T) { + var ( + ctx, h, s = setup(t) + m, _, dd = loadScenario(ctx, s, t, h) + ff []*report.Frame + def = dd[0] + ) + + ff = loadNoErr(ctx, h, m, def) + h.a.Len(ff, 1) + r := ff[0] + h.a.Equal(2, r.Size()) + + h.a.Equal("by_dob, count", r.Columns.String()) + + checkRows(h, ff[0], + "2020-01, 5", + "2021-01, 7") +} diff --git a/tests/reporter/testdata/group_date_composition/report.json b/tests/reporter/testdata/group_date_composition/report.json new file mode 100644 index 000000000..232c20a05 --- /dev/null +++ b/tests/reporter/testdata/group_date_composition/report.json @@ -0,0 +1,38 @@ +{ + "handle": "testing_report", + "sources": [ + { "step": { "load": { + "name": "users", + "source": "composeRecords", + "definition": { + "module": "user", + "namespace": "ns" + } + }}}, + + { "step": { "group": { + "name": "grouped", + "source": "users", + "keys": [ + { "name": "by_dob", "def": "concat(string(year(dob)), '-', string(month(dob)))" } + ], + "columns": [ + { + "name": "count", + "def": { + "ref": "count" + } + } + ] + }}} + ], + "frames": [{ + "name": "result", + "source": "grouped", + "columns": [ + { "name": "by_dob", "label": "by_dob" }, + { "name": "count", "label": "count" } + ], + "sort": "by_dob ASC" + }] +}