Add support for Row expression.

This commit is contained in:
go-jet 2024-10-06 14:21:42 +02:00
parent 58a386a3dd
commit 3fcbbec427
16 changed files with 254 additions and 63 deletions

View file

@ -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)
}

View file

@ -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()

View file

@ -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

View file

@ -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) {

View file

@ -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",

View file

@ -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)
}

View file

@ -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)