3
0

Allow format QL nodes to properly format their arguents

This commit is contained in:
Tomaž Jerman 2021-12-20 14:06:05 +01:00
parent 3a1d62d2a1
commit d700d76c39
4 changed files with 58 additions and 25 deletions

View File

@ -2,8 +2,9 @@ package ql
import (
"fmt"
"github.com/Masterminds/squirrel"
"strings"
"github.com/Masterminds/squirrel"
)
// SelectStatement represents a SQL SELECT statement.
@ -15,6 +16,8 @@ type (
Validate() error
}
replacer func(string) string
ASTSet []ASTNode // Stream of comma delimited nodes
ASTNodes []ASTNode // Stream of space delimited nodes
@ -63,6 +66,7 @@ type (
NodeF struct {
Expr string
Arguments []ASTNode
replacer replacer
}
)
@ -219,6 +223,11 @@ func (nn Columns) Strings() (out []string) {
return
}
// MakeReplacedFormattedNode also accepts the replacer to apply to the arguments
func MakeReplacedFormattedNode(expr string, r replacer, nn ...ASTNode) *NodeF {
return &NodeF{Expr: expr, Arguments: nn, replacer: r}
}
func MakeFormattedNode(expr string, nn ...ASTNode) *NodeF {
return &NodeF{Expr: expr, Arguments: nn}
}

View File

@ -127,7 +127,18 @@ func (n NodeF) ToSql() (string, []interface{}, error) {
adtArgs []interface{}
)
for _, s := range n.Arguments {
for i, s := range n.Arguments {
// When provided, apply the replacer over the arguments of the node.
// We can skip any node other then LString as thats the only one we can apply it to (currently)
if n.replacer != nil {
if c, ok := s.(LString); ok {
c.Value = n.replacer(c.Value)
// Updating the originals as we're dealing with values
n.Arguments[i] = c
s = c
}
}
if fa, aa, err := s.ToSql(); err != nil {
return "", nil, err
} else {

View File

@ -42,7 +42,7 @@ func sqlFunctionHandler(f ql.Function) (ql.ASTNode, error) {
case "QUARTER", "YEAR":
return ql.MakeFormattedNode(fmt.Sprintf("EXTRACT(%s FROM %%s::date)", f.Name), f.Arguments...), nil
case "DATE_FORMAT":
return ql.MakeFormattedNode("TO_CHAR(%s, %s)", f.Arguments...), nil
return ql.MakeReplacedFormattedNode("TO_CHAR(%s, %s)", translateDateFormatParams, f.Arguments...), nil
case "DATE":
return ql.MakeFormattedNode("%s::DATE", f.Arguments...), nil
case "DATE_ADD", "DATE_SUB", "STD":

View File

@ -1567,35 +1567,48 @@ func testComposeRecords(t *testing.T, s store.ComposeRecords) {
report []map[string]interface{}
)
report, err = s.ComposeRecordReport(ctx, mod, "MAX(num1)", "QUARTER(dt1)", "")
req.NoError(err)
req.Len(report, 3)
t.Run("base", func(t *testing.T) {
report, err = s.ComposeRecordReport(ctx, mod, "MAX(num1)", "QUARTER(dt1)", "")
req.NoError(err)
req.Len(report, 3)
// @todo find a way to compare the results
// @todo find a way to compare the results
//expected := []map[string]interface{}{
// {"count": 3, "dimension_0": 1, "metric_0": 3},
// {"count": 2, "dimension_0": 2, "metric_0": 5},
// {"count": 1, "dimension_0": nil, "metric_0": nil},
//}
//
//req.True(
// reflect.DeepEqual(report, expected),
// "report does not match expected results:\n%#v\n%#v", report, expected)
//expected := []map[string]interface{}{
// {"count": 3, "dimension_0": 1, "metric_0": 3},
// {"count": 2, "dimension_0": 2, "metric_0": 5},
// {"count": 1, "dimension_0": nil, "metric_0": nil},
//}
//
//req.True(
// reflect.DeepEqual(report, expected),
// "report does not match expected results:\n%#v\n%#v", report, expected)
report, err = s.ComposeRecordReport(ctx, mod, "COUNT(num1)", "YEAR(dt1)", "")
req.NoError(err)
report, err = s.ComposeRecordReport(ctx, mod, "COUNT(num1)", "YEAR(dt1)", "")
req.NoError(err)
report, err = s.ComposeRecordReport(ctx, mod, "SUM(num1)", "DATE(dt1)", "")
req.NoError(err)
report, err = s.ComposeRecordReport(ctx, mod, "SUM(num1)", "DATE(dt1)", "")
req.NoError(err)
report, err = s.ComposeRecordReport(ctx, mod, "MIN(num1)", "DATE(NOW())", "")
req.NoError(err)
report, err = s.ComposeRecordReport(ctx, mod, "MIN(num1)", "DATE(NOW())", "")
req.NoError(err)
report, err = s.ComposeRecordReport(ctx, mod, "AVG(num1)", "DATE(NOW())", "")
req.NoError(err)
report, err = s.ComposeRecordReport(ctx, mod, "AVG(num1)", "DATE(NOW())", "")
req.NoError(err)
// Note that not all functions are compatible across all backends
})
t.Run("date formatting", func(t *testing.T) {
report, err = s.ComposeRecordReport(ctx, mod, "MAX(num1)", "DATE_FORMAT(dt1, '%Y-%m-01')", "")
req.NoError(err)
req.Len(report, 3)
report, err = s.ComposeRecordReport(ctx, mod, "MAX(num1)", "DATE_FORMAT(dt1, '%y; %j @ %H %i %p')", "")
req.NoError(err)
req.Len(report, 3)
})
// Note that not all functions are compatible across all backends
})
t.Run("partial value update", func(t *testing.T) {