From 7360b72dbfac5277bf33c178f2485be5da2ead2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toma=C5=BE=20Jerman?= Date: Tue, 27 Sep 2022 22:32:02 +0200 Subject: [PATCH] Fix query interval definitions for date add/sub --- pkg/ql/ast_nodes.go | 5 ++- store/adapters/rdbms/dal/iterator.go | 1 + store/adapters/rdbms/drivers/postgres/ql.go | 44 ++++++++++++++++----- store/adapters/rdbms/ql/ref.go | 33 ++++++++++++++++ 4 files changed, 72 insertions(+), 11 deletions(-) diff --git a/pkg/ql/ast_nodes.go b/pkg/ql/ast_nodes.go index 9a7d57f42..680a69f0a 100644 --- a/pkg/ql/ast_nodes.go +++ b/pkg/ql/ast_nodes.go @@ -236,7 +236,10 @@ func (n interval) ToAST() (out *ASTNode) { return &ASTNode{ Ref: "interval", Args: ASTNodeSet{ - {Symbol: n.unit}, + // @todo consider introducing a new concept on the ASTNode to cover + // system defined symbols/keywords. This is a temporary solutions + // to fix reporting interval definitions; it should eventually be reworked + {Value: MakeValueOf("String", n.unit)}, {Value: MakeValueOf("Number", n.value)}, }, } diff --git a/store/adapters/rdbms/dal/iterator.go b/store/adapters/rdbms/dal/iterator.go index 952a2bb5f..8090f6a63 100644 --- a/store/adapters/rdbms/dal/iterator.go +++ b/store/adapters/rdbms/dal/iterator.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "fmt" + "github.com/cortezaproject/corteza-server/pkg/errors" "github.com/cortezaproject/corteza-server/pkg/dal" diff --git a/store/adapters/rdbms/drivers/postgres/ql.go b/store/adapters/rdbms/drivers/postgres/ql.go index 9b05cb2f6..6108635b6 100644 --- a/store/adapters/rdbms/drivers/postgres/ql.go +++ b/store/adapters/rdbms/drivers/postgres/ql.go @@ -63,17 +63,41 @@ var ( }, }, + "interval": { + Handler: func(args ...exp.Expression) exp.Expression { + // The problem here is that PGSQL, to my findings, doesn't have functions to add/sub dates + // like MySQL for example. + // + // We need to construct an expression in the lines of `INTERVAL 'N UNIT'` which + // then becomes, for example, d + INTERVAL 'N UNIT'. + // + // The problem #2 is that we can't just use value placeholders in string literals + // nor is there a 2 arg function to make an interval. There is a make_interval function + // but that one won't do. + // + // So... + // (? || 'S') makes the interval label a plural because MySQL uses singular and that + // is what QL supports. PgSQL uses plural, such as years, months, and days. + // + // The rest of the expression is just to construct the string which can then be casted to INTERVAL + // which can then be used in date math, which is done with regular math operators. + return exp.NewLiteralExpression("(?::INTEGER || ' ' || (? || 'S'))::INTERVAL", args[1], args[0]) + }, + }, + + "date_add": { + Handler: func(args ...exp.Expression) exp.Expression { + return exp.NewLiteralExpression("(? + ?)", args[0], args[1]) + }, + }, + + "date_sub": { + Handler: func(args ...exp.Expression) exp.Expression { + return exp.NewLiteralExpression("(? - ?)", args[0], args[1]) + }, + }, + // functions currently unsupported in PostgreSQL store backend - //"DATE_ADD": { - // Handler: func(args ...exp.Expression) exp.Expression { - // return exp.NewLiteralExpression("") - // }, - //}, - //"DATE_SUB": { - // Handler: func(args ...exp.Expression) exp.Expression { - // return exp.NewLiteralExpression("") - // }, - //}, //"STD": { // Handler: func(args ...exp.Expression) exp.Expression { // return exp.NewLiteralExpression("") diff --git a/store/adapters/rdbms/ql/ref.go b/store/adapters/rdbms/ql/ref.go index fb4372e0d..5600a4a59 100644 --- a/store/adapters/rdbms/ql/ref.go +++ b/store/adapters/rdbms/ql/ref.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/cortezaproject/corteza-server/pkg/ql" + "github.com/doug-martin/goqu/v9" "github.com/doug-martin/goqu/v9/exp" ) @@ -117,6 +118,38 @@ var ( }, }, + "interval": { + Handler: func(args ...exp.Expression) exp.Expression { + + // The problem here is similar to the one with PgSQL... but more complicated... + // The interval duration identifiers are keywords and can't be bound via value placeholders. + // This forces us to interpolate the thing when building the literal expression below. + // + // Since goqu doesn't let me get the raw value of the expression... I was forced + // to do this calamity + _, aa, err := goqu.Select(args[0]).ToSQL() + if err != nil { + // This error should never occur + panic(err) + } + intv := aa[0].(string) + + return exp.NewLiteralExpression(fmt.Sprintf("INTERVAL ? %s", intv), args[1]) + }, + }, + + "date_add": { + Handler: func(args ...exp.Expression) exp.Expression { + return exp.NewSQLFunctionExpression("DATE_ADD", args[0], args[1]) + }, + }, + + "date_sub": { + Handler: func(args ...exp.Expression) exp.Expression { + return exp.NewSQLFunctionExpression("DATE_SUB", args[0], args[1]) + }, + }, + // @todo better negation? "like": { Handler: func(args ...exp.Expression) exp.Expression {