Files
db/dialect/duckdb/duckdb_test.go
T
what 37581f3a65 feat: 新增 BooleanFunctionLookup 支持函数式操作符
- sqlgen: 新增 BooleanFunctionLookup,生成 FUNC(LHS, RHS) 格式,优先级高于 BooleanOperatorLookup
- dialect/duckdb: 使用 regexp_matches 替代 ~ 实现部分匹配,等价于 MySQL REGEXP
- sqlgen/expression_sql_generator_test: 补充 BooleanFunctionLookup 测试用例,修正 encode_error 前缀
- sqlgen/sql_dialect_options_test: 验证 BooleanFunctionLookup 默认为 nil
- dialect/duckdb_test: 新增 regexp_matches SQL 生成测试
2026-05-21 13:34:04 +08:00

162 lines
4.7 KiB
Go

package duckdb_test
import (
"testing"
dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/dialect/duckdb"
"github.com/stretchr/testify/assert"
)
func TestDialectOptions(t *testing.T) {
opts := duckdb.DialectOptions()
assert.NotNil(t, opts)
assert.Equal(t, '"', opts.QuoteRune)
assert.True(t, opts.IncludePlaceholderNum)
assert.Equal(t, []byte("$"), opts.PlaceHolderFragment)
assert.True(t, opts.SupportsReturn)
assert.True(t, opts.SupportsConflictTarget)
assert.True(t, opts.SupportsWithCTE)
assert.True(t, opts.SupportsDistinctOn)
assert.True(t, opts.SupportsWindowFunction)
}
func TestDialectRegistration(t *testing.T) {
// Verify the dialect is registered
dialect := dbv2.GetDialect("duckdb")
assert.NotNil(t, dialect)
}
func TestSelectSQL(t *testing.T) {
db := dbv2.Dialect("duckdb")
ds := db.From("users")
sql, _, err := ds.ToSQL()
assert.NoError(t, err)
assert.Equal(t, `SELECT * FROM "users"`, sql)
ds = db.From("users").Select("id", "name")
sql, _, err = ds.ToSQL()
assert.NoError(t, err)
assert.Equal(t, `SELECT "id", "name" FROM "users"`, sql)
ds = db.From("users").Where(dbv2.C("id").Eq(1))
sql, _, err = ds.ToSQL()
assert.NoError(t, err)
assert.Equal(t, `SELECT * FROM "users" WHERE ("id" = 1)`, sql)
}
func TestPreparedStatement(t *testing.T) {
db := dbv2.Dialect("duckdb")
ds := db.From("users").Prepared(true).Where(dbv2.C("id").Eq(1))
sql, args, err := ds.ToSQL()
assert.NoError(t, err)
assert.Equal(t, `SELECT * FROM "users" WHERE ("id" = $1)`, sql)
assert.Equal(t, []interface{}{int64(1)}, args)
ds = db.From("users").Prepared(true).Where(dbv2.C("name").Eq("John"))
sql, args, err = ds.ToSQL()
assert.NoError(t, err)
assert.Equal(t, `SELECT * FROM "users" WHERE ("name" = $1)`, sql)
assert.Equal(t, []interface{}{"John"}, args)
}
func TestInsertSQL(t *testing.T) {
db := dbv2.Dialect("duckdb")
ds := db.Insert("users").Rows(dbv2.Record{"id": 1, "name": "John"})
sql, _, err := ds.ToSQL()
assert.NoError(t, err)
assert.Contains(t, sql, `INSERT INTO "users"`)
assert.Contains(t, sql, `"id"`)
assert.Contains(t, sql, `"name"`)
}
func TestUpdateSQL(t *testing.T) {
db := dbv2.Dialect("duckdb")
ds := db.Update("users").Set(dbv2.Record{"name": "Jane"}).Where(dbv2.C("id").Eq(1))
sql, _, err := ds.ToSQL()
assert.NoError(t, err)
assert.Contains(t, sql, `UPDATE "users"`)
assert.Contains(t, sql, `"name"`)
assert.Contains(t, sql, `WHERE ("id" = 1)`)
}
func TestDeleteSQL(t *testing.T) {
db := dbv2.Dialect("duckdb")
ds := db.Delete("users").Where(dbv2.C("id").Eq(1))
sql, _, err := ds.ToSQL()
assert.NoError(t, err)
assert.Equal(t, `DELETE FROM "users" WHERE ("id" = 1)`, sql)
}
func TestJoinSQL(t *testing.T) {
db := dbv2.Dialect("duckdb")
ds := db.From("users").
Join(dbv2.T("orders"), dbv2.On(dbv2.Ex{"users.id": dbv2.I("orders.user_id")})).
Select("users.name", "orders.total")
sql, _, err := ds.ToSQL()
assert.NoError(t, err)
assert.Contains(t, sql, `FROM "users"`)
assert.Contains(t, sql, `INNER JOIN "orders"`)
assert.Contains(t, sql, `ON ("users"."id" = "orders"."user_id")`)
}
func TestRegexpSQL(t *testing.T) {
db := dbv2.Dialect("duckdb")
// RegexpLike → regexp_matches(col, pattern)
ds := db.From("docs").Where(dbv2.C("content").RegexpLike("简介"))
sql, _, err := ds.ToSQL()
assert.NoError(t, err)
assert.Equal(t, `SELECT * FROM "docs" WHERE regexp_matches("content", '简介')`, sql)
// RegexpILike → 同样生成 regexp_matches
ds = db.From("docs").Where(dbv2.C("content").RegexpILike("简介"))
sql, _, err = ds.ToSQL()
assert.NoError(t, err)
assert.Equal(t, `SELECT * FROM "docs" WHERE regexp_matches("content", '简介')`, sql)
}
func TestRegexpPreparedSQL(t *testing.T) {
db := dbv2.Dialect("duckdb")
ds := db.From("docs").Prepared(true).Where(dbv2.C("content").RegexpLike("简介"))
sql, args, err := ds.ToSQL()
assert.NoError(t, err)
assert.Equal(t, `SELECT * FROM "docs" WHERE regexp_matches("content", $1)`, sql)
assert.Equal(t, []interface{}{"简介"}, args)
}
func TestWindowFunction(t *testing.T) {
db := dbv2.Dialect("duckdb")
ds := db.From("sales").
Select(
"product",
"amount",
dbv2.ROW_NUMBER().Over(dbv2.W().PartitionBy("product").OrderBy(dbv2.C("amount").Desc())).As("rank"),
)
sql, _, err := ds.ToSQL()
assert.NoError(t, err)
assert.Contains(t, sql, `ROW_NUMBER()`)
assert.Contains(t, sql, `OVER`)
assert.Contains(t, sql, `PARTITION BY`)
}
func TestCTE(t *testing.T) {
db := dbv2.Dialect("duckdb")
cte := db.From("users").Where(dbv2.C("active").IsTrue())
ds := db.From("active_users").With("active_users", cte).Select("*")
sql, _, err := ds.ToSQL()
assert.NoError(t, err)
assert.Contains(t, sql, `WITH active_users AS`)
assert.Contains(t, sql, `SELECT * FROM "users" WHERE ("active" IS TRUE)`)
}