From 1e4089ff343b62b84feeb32140c074b8e811f868 Mon Sep 17 00:00:00 2001 From: Vivek Patel Date: Tue, 28 Feb 2023 17:56:28 +0530 Subject: [PATCH] Add explicit type cast to JSONB for PSQL Explicit type casting was needed for timezone to cast into jsonb. --- server/store/adapters/rdbms/dal/iterator.go | 2 +- server/store/adapters/rdbms/drivers/dialect.go | 11 +++++------ server/store/adapters/rdbms/drivers/mysql/dialect.go | 2 +- .../store/adapters/rdbms/drivers/postgres/dialect.go | 12 ++++++++++-- .../store/adapters/rdbms/drivers/sqlite/dialect.go | 2 +- .../adapters/rdbms/drivers/tests/dialect_test.go | 1 + 6 files changed, 19 insertions(+), 11 deletions(-) diff --git a/server/store/adapters/rdbms/dal/iterator.go b/server/store/adapters/rdbms/dal/iterator.go index 5ed3a6f15..a55450d44 100644 --- a/server/store/adapters/rdbms/dal/iterator.go +++ b/server/store/adapters/rdbms/dal/iterator.go @@ -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 { diff --git a/server/store/adapters/rdbms/drivers/dialect.go b/server/store/adapters/rdbms/drivers/dialect.go index f194b4052..7fb52e122 100644 --- a/server/store/adapters/rdbms/drivers/dialect.go +++ b/server/store/adapters/rdbms/drivers/dialect.go @@ -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 } diff --git a/server/store/adapters/rdbms/drivers/mysql/dialect.go b/server/store/adapters/rdbms/drivers/mysql/dialect.go index a5e14a9b5..108678f2a 100644 --- a/server/store/adapters/rdbms/drivers/mysql/dialect.go +++ b/server/store/adapters/rdbms/drivers/mysql/dialect.go @@ -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 } diff --git a/server/store/adapters/rdbms/drivers/postgres/dialect.go b/server/store/adapters/rdbms/drivers/postgres/dialect.go index 0bb9233a0..7b01df145 100644 --- a/server/store/adapters/rdbms/drivers/postgres/dialect.go +++ b/server/store/adapters/rdbms/drivers/postgres/dialect.go @@ -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 { diff --git a/server/store/adapters/rdbms/drivers/sqlite/dialect.go b/server/store/adapters/rdbms/drivers/sqlite/dialect.go index a6c9b38d6..6accb0667 100644 --- a/server/store/adapters/rdbms/drivers/sqlite/dialect.go +++ b/server/store/adapters/rdbms/drivers/sqlite/dialect.go @@ -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 diff --git a/server/store/adapters/rdbms/drivers/tests/dialect_test.go b/server/store/adapters/rdbms/drivers/tests/dialect_test.go index d441544e1..80dd66eb3 100644 --- a/server/store/adapters/rdbms/drivers/tests/dialect_test.go +++ b/server/store/adapters/rdbms/drivers/tests/dialect_test.go @@ -69,6 +69,7 @@ func TestJSONOp(t *testing.T) { contains, err := conn.dialect.JsonArrayContains( exp.NewLiteralExpression("?", val), a2e(attr), + "", ) req.NoError(err)