From c64a98e36cfc8f716407acc73c60ae7ca283fb7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toma=C5=BE=20Jerman?= Date: Mon, 21 Mar 2022 12:28:53 +0100 Subject: [PATCH] Fix SQLite date formatting * Add missing mappings, * add validation for supported substitutions. --- store/sqlite3/sql_functions.go | 49 ++++++++++++++++++++++++++++- store/tests/compose_records_test.go | 2 +- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/store/sqlite3/sql_functions.go b/store/sqlite3/sql_functions.go index 3be24662a..8ba49ac92 100644 --- a/store/sqlite3/sql_functions.go +++ b/store/sqlite3/sql_functions.go @@ -2,6 +2,7 @@ package sqlite3 import ( "fmt" + "regexp" "strings" "github.com/cortezaproject/corteza-server/pkg/ql" @@ -103,6 +104,19 @@ var ( return }, } + + supportedSubstitutions = map[string]bool{ + "d": true, + "H": true, + "j": true, + "m": true, + "M": true, + "S": true, + "w": true, + "W": true, + "Y": true, + "%": true, + } ) func sqlASTFormatter(n *qlng.ASTNode) rdbms.HandlerSig { @@ -123,7 +137,15 @@ func sqlFunctionHandler(f ql.Function) (ql.ASTNode, error) { if len(f.Arguments) != 2 { return nil, fmt.Errorf("expecting exactly two arguments for DATE_FORMAT function") } - return ql.MakeFormattedNode("STRFTIME('%s', %s)", f.Arguments[0], f.Arguments[1]), nil + format := f.Arguments[1] + col := f.Arguments[0] + + err := supportedDateFormatParams(format) + if err != nil { + return nil, err + } + + return ql.MakeReplacedFormattedNode("STRFTIME(%s, %s)", translateDateFormatParams, format, col), nil case "DATE": // need to convert back to datetime so it can be converted to time.Time return ql.MakeFormattedNode("STRFTIME('%%Y-%%m-%%dT00:00:00Z', %s)", f.Arguments...), nil @@ -133,3 +155,28 @@ func sqlFunctionHandler(f ql.Function) (ql.ASTNode, error) { return f, nil } + +func supportedDateFormatParams(fmtNode ql.ASTNode) error { + format := translateDateFormatParams(fmtNode.String()) + + r := regexp.MustCompile(`%(?P.)`) + + for _, m := range r.FindAllStringSubmatch(format, -1) { + if len(m) == 0 { + continue + } + + if _, ok := supportedSubstitutions[m[1]]; !ok { + return fmt.Errorf("format substitution not supported: %%%s", m[1]) + } + } + + return nil +} + +func translateDateFormatParams(format string) string { + return strings.NewReplacer( + `%i`, `%M`, + `%U`, `%W`, + ).Replace(format) +} diff --git a/store/tests/compose_records_test.go b/store/tests/compose_records_test.go index 9ce1150c1..2cf46c37c 100644 --- a/store/tests/compose_records_test.go +++ b/store/tests/compose_records_test.go @@ -1606,7 +1606,7 @@ func testComposeRecords(t *testing.T, s store.ComposeRecords) { req.NoError(err) req.Len(report, 3) - report, err = s.ComposeRecordReport(ctx, mod, "MAX(num1)", "DATE_FORMAT(dt1, '%y; %j @ %H %i %p')", "") + report, err = s.ComposeRecordReport(ctx, mod, "MAX(num1)", "DATE_FORMAT(dt1, '%Y; %j @ %H %i')", "") req.NoError(err) req.Len(report, 3) })