Add explicit type cast to JSONB for PSQL

Explicit type casting was needed for timezone to cast into jsonb.
This commit is contained in:
Vivek Patel
2023-03-01 17:28:21 +05:30
parent 392d21d7af
commit 1e4089ff34
6 changed files with 19 additions and 11 deletions
+1 -1
View File
@@ -287,7 +287,7 @@ func (i *iterator) collectCursorValues(r dal.ValueGetter) (_ *filter.PagingCurso
}
if len(pKeys) == 0 {
//return nil, fmt.Errorf("can not construct cursor without primary key attributes")
// return nil, fmt.Errorf("can not construct cursor without primary key attributes")
}
for _, c := range i.sorting {
@@ -45,7 +45,7 @@ type (
// Literal values need to be JSON docs!
//
// @todo recheck if we really need JsonArrayContains on Dialect interface
JsonArrayContains(needle, haystack exp.Expression) (exp.Expression, error)
JsonArrayContains(needle, haystack exp.Expression, nodeRef string) (exp.Expression, error)
// AttributeCast prepares complex SQL expression that verifies
// arbitrary string value in the db and casts it to b used in
@@ -110,7 +110,9 @@ func OpHandlerNotIn(d Dialect, n *ql.ASTNode, args ...exp.Expression) (expr exp.
}
func opHandlerIn(d Dialect, n *ql.ASTNode, negate bool, args ...exp.Expression) (expr exp.Expression, err error) {
if len(n.Args) == 2 && n.Args[1] != nil && n.Args[1].Meta["dal.Attribute"] != nil && n.Args[1].Meta["dal.Attribute"].(*dal.Attribute).MultiValue {
// @todo: this needs to be refactor to handle json extraction for different types depending on the node layers
if len(n.Args) == 2 && n.Args[1] != nil && (n.Args[1].Meta["dal.Attribute"] != nil && n.Args[1].Meta["dal.Attribute"].(*dal.Attribute).MultiValue) || n.Args[1].Ref == "TIMESTAMP" {
nodeRef := n.Args[1].Ref
// if right-side argument is multi-value attribute,
// then we need to adjust the arguments a bit:
// 1) left side, if it is a value, is encoded as JSON
@@ -118,9 +120,6 @@ func opHandlerIn(d Dialect, n *ql.ASTNode, negate bool, args ...exp.Expression)
//
// right side, access JSON encoded array of values.
//
//
//
//
for a := range n.Args {
left := a == 0
@@ -180,7 +179,7 @@ func opHandlerIn(d Dialect, n *ql.ASTNode, negate bool, args ...exp.Expression)
}
}
expr, err = d.JsonArrayContains(args[0], args[1])
expr, err = d.JsonArrayContains(args[0], args[1], nodeRef)
if err != nil {
return
}
@@ -82,7 +82,7 @@ func (d mysqlDialect) JsonExtractUnquote(jsonDoc exp.Expression, pp ...any) (_ e
// JSON_CONTAINS(v, '"needle"', '$.f2')
//
// This approach is not optimal, but it is the only way to make it work
func (d mysqlDialect) JsonArrayContains(needle, haystack exp.Expression) (_ exp.Expression, err error) {
func (d mysqlDialect) JsonArrayContains(needle, haystack exp.Expression, _ string) (_ exp.Expression, err error) {
return exp.NewSQLFunctionExpression("JSON_CONTAINS", haystack, needle), nil
}
@@ -64,8 +64,16 @@ func (d postgresDialect) JsonExtractUnquote(ident exp.Expression, pp ...any) (ex
//
// single-value field = multi-value field / plain
// v->'f1'->0 <@ (v->'f0')::JSONB
func (d postgresDialect) JsonArrayContains(needle, haystack exp.Expression) (exp.Expression, error) {
return exp.NewLiteralExpression("(?)::JSONB <@ (?)::JSONB", needle, haystack), nil
//
// Timestamp field must to cast into text
// TO_JSONB('value') <@ TO_JSONB(v->'f0')
func (d postgresDialect) JsonArrayContains(needle, haystack exp.Expression, nodeRef string) (exp.Expression, error) {
switch nodeRef {
case "TIMESTAMP":
return exp.NewLiteralExpression("TO_JSONB(?) <@ TO_JSONB(?)", needle, haystack), nil
default:
return exp.NewLiteralExpression("(?)::jsonb <@ TO_JSONB(?)::jsonb", needle, haystack), nil
}
}
func (d postgresDialect) TableCodec(m *dal.Model) drivers.TableCodec {
@@ -79,7 +79,7 @@ func (sqliteDialect) JsonExtractUnquote(ident exp.Expression, pp ...any) (exp.Ex
//
// Unfortunately SQLite converts boolean values into 0 and 1 when decoding from
// JSON and we need a special handler for that.
func (sqliteDialect) JsonArrayContains(needle, haystack exp.Expression) (exp.Expression, error) {
func (sqliteDialect) JsonArrayContains(needle, haystack exp.Expression, _ string) (exp.Expression, error) {
// @todo should be implemented using native SQLite capabilties and
// not through custom JSON_ARRAY_CONTAINS function
return exp.NewLiteralExpression("JSON_ARRAY_CONTAINS(?, ?)", needle, haystack), nil
@@ -69,6 +69,7 @@ func TestJSONOp(t *testing.T) {
contains, err := conn.dialect.JsonArrayContains(
exp.NewLiteralExpression("?", val),
a2e(attr),
"",
)
req.NoError(err)