From 3fcbbec427774fa8bd3e812a4234dcece791456f Mon Sep 17 00:00:00 2001 From: go-jet Date: Sun, 6 Oct 2024 14:21:42 +0200 Subject: [PATCH] Add support for Row expression. --- internal/jet/func_expression.go | 7 +- internal/jet/literal_expression.go | 26 ------- internal/jet/order_set_aggregate_functions.go | 7 +- internal/jet/row_expression.go | 78 +++++++++++++++++++ mysql/expressions.go | 8 ++ postgres/expressions.go | 8 ++ postgres/literal.go | 10 +++ sqlite/expressions.go | 8 ++ sqlite/functions.go | 6 +- tests/mysql/alltypes_test.go | 39 +++++++++- tests/mysql/with_test.go | 4 +- tests/postgres/alltypes_test.go | 50 ++++++++++-- tests/postgres/update_test.go | 5 +- tests/postgres/with_test.go | 12 +-- tests/sqlite/alltypes_test.go | 41 +++++++++- tests/sqlite/with_test.go | 8 +- 16 files changed, 254 insertions(+), 63 deletions(-) create mode 100644 internal/jet/row_expression.go diff --git a/internal/jet/func_expression.go b/internal/jet/func_expression.go index 7e49880..ddc579e 100644 --- a/internal/jet/func_expression.go +++ b/internal/jet/func_expression.go @@ -12,11 +12,6 @@ func OR(expressions ...BoolExpression) BoolExpression { return newBoolExpressionListOperator("OR", expressions...) } -// ROW function is used to create a tuple value that consists of a set of expressions or column values. -func ROW(expressions ...Expression) Expression { - return NewFunc("ROW", expressions, nil) -} - // ------------------ Mathematical functions ---------------// // ABSf calculates absolute value from float expression @@ -711,7 +706,7 @@ func (p parametersSerializer) serialize(statement StatementType, out *SQLBuilder if _, isStatement := expression.(Statement); isStatement { expression.serialize(statement, out, options...) } else { - skipWrap(expression).serialize(statement, out, options...) + expression.serialize(statement, out, append(options, NoWrap, Ident)...) } } } diff --git a/internal/jet/literal_expression.go b/internal/jet/literal_expression.go index d6f0b41..251d3ab 100644 --- a/internal/jet/literal_expression.go +++ b/internal/jet/literal_expression.go @@ -374,32 +374,6 @@ func (n *starLiteral) serialize(statement StatementType, out *SQLBuilder, option //---------------------------------------------------// -type wrap struct { - ExpressionInterfaceImpl - expressions []Expression -} - -func (n *wrap) serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) { - out.WriteString("(") - - if len(n.expressions) == 1 { - options = append(options, NoWrap, Ident) - } - serializeExpressionList(statementType, n.expressions, ", ", out, options...) - - out.WriteString(")") -} - -// WRAP wraps list of expressions with brackets - ( expression1, expression2, ... ) -func WRAP(expression ...Expression) Expression { - wrap := &wrap{expressions: expression} - wrap.ExpressionInterfaceImpl.Parent = wrap - - return wrap -} - -//---------------------------------------------------// - type rawExpression struct { ExpressionInterfaceImpl diff --git a/internal/jet/order_set_aggregate_functions.go b/internal/jet/order_set_aggregate_functions.go index 8ce5d1e..eff954a 100644 --- a/internal/jet/order_set_aggregate_functions.go +++ b/internal/jet/order_set_aggregate_functions.go @@ -54,7 +54,12 @@ type orderSetAggregateFuncExpression struct { func (p *orderSetAggregateFuncExpression) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) { out.WriteString(p.name) - WRAP(p.fraction).serialize(statement, out, FallTrough(options)...) + + if p.fraction != nil { + WRAP(p.fraction).serialize(statement, out, FallTrough(options)...) + } else { + WRAP().serialize(statement, out, FallTrough(options)...) + } out.WriteString("WITHIN GROUP") p.orderBy.serialize(statement, out) } diff --git a/internal/jet/row_expression.go b/internal/jet/row_expression.go new file mode 100644 index 0000000..819cf15 --- /dev/null +++ b/internal/jet/row_expression.go @@ -0,0 +1,78 @@ +package jet + +// RowExpression interface +type RowExpression interface { + Expression + + EQ(rhs RowExpression) BoolExpression + NOT_EQ(rhs RowExpression) BoolExpression + IS_DISTINCT_FROM(rhs RowExpression) BoolExpression + IS_NOT_DISTINCT_FROM(rhs RowExpression) BoolExpression + + LT(rhs RowExpression) BoolExpression + LT_EQ(rhs RowExpression) BoolExpression + GT(rhs RowExpression) BoolExpression + GT_EQ(rhs RowExpression) BoolExpression +} + +type rowInterfaceImpl struct { + parent RowExpression +} + +func (n *rowInterfaceImpl) EQ(rhs RowExpression) BoolExpression { + return Eq(n.parent, rhs) +} + +func (n *rowInterfaceImpl) NOT_EQ(rhs RowExpression) BoolExpression { + return NotEq(n.parent, rhs) +} + +func (n *rowInterfaceImpl) IS_DISTINCT_FROM(rhs RowExpression) BoolExpression { + return IsDistinctFrom(n.parent, rhs) +} + +func (n *rowInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs RowExpression) BoolExpression { + return IsNotDistinctFrom(n.parent, rhs) +} + +func (n *rowInterfaceImpl) GT(rhs RowExpression) BoolExpression { + return Gt(n.parent, rhs) +} + +func (n *rowInterfaceImpl) GT_EQ(rhs RowExpression) BoolExpression { + return GtEq(n.parent, rhs) +} + +func (n *rowInterfaceImpl) LT(rhs RowExpression) BoolExpression { + return Lt(n.parent, rhs) +} + +func (n *rowInterfaceImpl) LT_EQ(rhs RowExpression) BoolExpression { + return LtEq(n.parent, rhs) +} + +//---------------------------------------------------// + +type rowExpressionWrapper struct { + rowInterfaceImpl + Expression +} + +// RowExp serves as a wrapper for an arbitrary expression, treating it as a row expression. +// This enables the Go compiler to interpret any expression as a row expression +// Note: This does not modify the generated SQL builder output by adding a SQL CAST operation. +func RowExp(expression Expression) RowExpression { + rowExpressionWrap := rowExpressionWrapper{Expression: expression} + rowExpressionWrap.rowInterfaceImpl.parent = &rowExpressionWrap + return &rowExpressionWrap +} + +// ROW function is used to create a tuple value that consists of a set of expressions or column values. +func ROW(expressions ...Expression) RowExpression { + return RowExp(NewFunc("ROW", expressions, nil)) +} + +// WRAP creates row expressions without ROW keyword `( expression1, expression2, ... )`. +func WRAP(expressions ...Expression) RowExpression { + return RowExp(NewFunc("", expressions, nil)) +} diff --git a/mysql/expressions.go b/mysql/expressions.go index 53b1fa7..4073ef5 100644 --- a/mysql/expressions.go +++ b/mysql/expressions.go @@ -30,6 +30,9 @@ type DateTimeExpression = jet.TimestampExpression // TimestampExpression interface type TimestampExpression = jet.TimestampExpression +// RowExpression interface +type RowExpression = jet.RowExpression + // BoolExp is bool expression wrapper around arbitrary expression. // Allows go compiler to see any expression as bool expression. // Does not add sql cast to generated sql builder output. @@ -70,6 +73,11 @@ var DateTimeExp = jet.TimestampExp // Does not add sql cast to generated sql builder output. var TimestampExp = jet.TimestampExp +// RowExp serves as a wrapper for an arbitrary expression, treating it as a row expression. +// This enables the Go compiler to interpret any expression as a row expression +// Note: This does not modify the generated SQL builder output by adding a SQL CAST operation. +var RowExp = jet.RowExp + // CustomExpression is used to define custom expressions. var CustomExpression = jet.CustomExpression diff --git a/postgres/expressions.go b/postgres/expressions.go index 9872910..d8ad34b 100644 --- a/postgres/expressions.go +++ b/postgres/expressions.go @@ -36,6 +36,9 @@ type TimestampExpression = jet.TimestampExpression // TimestampzExpression interface type TimestampzExpression = jet.TimestampzExpression +// RowExpression interface +type RowExpression = jet.RowExpression + // DateRange Expression interface type DateRange = jet.Range[DateExpression] @@ -99,6 +102,11 @@ var TimestampExp = jet.TimestampExp // Does not add sql cast to generated sql builder output. var TimestampzExp = jet.TimestampzExp +// RowExp serves as a wrapper for an arbitrary expression, treating it as a row expression. +// This enables the Go compiler to interpret any expression as a row expression +// Note: This does not modify the generated SQL builder output by adding a SQL CAST operation. +var RowExp = jet.RowExp + // RangeExp is range expression wrapper around arbitrary expression. // Allows go compiler to see any expression as range expression. // Does not add sql cast to generated sql builder output. diff --git a/postgres/literal.go b/postgres/literal.go index e3a95b3..26b75d8 100644 --- a/postgres/literal.go +++ b/postgres/literal.go @@ -57,6 +57,16 @@ func Uint64(value uint64) IntegerExpression { // Float creates new float literal expression var Float = jet.Float +// Float32 is constructor for 32 bit float literals +func Float32(value float32) FloatExpression { + return CAST(jet.Literal(value)).AS_REAL() +} + +// Float64 is constructor for 64 bit float literals +func Float64(value float64) FloatExpression { + return CAST(jet.Literal(value)).AS_DOUBLE() +} + // Decimal creates new float literal expression var Decimal = jet.Decimal diff --git a/sqlite/expressions.go b/sqlite/expressions.go index 42ccc96..0b2d320 100644 --- a/sqlite/expressions.go +++ b/sqlite/expressions.go @@ -33,6 +33,9 @@ type DateTimeExpression = jet.TimestampExpression // TimestampExpression interface type TimestampExpression = jet.TimestampExpression +// RowExpression interface +type RowExpression = jet.RowExpression + // BoolExp is bool expression wrapper around arbitrary expression. // Allows go compiler to see any expression as bool expression. // Does not add sql cast to generated sql builder output. @@ -73,6 +76,11 @@ var DateTimeExp = jet.TimestampExp // Does not add sql cast to generated sql builder output. var TimestampExp = jet.TimestampExp +// RowExp serves as a wrapper for an arbitrary expression, treating it as a row expression. +// This enables the Go compiler to interpret any expression as a row expression +// Note: This does not modify the generated SQL builder output by adding a SQL CAST operation. +var RowExp = jet.RowExp + // CustomExpression is used to define custom expressions. var CustomExpression = jet.CustomExpression diff --git a/sqlite/functions.go b/sqlite/functions.go index 47a0a5b..a76f236 100644 --- a/sqlite/functions.go +++ b/sqlite/functions.go @@ -15,10 +15,8 @@ var ( OR = jet.OR ) -// ROW is construct one table row from list of expressions. -func ROW(expressions ...Expression) Expression { - return jet.NewFunc("", expressions, nil) -} +// ROW is construct one row from a list of expressions. +var ROW = jet.WRAP // ------------------ Mathematical functions ---------------// diff --git a/tests/mysql/alltypes_test.go b/tests/mysql/alltypes_test.go index d5702bd..61bc7f2 100644 --- a/tests/mysql/alltypes_test.go +++ b/tests/mysql/alltypes_test.go @@ -97,18 +97,18 @@ func TestExpressionOperators(t *testing.T) { SELECT all_types.'integer' IS NULL AS "result.is_null", all_types.date_ptr IS NOT NULL AS "result.is_not_null", (all_types.small_int_ptr IN (?, ?)) AS "result.in", - (all_types.small_int_ptr IN ( + (all_types.small_int_ptr IN (( SELECT all_types.'integer' AS "all_types.integer" FROM test_sample.all_types - )) AS "result.in_select", + ))) AS "result.in_select", (CURRENT_USER()) AS "result.raw", (? + COALESCE(all_types.small_int_ptr, 0) + ?) AS "result.raw_arg", (? + all_types.integer + ? + ? + ? + ?) AS "result.raw_arg2", (all_types.small_int_ptr NOT IN (?, ?, NULL)) AS "result.not_in", - (all_types.small_int_ptr NOT IN ( + (all_types.small_int_ptr NOT IN (( SELECT all_types.'integer' AS "all_types.integer" FROM test_sample.all_types - )) AS "result.not_in_select" + ))) AS "result.not_in_select" FROM test_sample.all_types LIMIT ?; `, "'", "`", -1), int64(11), int64(22), 78, 56, 11, 22, 11, 33, 44, int64(11), int64(22), int64(2)) @@ -1404,3 +1404,34 @@ VALUES ('91.23', '45.67', '12.35', '56.79', 0.2, 0.22, 0.3, 0.33, 0.4, 0.44); require.Equal(t, 45.67, *result.Floats.DecimalPtr) }) } + +func TestRowExpression(t *testing.T) { + now := time.Now() + nowAddHour := time.Now().Add(time.Hour) + + stmt := SELECT( + ROW(Bool(false), DateT(now)).EQ(ROW(Bool(true), DateT(now))), + ROW(Bool(false), DateT(now)).NOT_EQ(ROW(Bool(true), DateT(now))), + ROW(TimestampT(nowAddHour), String("txt")).IS_DISTINCT_FROM(RowExp(Raw("row(NOW(), 'png')"))), + ROW(TimestampT(now), DateTimeT(nowAddHour)).GT(ROW(TimestampT(now), DateTimeT(now))), + ROW(DateTimeT(nowAddHour), Int(1)).GT_EQ(ROW(DateTimeT(now), Int(2))), + ROW(TimestampT(now), DateTimeT(nowAddHour)).LT(ROW(TimestampT(now), DateTimeT(now))), + ROW(DateTimeT(nowAddHour), Float(1.22)).LT_EQ(ROW(DateTimeT(now), Float(2.33))), + ) + + //fmt.Println(stmt.Sql()) + //fmt.Println(stmt.DebugSql()) + + testutils.AssertStatementSql(t, stmt, ` +SELECT ROW(?, CAST(? AS DATE)) = ROW(?, CAST(? AS DATE)), + ROW(?, CAST(? AS DATE)) != ROW(?, CAST(? AS DATE)), + NOT(ROW(TIMESTAMP(?), ?) <=> (row(NOW(), 'png'))), + ROW(TIMESTAMP(?), CAST(? AS DATETIME)) > ROW(TIMESTAMP(?), CAST(? AS DATETIME)), + ROW(CAST(? AS DATETIME), ?) >= ROW(CAST(? AS DATETIME), ?), + ROW(TIMESTAMP(?), CAST(? AS DATETIME)) < ROW(TIMESTAMP(?), CAST(? AS DATETIME)), + ROW(CAST(? AS DATETIME), ?) <= ROW(CAST(? AS DATETIME), ?); +`) + + err := stmt.Query(db, &struct{}{}) + require.NoError(t, err) +} diff --git a/tests/mysql/with_test.go b/tests/mysql/with_test.go index d7d8d3d..59da3a5 100644 --- a/tests/mysql/with_test.go +++ b/tests/mysql/with_test.go @@ -164,10 +164,10 @@ WITH payments_to_delete AS ( WHERE payment.amount < 0.5 ) DELETE FROM dvds.payment -WHERE payment.payment_id IN ( +WHERE payment.payment_id IN (( SELECT payments_to_delete.''payment.payment_id'' AS "payment.payment_id" FROM payments_to_delete - ); + )); `, "''", "`")) tx, err := db.Begin() diff --git a/tests/postgres/alltypes_test.go b/tests/postgres/alltypes_test.go index 70c4332..07350b8 100644 --- a/tests/postgres/alltypes_test.go +++ b/tests/postgres/alltypes_test.go @@ -347,24 +347,24 @@ func TestExpressionOperators(t *testing.T) { AllTypes.SmallIntPtr.NOT_IN(AllTypes.SELECT(AllTypes.Integer)).AS("result.not_in_select"), ).LIMIT(2) - //fmt.Println(query.Sql()) + // fmt.Println(query.Sql()) testutils.AssertStatementSql(t, query, ` SELECT all_types.integer IS NULL AS "result.is_null", all_types.date_ptr IS NOT NULL AS "result.is_not_null", (all_types.small_int_ptr IN ($1::smallint, $2::smallint)) AS "result.in", - (all_types.small_int_ptr IN ( + (all_types.small_int_ptr IN (( SELECT all_types.integer AS "all_types.integer" FROM test_sample.all_types - )) AS "result.in_select", + ))) AS "result.in_select", (CURRENT_USER) AS "result.raw", ($3 + COALESCE(all_types.small_int_ptr, 0) + $4) AS "result.raw_arg", ($5 + all_types.integer + $6 + $5 + $7 + $8) AS "result.raw_arg2", (all_types.small_int_ptr NOT IN ($9, $10::smallint, NULL)) AS "result.not_in", - (all_types.small_int_ptr NOT IN ( + (all_types.small_int_ptr NOT IN (( SELECT all_types.integer AS "all_types.integer" FROM test_sample.all_types - )) AS "result.not_in_select" + ))) AS "result.not_in_select" FROM test_sample.all_types LIMIT $11; `, int8(11), int8(22), 78, 56, 11, 22, 33, 44, int64(11), int16(22), int64(2)) @@ -1111,6 +1111,46 @@ FROM test_sample.all_types; require.NoError(t, err) } +func TestRowExpression(t *testing.T) { + now := time.Now() + nowAddHour := time.Now().Add(time.Hour) + + stmt := SELECT( + ROW(Int32(1), Float32(11.22), String("john")).AS("row"), + WRAP(Int64(1), Float64(11.22), String("john")).AS("wrap"), + + ROW(Bool(false), DateT(now)).EQ(ROW(Bool(true), DateT(now))), + WRAP(Bool(false), DateT(now)).NOT_EQ(WRAP(Bool(true), DateT(now))), + + ROW(TimeT(nowAddHour)).IS_DISTINCT_FROM(RowExp(Raw("row(NOW()::time)"))), + ROW().IS_NOT_DISTINCT_FROM(ROW()), + + ROW(TimestampT(now), TimestampzT(nowAddHour)).GT(WRAP(TimestampT(now), TimestampzT(now))), + ROW(TimestampzT(nowAddHour)).GT_EQ(ROW(TimestampzT(now))), + WRAP(TimestampT(now), TimestampzT(nowAddHour)).LT(ROW(TimestampT(now), TimestampzT(now))), + ROW(TimestampzT(nowAddHour)).LT_EQ(ROW(TimestampzT(now))), + ) + + //fmt.Println(stmt.Sql()) + //fmt.Println(stmt.DebugSql()) + + testutils.AssertStatementSql(t, stmt, ` +SELECT ROW($1::integer, $2::real, $3::text) AS "row", + ($4::bigint, $5::double precision, $6::text) AS "wrap", + ROW($7::boolean, $8::date) = ROW($9::boolean, $10::date), + ($11::boolean, $12::date) != ($13::boolean, $14::date), + ROW($15::time without time zone) IS DISTINCT FROM (row(NOW()::time)), + ROW() IS NOT DISTINCT FROM ROW(), + ROW($16::timestamp without time zone, $17::timestamp with time zone) > ($18::timestamp without time zone, $19::timestamp with time zone), + ROW($20::timestamp with time zone) >= ROW($21::timestamp with time zone), + ($22::timestamp without time zone, $23::timestamp with time zone) < ROW($24::timestamp without time zone, $25::timestamp with time zone), + ROW($26::timestamp with time zone) <= ROW($27::timestamp with time zone); +`) + + err := stmt.Query(db, &struct{}{}) + require.NoError(t, err) +} + func TestSubQueryColumnReference(t *testing.T) { type expected struct { sql string diff --git a/tests/postgres/update_test.go b/tests/postgres/update_test.go index e0c7da2..103b420 100644 --- a/tests/postgres/update_test.go +++ b/tests/postgres/update_test.go @@ -344,7 +344,10 @@ func TestUpdateExecContext(t *testing.T) { time.Sleep(10 * time.Millisecond) - testutils.AssertExecContextErr(ctx, t, updateStmt, db, "context deadline exceeded") + testutils.ExecuteInTxAndRollback(t, db, func(tx *sql.Tx) { + _, err := updateStmt.ExecContext(ctx, tx) + require.Error(t, err, "context deadline exceeded") + }) } func TestUpdateFrom(t *testing.T) { diff --git a/tests/postgres/with_test.go b/tests/postgres/with_test.go index 21fca32..92b5649 100644 --- a/tests/postgres/with_test.go +++ b/tests/postgres/with_test.go @@ -83,10 +83,10 @@ SELECT orders.ship_region AS "orders.ship_region", SUM(order_details.quantity) AS "product_sales" FROM northwind.orders INNER JOIN northwind.order_details ON (orders.order_id = order_details.order_id) -WHERE orders.ship_region IN ( +WHERE orders.ship_region IN (( SELECT top_region."orders.ship_region" AS "orders.ship_region" FROM top_region - ) + )) GROUP BY orders.ship_region, order_details.product_id ORDER BY SUM(order_details.quantity) DESC; `) @@ -157,19 +157,19 @@ func TestWithStatementDeleteAndInsert(t *testing.T) { testutils.AssertStatementSql(t, stmt, ` WITH remove_discontinued_orders AS ( DELETE FROM northwind.order_details - WHERE order_details.product_id IN ( + WHERE order_details.product_id IN (( SELECT products.product_id AS "products.product_id" FROM northwind.products WHERE products.discontinued = $1 - ) + )) RETURNING order_details.product_id AS "order_details.product_id" ),update_discontinued_price AS ( UPDATE northwind.products SET unit_price = $2 - WHERE products.product_id IN ( + WHERE products.product_id IN (( SELECT remove_discontinued_orders."order_details.product_id" AS "order_details.product_id" FROM remove_discontinued_orders - ) + )) RETURNING products.product_id AS "products.product_id", products.product_name AS "products.product_name", products.supplier_id AS "products.supplier_id", diff --git a/tests/sqlite/alltypes_test.go b/tests/sqlite/alltypes_test.go index 41e5cf7..080e870 100644 --- a/tests/sqlite/alltypes_test.go +++ b/tests/sqlite/alltypes_test.go @@ -234,18 +234,18 @@ func TestExpressionOperators(t *testing.T) { SELECT all_types.integer IS NULL AS "result.is_null", all_types.date_ptr IS NOT NULL AS "result.is_not_null", (all_types.small_int_ptr IN (?, ?)) AS "result.in", - (all_types.small_int_ptr IN ( + (all_types.small_int_ptr IN (( SELECT all_types.integer AS "all_types.integer" FROM all_types - )) AS "result.in_select", + ))) AS "result.in_select", (length(121232459)) AS "result.raw", (? + COALESCE(all_types.small_int_ptr, 0) + ?) AS "result.raw_arg", (? + all_types.integer + ? + ? + ? + ?) AS "result.raw_arg2", (all_types.small_int_ptr NOT IN (?, ?, NULL)) AS "result.not_in", - (all_types.small_int_ptr NOT IN ( + (all_types.small_int_ptr NOT IN (( SELECT all_types.integer AS "all_types.integer" FROM all_types - )) AS "result.not_in_select" + ))) AS "result.not_in_select" FROM all_types LIMIT ?; `, "'", "`", -1), int64(11), int64(22), 78, 56, 11, 22, 11, 33, 44, int64(11), int64(22), int64(2)) @@ -900,3 +900,36 @@ func TestDateTimeExpressions(t *testing.T) { require.Equal(t, dest.JulianDay, 2.4551543576232754e+06) require.Equal(t, dest.StrfTime, "20:34") } + +func TestRowExpression(t *testing.T) { + date := Date(2000, 9, 9) + time := Time(11, 22, 11) + dateTime := DateTime(2008, 11, 22, 10, 12, 40) + dateTime2 := DateTime(2011, 1, 2, 5, 12, 40) + + stmt := SELECT( + ROW(Bool(false), date).EQ(ROW(Bool(true), date)), + ROW(Bool(false), time).NOT_EQ(ROW(Bool(true), time)), + ROW(time).IS_DISTINCT_FROM(RowExp(Raw("(time('now'))"))), + ROW(dateTime, dateTime2).GT(ROW(dateTime, dateTime2)), + ROW(dateTime2).GT_EQ(ROW(dateTime)), + ROW(dateTime, dateTime2).LT(ROW(dateTime, dateTime2)), + ROW(dateTime2).LT_EQ(ROW(dateTime2)), + ) + + //fmt.Println(stmt.Sql()) + //fmt.Println(stmt.DebugSql()) + + testutils.AssertDebugStatementSql(t, stmt, ` +SELECT (FALSE, DATE('2000-09-09')) = (TRUE, DATE('2000-09-09')), + (FALSE, TIME('11:22:11')) != (TRUE, TIME('11:22:11')), + (TIME('11:22:11')) IS NOT ((time('now'))), + (DATETIME('2008-11-22 10:12:40'), DATETIME('2011-01-02 05:12:40')) > (DATETIME('2008-11-22 10:12:40'), DATETIME('2011-01-02 05:12:40')), + (DATETIME('2011-01-02 05:12:40')) >= (DATETIME('2008-11-22 10:12:40')), + (DATETIME('2008-11-22 10:12:40'), DATETIME('2011-01-02 05:12:40')) < (DATETIME('2008-11-22 10:12:40'), DATETIME('2011-01-02 05:12:40')), + (DATETIME('2011-01-02 05:12:40')) <= (DATETIME('2011-01-02 05:12:40')); +`) + + err := stmt.Query(db, &struct{}{}) + require.NoError(t, err) +} diff --git a/tests/sqlite/with_test.go b/tests/sqlite/with_test.go index 402df2f..485655c 100644 --- a/tests/sqlite/with_test.go +++ b/tests/sqlite/with_test.go @@ -153,10 +153,10 @@ WITH payments_to_update AS ( ) UPDATE payment SET amount = 0 -WHERE payment.payment_id IN ( +WHERE payment.payment_id IN (( SELECT payments_to_update.''payment.payment_id'' AS "payment.payment_id" FROM payments_to_update - ); + )); `, "''", "`", -1)) tx := beginDBTx(t) @@ -205,10 +205,10 @@ WITH payments_to_delete AS ( WHERE payment.amount < 0.5 ) DELETE FROM payment -WHERE payment.payment_id IN ( +WHERE payment.payment_id IN (( SELECT payments_to_delete.''payment.payment_id'' AS "payment.payment_id" FROM payments_to_delete - ); + )); `, "''", "`", -1)) tx := beginDBTx(t)