Files
db/sqlgen/expression_sql_generator_test.go
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

1689 lines
66 KiB
Go

package sqlgen_test
import (
"database/sql/driver"
"fmt"
"regexp"
"testing"
"time"
"git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/sqlgen"
"github.com/stretchr/testify/suite"
)
var emptyArgs = make([]interface{}, 0)
type testAppendableExpression struct {
sql string
args []interface{}
err error
alias exp.IdentifierExpression
returnsColumns bool
}
func newTestAppendableExpression(
sql string,
args []interface{},
err error,
alias exp.IdentifierExpression) exp.AppendableExpression {
return &testAppendableExpression{sql: sql, args: args, err: err, alias: alias}
}
func (tae *testAppendableExpression) Expression() exp.Expression {
return tae
}
func (tae *testAppendableExpression) Clone() exp.Expression {
return tae
}
func (tae *testAppendableExpression) GetAs() exp.IdentifierExpression {
return tae.alias
}
func (tae *testAppendableExpression) ReturnsColumns() bool {
return tae.returnsColumns
}
func (tae *testAppendableExpression) AppendSQL(b sb.SQLBuilder) {
if tae.err != nil {
b.SetError(tae.err)
return
}
b.WriteStrings(tae.sql)
if len(tae.args) > 0 {
b.WriteArg(tae.args...)
}
}
type (
expressionTestCase struct {
val interface{}
sql string
err string
isPrepared bool
args []interface{}
}
expressionSQLGeneratorSuite struct {
suite.Suite
}
)
func (esgs *expressionSQLGeneratorSuite) assertCases(esg sqlgen.ExpressionSQLGenerator, cases ...expressionTestCase) {
for i, c := range cases {
b := sb.NewSQLBuilder(c.isPrepared)
esg.Generate(b, c.val)
actualSQL, actualArgs, err := b.ToSQL()
if c.err == "" {
esgs.NoError(err, "test case %d failed", i)
} else {
esgs.EqualError(err, c.err, "test case %d failed", i)
}
esgs.Equal(c.sql, actualSQL, "test case %d failed", i)
if c.isPrepared && c.args != nil || len(c.args) > 0 {
esgs.Equal(c.args, actualArgs, "test case %d failed", i)
} else {
esgs.Empty(actualArgs, "test case %d failed", i)
}
}
}
func (esgs *expressionSQLGeneratorSuite) TestDialect() {
esg := sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions())
esgs.Equal("test", esg.Dialect())
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_ErroredBuilder() {
esg := sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions())
expectedErr := errors.New("test error")
b := sb.NewSQLBuilder(false).SetError(expectedErr)
esg.Generate(b, 1)
sql, args, err := b.ToSQL()
esgs.Equal(expectedErr, err)
esgs.Empty(sql)
esgs.Empty(args)
b = sb.NewSQLBuilder(true).SetError(err)
esg.Generate(b, true)
sql, args, err = b.ToSQL()
esgs.Equal(expectedErr, err)
esgs.Empty(sql)
esgs.Empty(args)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_Invalid() {
var b *bool
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: b, sql: "NULL"},
expressionTestCase{val: b, sql: "?", isPrepared: true, args: []interface{}{nil}},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_UnsupportedType() {
type strct struct{}
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: strct{}, err: "db_encode_error: Unable to encode value {}"},
expressionTestCase{val: strct{}, err: "db_encode_error: Unable to encode value {}", isPrepared: true},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_IncludePlaceholderNum() {
opts := sqlgen.DefaultDialectOptions()
opts.IncludePlaceholderNum = true
opts.PlaceHolderFragment = []byte("$")
ex := exp.Ex{
"a": 1,
"b": true,
"c": false,
"d": []string{"a", "b", "c"},
}
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", opts),
expressionTestCase{
val: ex,
sql: `(("a" = 1) AND ("b" IS TRUE) AND ("c" IS FALSE) AND ("d" IN ('a', 'b', 'c')))`,
},
expressionTestCase{
val: ex,
sql: `(("a" = $1) AND ("b" IS TRUE) AND ("c" IS FALSE) AND ("d" IN ($2, $3, $4)))`,
isPrepared: true,
args: []interface{}{int64(1), "a", "b", "c"},
},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_FloatTypes() {
var float float64
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: float32(10.01), sql: "10.010000228881836"},
expressionTestCase{val: float32(10.01), sql: "?", isPrepared: true, args: []interface{}{float64(float32(10.01))}},
expressionTestCase{val: float64(10.01), sql: "10.01"},
expressionTestCase{val: float64(10.01), sql: "?", isPrepared: true, args: []interface{}{float64(10.01)}},
expressionTestCase{val: &float, sql: "0"},
expressionTestCase{val: &float, sql: "?", isPrepared: true, args: []interface{}{float}},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_IntTypes() {
var i int64
ints := []interface{}{
int(10),
int16(10),
int32(10),
int64(10),
uint(10),
uint16(10),
uint32(10),
uint64(10),
}
for _, i := range ints {
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: i, sql: "10"},
expressionTestCase{val: i, sql: "?", isPrepared: true, args: []interface{}{int64(10)}},
)
}
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: &i, sql: "0"},
expressionTestCase{val: &i, sql: "?", isPrepared: true, args: []interface{}{i}},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_StringTypes() {
var str string
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: "Hello", sql: "'Hello'"},
expressionTestCase{val: "Hello", sql: "?", isPrepared: true, args: []interface{}{"Hello"}},
expressionTestCase{val: "Hello'", sql: "'Hello'''"},
expressionTestCase{val: "Hello'", sql: "?", isPrepared: true, args: []interface{}{"Hello'"}},
expressionTestCase{val: &str, sql: "''"},
expressionTestCase{val: &str, sql: "?", isPrepared: true, args: []interface{}{str}},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_BytesTypes() {
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: []byte("Hello"), sql: "'Hello'"},
expressionTestCase{val: []byte("Hello"), sql: "?", isPrepared: true, args: []interface{}{[]byte("Hello")}},
expressionTestCase{val: []byte("Hello'"), sql: "'Hello'''"},
expressionTestCase{val: []byte("Hello'"), sql: "?", isPrepared: true, args: []interface{}{[]byte("Hello'")}},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_BoolTypes() {
var bl bool
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: true, sql: "TRUE"},
expressionTestCase{val: true, sql: "?", isPrepared: true, args: []interface{}{true}},
expressionTestCase{val: false, sql: "FALSE"},
expressionTestCase{val: false, sql: "?", isPrepared: true, args: []interface{}{false}},
expressionTestCase{val: &bl, sql: "FALSE"},
expressionTestCase{val: &bl, sql: "?", isPrepared: true, args: []interface{}{bl}},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_TimeTypes() {
var nt *time.Time
ts, err := time.Parse(time.RFC3339, "2019-10-01T15:01:00Z")
esgs.Require().NoError(err)
originalLoc := sqlgen.GetTimeLocation()
loc, err := time.LoadLocation("Asia/Shanghai")
esgs.Require().NoError(err)
sqlgen.SetTimeLocation(loc)
// non time
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: ts, sql: "'2019-10-01T23:01:00+08:00'"},
expressionTestCase{val: ts, sql: "?", isPrepared: true, args: []interface{}{ts}},
expressionTestCase{val: &ts, sql: "'2019-10-01T23:01:00+08:00'"},
expressionTestCase{val: &ts, sql: "?", isPrepared: true, args: []interface{}{ts}},
)
sqlgen.SetTimeLocation(time.UTC)
// utc time
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: ts, sql: "'2019-10-01T15:01:00Z'"},
expressionTestCase{val: ts, sql: "?", isPrepared: true, args: []interface{}{ts}},
expressionTestCase{val: &ts, sql: "'2019-10-01T15:01:00Z'"},
expressionTestCase{val: &ts, sql: "?", isPrepared: true, args: []interface{}{ts}},
)
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: nt, sql: "NULL"},
expressionTestCase{val: nt, sql: "?", isPrepared: true, args: []interface{}{nil}},
)
sqlgen.SetTimeLocation(originalLoc)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_NilTypes() {
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: nil, sql: "NULL"},
expressionTestCase{val: nil, sql: "?", isPrepared: true, args: []interface{}{nil}},
)
}
type datasetValuerType struct {
int int64
err error
}
func (j datasetValuerType) Value() (driver.Value, error) {
if j.err != nil {
return nil, j.err
}
return []byte(fmt.Sprintf("Hello World %d", j.int)), nil
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_Valuer() {
err := errors.New("valuer error")
var val *datasetValuerType
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: datasetValuerType{int: 10}, sql: "'Hello World 10'"},
expressionTestCase{
val: datasetValuerType{int: 10}, sql: "?", isPrepared: true, args: []interface{}{[]byte("Hello World 10")},
},
expressionTestCase{val: datasetValuerType{err: err}, err: "db: valuer error"},
expressionTestCase{
val: datasetValuerType{err: err}, isPrepared: true, err: "db: valuer error",
},
expressionTestCase{
val: val, sql: "NULL",
},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_Slice() {
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: []string{"a", "b", "c"}, sql: `('a', 'b', 'c')`},
expressionTestCase{
val: []string{"a", "b", "c"}, sql: "(?, ?, ?)", isPrepared: true, args: []interface{}{"a", "b", "c"},
},
expressionTestCase{val: []byte{'a', 'b', 'c'}, sql: `'abc'`},
expressionTestCase{
val: []byte{'a', 'b', 'c'}, sql: "?", isPrepared: true, args: []interface{}{[]byte{'a', 'b', 'c'}},
},
)
}
type unknownExpression struct{}
func (ue unknownExpression) Expression() exp.Expression {
return ue
}
func (ue unknownExpression) Clone() exp.Expression {
return ue
}
func (esgs *expressionSQLGeneratorSuite) TestGenerateUnsupportedExpression() {
errMsg := "db: unsupported expression type sqlgen_test.unknownExpression"
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: unknownExpression{}, err: errMsg},
expressionTestCase{
val: unknownExpression{}, isPrepared: true, err: errMsg,
},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_AppendableExpression() {
ti := exp.NewIdentifierExpression("", "b", "")
a := newTestAppendableExpression(`select * from "a"`, []interface{}{}, nil, nil)
aliasedA := newTestAppendableExpression(`select * from "a"`, []interface{}{}, nil, ti)
argsA := newTestAppendableExpression(`select * from "a" where x=?`, []interface{}{true}, nil, ti)
ae := newTestAppendableExpression(`select * from "a"`, emptyArgs, errors.New("expected error"), nil)
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: a, sql: `(select * from "a")`},
expressionTestCase{val: a, sql: `(select * from "a")`, isPrepared: true},
expressionTestCase{val: aliasedA, sql: `(select * from "a") AS "b"`},
expressionTestCase{val: aliasedA, sql: `(select * from "a") AS "b"`, isPrepared: true},
expressionTestCase{val: ae, err: "db: expected error"},
expressionTestCase{val: ae, err: "db: expected error", isPrepared: true},
expressionTestCase{val: argsA, sql: `(select * from "a" where x=?) AS "b"`, args: []interface{}{true}},
expressionTestCase{val: argsA, sql: `(select * from "a" where x=?) AS "b"`, isPrepared: true, args: []interface{}{true}},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_ColumnList() {
cl := exp.NewColumnListExpression("a", exp.NewLiteralExpression("true"))
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: cl, sql: `"a", true`},
expressionTestCase{val: cl, sql: `"a", true`, isPrepared: true},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_ExpressionList() {
andEl := exp.NewExpressionList(
exp.AndType,
exp.NewIdentifierExpression("", "", "a").Eq("b"),
exp.NewIdentifierExpression("", "", "c").Neq(1),
)
orEl := exp.NewExpressionList(
exp.OrType,
exp.NewIdentifierExpression("", "", "a").Eq("b"),
exp.NewIdentifierExpression("", "", "c").Neq(1),
)
andOrEl := exp.NewExpressionList(exp.OrType,
exp.NewIdentifierExpression("", "", "a").Eq("b"),
exp.NewExpressionList(exp.AndType,
exp.NewIdentifierExpression("", "", "c").Neq(1),
exp.NewIdentifierExpression("", "", "d").Eq(exp.NewLiteralExpression("NOW()")),
),
)
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: andEl, sql: `(("a" = 'b') AND ("c" != 1))`},
expressionTestCase{
val: andEl, sql: `(("a" = ?) AND ("c" != ?))`, isPrepared: true, args: []interface{}{"b", int64(1)},
},
expressionTestCase{val: orEl, sql: `(("a" = 'b') OR ("c" != 1))`},
expressionTestCase{
val: orEl, sql: `(("a" = ?) OR ("c" != ?))`, isPrepared: true, args: []interface{}{"b", int64(1)},
},
expressionTestCase{val: andOrEl, sql: `(("a" = 'b') OR (("c" != 1) AND ("d" = NOW())))`},
expressionTestCase{
val: andOrEl,
sql: `(("a" = ?) OR (("c" != ?) AND ("d" = NOW())))`,
isPrepared: true,
args: []interface{}{"b", int64(1)},
},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_LiteralExpression() {
noArgsL := exp.NewLiteralExpression(`"b"::DATE = '2010-09-02'`)
argsL := exp.NewLiteralExpression(`"b" = ? or "c" = ? or d IN ?`, "a", 1, []int{1, 2, 3, 4})
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: noArgsL, sql: `"b"::DATE = '2010-09-02'`},
expressionTestCase{val: noArgsL, sql: `"b"::DATE = '2010-09-02'`, isPrepared: true},
expressionTestCase{val: argsL, sql: `"b" = 'a' or "c" = 1 or d IN (1, 2, 3, 4)`},
expressionTestCase{
val: argsL,
sql: `"b" = ? or "c" = ? or d IN (?, ?, ?, ?)`,
isPrepared: true,
args: []interface{}{
"a",
int64(1),
int64(1),
int64(2),
int64(3),
int64(4),
},
},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_AliasedExpression() {
aliasedI := exp.NewIdentifierExpression("", "", "a").As("b")
aliasedWithII := exp.NewIdentifierExpression("", "", "a").
As(exp.NewIdentifierExpression("", "", "b"))
aliasedL := exp.NewLiteralExpression("count(*)").As("count")
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: aliasedI, sql: `"a" AS "b"`},
expressionTestCase{val: aliasedI, sql: `"a" AS "b"`, isPrepared: true},
expressionTestCase{val: aliasedWithII, sql: `"a" AS "b"`},
expressionTestCase{val: aliasedWithII, sql: `"a" AS "b"`, isPrepared: true},
expressionTestCase{val: aliasedL, sql: `count(*) AS "count"`},
expressionTestCase{val: aliasedL, sql: `count(*) AS "count"`, isPrepared: true},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_BooleanExpressionAliased() {
ident := exp.NewIdentifierExpression("", "", "a")
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: ident.Eq(1).As("b"), sql: `("a" = 1) AS "b"`},
expressionTestCase{val: ident.Eq(1).As("b"), sql: `("a" = ?) AS "b"`,
isPrepared: true, args: []interface{}{int64(1)}},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_BooleanExpression() {
ae := newTestAppendableExpression(`SELECT "id" FROM "test2"`, emptyArgs, nil, nil)
re := regexp.MustCompile("[ab]")
ident := exp.NewIdentifierExpression("", "", "a")
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: ident.Eq(1), sql: `("a" = 1)`},
expressionTestCase{val: ident.Eq(1), sql: `("a" = ?)`, isPrepared: true, args: []interface{}{int64(1)}},
expressionTestCase{val: ident.Eq(true), sql: `("a" IS TRUE)`},
expressionTestCase{val: ident.Eq(true), sql: `("a" IS TRUE)`, isPrepared: true},
expressionTestCase{val: ident.Eq(false), sql: `("a" IS FALSE)`},
expressionTestCase{val: ident.Eq(false), sql: `("a" IS FALSE)`, isPrepared: true},
expressionTestCase{val: ident.Eq(nil), sql: `("a" IS NULL)`},
expressionTestCase{val: ident.Eq(nil), sql: `("a" IS NULL)`, isPrepared: true},
expressionTestCase{val: ident.Eq([]int64{1, 2, 3}), sql: `("a" IN (1, 2, 3))`},
expressionTestCase{val: ident.Eq([]int64{1, 2, 3}), sql: `("a" IN (?, ?, ?))`, isPrepared: true, args: []interface{}{
int64(1), int64(2), int64(3),
}},
expressionTestCase{val: ident.Eq(ae), sql: `("a" IN (SELECT "id" FROM "test2"))`},
expressionTestCase{val: ident.Eq(ae), sql: `("a" IN (SELECT "id" FROM "test2"))`, isPrepared: true},
expressionTestCase{val: ident.Neq(1), sql: `("a" != 1)`},
expressionTestCase{val: ident.Neq(1), sql: `("a" != ?)`, isPrepared: true, args: []interface{}{int64(1)}},
expressionTestCase{val: ident.Neq(true), sql: `("a" IS NOT TRUE)`},
expressionTestCase{val: ident.Neq(true), sql: `("a" IS NOT TRUE)`, isPrepared: true},
expressionTestCase{val: ident.Neq(false), sql: `("a" IS NOT FALSE)`},
expressionTestCase{val: ident.Neq(false), sql: `("a" IS NOT FALSE)`, isPrepared: true},
expressionTestCase{val: ident.Neq(nil), sql: `("a" IS NOT NULL)`},
expressionTestCase{val: ident.Neq(nil), sql: `("a" IS NOT NULL)`, isPrepared: true},
expressionTestCase{val: ident.Neq([]int64{1, 2, 3}), sql: `("a" NOT IN (1, 2, 3))`},
expressionTestCase{val: ident.Neq([]int64{1, 2, 3}), sql: `("a" NOT IN (?, ?, ?))`, isPrepared: true, args: []interface{}{
int64(1), int64(2), int64(3),
}},
expressionTestCase{val: ident.Neq(ae), sql: `("a" NOT IN (SELECT "id" FROM "test2"))`},
expressionTestCase{val: ident.Neq(ae), sql: `("a" NOT IN (SELECT "id" FROM "test2"))`, isPrepared: true},
expressionTestCase{val: ident.Is(true), sql: `("a" IS TRUE)`},
expressionTestCase{val: ident.Is(true), sql: `("a" IS TRUE)`, isPrepared: true},
expressionTestCase{val: ident.Is(false), sql: `("a" IS FALSE)`},
expressionTestCase{val: ident.Is(false), sql: `("a" IS FALSE)`, isPrepared: true},
expressionTestCase{val: ident.Is(nil), sql: `("a" IS NULL)`},
expressionTestCase{val: ident.Is(nil), sql: `("a" IS NULL)`, isPrepared: true},
expressionTestCase{val: ident.IsNot(true), sql: `("a" IS NOT TRUE)`},
expressionTestCase{val: ident.IsNot(true), sql: `("a" IS NOT TRUE)`, isPrepared: true},
expressionTestCase{val: ident.IsNot(false), sql: `("a" IS NOT FALSE)`},
expressionTestCase{val: ident.IsNot(false), sql: `("a" IS NOT FALSE)`, isPrepared: true},
expressionTestCase{val: ident.IsNot(nil), sql: `("a" IS NOT NULL)`},
expressionTestCase{val: ident.IsNot(nil), sql: `("a" IS NOT NULL)`, isPrepared: true},
expressionTestCase{val: ident.Gt(1), sql: `("a" > 1)`},
expressionTestCase{val: ident.Gt(1), sql: `("a" > ?)`, isPrepared: true, args: []interface{}{int64(1)}},
expressionTestCase{val: ident.Gte(1), sql: `("a" >= 1)`},
expressionTestCase{val: ident.Gte(1), sql: `("a" >= ?)`, isPrepared: true, args: []interface{}{int64(1)}},
expressionTestCase{val: ident.Lt(1), sql: `("a" < 1)`},
expressionTestCase{val: ident.Lt(1), sql: `("a" < ?)`, isPrepared: true, args: []interface{}{int64(1)}},
expressionTestCase{val: ident.Lte(1), sql: `("a" <= 1)`},
expressionTestCase{val: ident.Lte(1), sql: `("a" <= ?)`, isPrepared: true, args: []interface{}{int64(1)}},
expressionTestCase{val: ident.In([]int64{1, 2, 3}), sql: `("a" IN (1, 2, 3))`},
expressionTestCase{val: ident.In([]int64{1, 2, 3}), sql: `("a" IN (?, ?, ?))`, isPrepared: true, args: []interface{}{
int64(1), int64(2), int64(3),
}},
expressionTestCase{val: ident.In(ae), sql: `("a" IN ((SELECT "id" FROM "test2")))`},
expressionTestCase{val: ident.In(ae), sql: `("a" IN ((SELECT "id" FROM "test2")))`, isPrepared: true},
expressionTestCase{val: ident.NotIn([]int64{1, 2, 3}), sql: `("a" NOT IN (1, 2, 3))`},
expressionTestCase{val: ident.NotIn([]int64{1, 2, 3}), sql: `("a" NOT IN (?, ?, ?))`, isPrepared: true, args: []interface{}{
int64(1), int64(2), int64(3),
}},
expressionTestCase{val: ident.NotIn(ae), sql: `("a" NOT IN ((SELECT "id" FROM "test2")))`},
expressionTestCase{val: ident.NotIn(ae), sql: `("a" NOT IN ((SELECT "id" FROM "test2")))`, isPrepared: true},
expressionTestCase{val: ident.Like("a%"), sql: `("a" LIKE 'a%')`},
expressionTestCase{val: ident.Like("a%"), sql: `("a" LIKE ?)`, isPrepared: true, args: []interface{}{"a%"}},
expressionTestCase{val: ident.Like(re), sql: `("a" ~ '[ab]')`},
expressionTestCase{val: ident.Like(re), sql: `("a" ~ ?)`, isPrepared: true, args: []interface{}{"[ab]"}},
expressionTestCase{val: ident.ILike("a%"), sql: `("a" ILIKE 'a%')`},
expressionTestCase{val: ident.ILike("a%"), sql: `("a" ILIKE ?)`, isPrepared: true, args: []interface{}{"a%"}},
expressionTestCase{val: ident.ILike(re), sql: `("a" ~* '[ab]')`},
expressionTestCase{val: ident.ILike(re), sql: `("a" ~* ?)`, isPrepared: true, args: []interface{}{"[ab]"}},
expressionTestCase{val: ident.NotLike("a%"), sql: `("a" NOT LIKE 'a%')`},
expressionTestCase{val: ident.NotLike("a%"), sql: `("a" NOT LIKE ?)`, isPrepared: true, args: []interface{}{"a%"}},
expressionTestCase{val: ident.NotLike(re), sql: `("a" !~ '[ab]')`},
expressionTestCase{val: ident.NotLike(re), sql: `("a" !~ ?)`, isPrepared: true, args: []interface{}{"[ab]"}},
expressionTestCase{val: ident.NotILike("a%"), sql: `("a" NOT ILIKE 'a%')`},
expressionTestCase{val: ident.NotILike("a%"), sql: `("a" NOT ILIKE ?)`, isPrepared: true, args: []interface{}{"a%"}},
expressionTestCase{val: ident.NotILike(re), sql: `("a" !~* '[ab]')`},
expressionTestCase{val: ident.NotILike(re), sql: `("a" !~* ?)`, isPrepared: true, args: []interface{}{"[ab]"}},
)
opts := sqlgen.DefaultDialectOptions()
opts.BooleanOperatorLookup = map[exp.BooleanOperation][]byte{}
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", opts),
expressionTestCase{val: ident.Eq(1), err: "db: boolean operator 'eq' not supported"},
expressionTestCase{val: ident.Neq(1), err: "db: boolean operator 'neq' not supported"},
expressionTestCase{val: ident.Is(true), err: "db: boolean operator 'is' not supported"},
expressionTestCase{val: ident.IsNot(true), err: "db: boolean operator 'isnot' not supported"},
expressionTestCase{val: ident.Gt(1), err: "db: boolean operator 'gt' not supported"},
expressionTestCase{val: ident.Gte(1), err: "db: boolean operator 'gte' not supported"},
expressionTestCase{val: ident.Lt(1), err: "db: boolean operator 'lt' not supported"},
expressionTestCase{val: ident.Lte(1), err: "db: boolean operator 'lte' not supported"},
expressionTestCase{val: ident.In([]int64{1, 2, 3}), err: "db: boolean operator 'in' not supported"},
expressionTestCase{val: ident.NotIn([]int64{1, 2, 3}), err: "db: boolean operator 'notin' not supported"},
expressionTestCase{val: ident.Like("a%"), err: "db: boolean operator 'like' not supported"},
expressionTestCase{val: ident.Like(re), err: "db: boolean operator 'regexplike' not supported"},
expressionTestCase{val: ident.ILike("a%"), err: "db: boolean operator 'ilike' not supported"},
expressionTestCase{val: ident.ILike(re), err: "db: boolean operator 'regexpilike' not supported"},
expressionTestCase{val: ident.NotLike("a%"), err: "db: boolean operator 'notlike' not supported"},
expressionTestCase{val: ident.NotLike(re), err: "db: boolean operator 'regexpnotlike' not supported"},
expressionTestCase{val: ident.NotILike("a%"), err: "db: boolean operator 'notilike' not supported"},
expressionTestCase{val: ident.NotILike(re), err: "db: boolean operator 'regexpnotilike' not supported"},
)
// BooleanFunctionLookup 生成 FUNC(LHS, RHS) 格式,优先级高于 BooleanOperatorLookup
opts2 := sqlgen.DefaultDialectOptions()
opts2.BooleanFunctionLookup = map[exp.BooleanOperation][]byte{
exp.RegexpLikeOp: []byte("regexp_matches"),
exp.RegexpILikeOp: []byte("regexp_matches"),
}
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", opts2),
// 函数调用格式
expressionTestCase{val: ident.Like(re), sql: `regexp_matches("a", '[ab]')`},
expressionTestCase{val: ident.Like(re), sql: `regexp_matches("a", ?)`, isPrepared: true, args: []interface{}{"[ab]"}},
expressionTestCase{val: ident.ILike(re), sql: `regexp_matches("a", '[ab]')`},
expressionTestCase{val: ident.ILike(re), sql: `regexp_matches("a", ?)`, isPrepared: true, args: []interface{}{"[ab]"}},
// 未在 BooleanFunctionLookup 中的操作符仍走中缀格式
expressionTestCase{val: ident.Eq(1), sql: `("a" = 1)`},
expressionTestCase{val: ident.NotLike(re), sql: `("a" !~ '[ab]')`},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_BitwiseExpression() {
ident := exp.NewIdentifierExpression("", "", "a")
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: ident.BitwiseInversion(), sql: `(~ "a")`},
expressionTestCase{val: ident.BitwiseInversion(), sql: `(~ "a")`, isPrepared: true},
expressionTestCase{val: ident.BitwiseAnd(1), sql: `("a" & 1)`},
expressionTestCase{val: ident.BitwiseAnd(1), sql: `("a" & ?)`, isPrepared: true, args: []interface{}{int64(1)}},
expressionTestCase{val: ident.BitwiseOr(1), sql: `("a" | 1)`},
expressionTestCase{val: ident.BitwiseOr(1), sql: `("a" | ?)`, isPrepared: true, args: []interface{}{int64(1)}},
expressionTestCase{val: ident.BitwiseXor(1), sql: `("a" # 1)`},
expressionTestCase{val: ident.BitwiseXor(1), sql: `("a" # ?)`, isPrepared: true, args: []interface{}{int64(1)}},
expressionTestCase{val: ident.BitwiseLeftShift(1), sql: `("a" << 1)`},
expressionTestCase{val: ident.BitwiseLeftShift(1), sql: `("a" << ?)`, isPrepared: true, args: []interface{}{int64(1)}},
expressionTestCase{val: ident.BitwiseRightShift(1), sql: `("a" >> 1)`},
expressionTestCase{val: ident.BitwiseRightShift(1), sql: `("a" >> ?)`, isPrepared: true, args: []interface{}{int64(1)}},
)
opts := sqlgen.DefaultDialectOptions()
opts.BitwiseOperatorLookup = map[exp.BitwiseOperation][]byte{}
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", opts),
expressionTestCase{val: ident.BitwiseInversion(), err: "db: bitwise operator 'Inversion' not supported"},
expressionTestCase{val: ident.BitwiseAnd(1), err: "db: bitwise operator 'AND' not supported"},
expressionTestCase{val: ident.BitwiseOr(1), err: "db: bitwise operator 'OR' not supported"},
expressionTestCase{val: ident.BitwiseXor(1), err: "db: bitwise operator 'XOR' not supported"},
expressionTestCase{val: ident.BitwiseLeftShift(1), err: "db: bitwise operator 'Left Shift' not supported"},
expressionTestCase{val: ident.BitwiseRightShift(1), err: "db: bitwise operator 'Right Shift' not supported"},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_RangeExpression() {
betweenNum := exp.NewIdentifierExpression("", "", "a").
Between(exp.NewRangeVal(1, 2))
notBetweenNum := exp.NewIdentifierExpression("", "", "a").
NotBetween(exp.NewRangeVal(1, 2))
betweenStr := exp.NewIdentifierExpression("", "", "a").
Between(exp.NewRangeVal("aaa", "zzz"))
notBetweenStr := exp.NewIdentifierExpression("", "", "a").
NotBetween(exp.NewRangeVal("aaa", "zzz"))
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: betweenNum, sql: `("a" BETWEEN 1 AND 2)`},
expressionTestCase{val: betweenNum, sql: `("a" BETWEEN ? AND ?)`, isPrepared: true, args: []interface{}{
int64(1),
int64(2),
}},
expressionTestCase{val: notBetweenNum, sql: `("a" NOT BETWEEN 1 AND 2)`},
expressionTestCase{val: notBetweenNum, sql: `("a" NOT BETWEEN ? AND ?)`, isPrepared: true, args: []interface{}{
int64(1),
int64(2),
}},
expressionTestCase{val: betweenStr, sql: `("a" BETWEEN 'aaa' AND 'zzz')`},
expressionTestCase{val: betweenStr, sql: `("a" BETWEEN ? AND ?)`, isPrepared: true, args: []interface{}{
"aaa",
"zzz",
}},
expressionTestCase{val: notBetweenStr, sql: `("a" NOT BETWEEN 'aaa' AND 'zzz')`},
expressionTestCase{val: notBetweenStr, sql: `("a" NOT BETWEEN ? AND ?)`, isPrepared: true, args: []interface{}{
"aaa",
"zzz",
}},
)
opts := sqlgen.DefaultDialectOptions()
opts.RangeOperatorLookup = map[exp.RangeOperation][]byte{}
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", opts),
expressionTestCase{val: betweenNum, err: "db: range operator between not supported"},
expressionTestCase{val: betweenNum, err: "db: range operator between not supported"},
expressionTestCase{val: notBetweenNum, err: "db: range operator not between not supported"},
expressionTestCase{val: notBetweenNum, err: "db: range operator not between not supported"},
expressionTestCase{val: betweenStr, err: "db: range operator between not supported"},
expressionTestCase{val: betweenStr, err: "db: range operator between not supported"},
expressionTestCase{val: notBetweenStr, err: "db: range operator not between not supported"},
expressionTestCase{val: notBetweenStr, err: "db: range operator not between not supported"},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_OrderedExpression() {
asc := exp.NewIdentifierExpression("", "", "a").Asc()
ascNf := exp.NewIdentifierExpression("", "", "a").Asc().NullsFirst()
ascNl := exp.NewIdentifierExpression("", "", "a").Asc().NullsLast()
desc := exp.NewIdentifierExpression("", "", "a").Desc()
descNf := exp.NewIdentifierExpression("", "", "a").Desc().NullsFirst()
descNl := exp.NewIdentifierExpression("", "", "a").Desc().NullsLast()
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: asc, sql: `"a" ASC`},
expressionTestCase{val: asc, sql: `"a" ASC`, isPrepared: true},
expressionTestCase{val: ascNf, sql: `"a" ASC NULLS FIRST`},
expressionTestCase{val: ascNf, sql: `"a" ASC NULLS FIRST`, isPrepared: true},
expressionTestCase{val: ascNl, sql: `"a" ASC NULLS LAST`},
expressionTestCase{val: ascNl, sql: `"a" ASC NULLS LAST`, isPrepared: true},
expressionTestCase{val: desc, sql: `"a" DESC`},
expressionTestCase{val: desc, sql: `"a" DESC`, isPrepared: true},
expressionTestCase{val: descNf, sql: `"a" DESC NULLS FIRST`},
expressionTestCase{val: descNf, sql: `"a" DESC NULLS FIRST`, isPrepared: true},
expressionTestCase{val: descNl, sql: `"a" DESC NULLS LAST`},
expressionTestCase{val: descNl, sql: `"a" DESC NULLS LAST`, isPrepared: true},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_UpdateExpression() {
ue := exp.NewIdentifierExpression("", "", "a").Set(1)
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: ue, sql: `"a"=1`},
expressionTestCase{val: ue, sql: `"a"=?`, isPrepared: true, args: []interface{}{int64(1)}},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_SQLFunctionExpression() {
min := exp.NewSQLFunctionExpression("MIN", exp.NewIdentifierExpression("", "", "a"))
coalesce := exp.NewSQLFunctionExpression("COALESCE", exp.NewIdentifierExpression("", "", "a"), "a")
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: min, sql: `MIN("a")`},
expressionTestCase{val: min, sql: `MIN("a")`, isPrepared: true},
expressionTestCase{val: coalesce, sql: `COALESCE("a", 'a')`},
expressionTestCase{val: coalesce, sql: `COALESCE("a", ?)`, isPrepared: true, args: []interface{}{"a"}},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_SQLWindowFunctionExpression() {
sqlWinFunc := exp.NewSQLWindowFunctionExpression(
exp.NewSQLFunctionExpression("some_func"),
nil,
exp.NewWindowExpression(
nil,
exp.NewIdentifierExpression("", "", "win"),
nil,
nil,
),
)
sqlWinFuncFromWindow := exp.NewSQLWindowFunctionExpression(
exp.NewSQLFunctionExpression("some_func"),
exp.NewIdentifierExpression("", "", "win"),
nil,
)
emptyWinFunc := exp.NewSQLWindowFunctionExpression(
exp.NewSQLFunctionExpression("some_func"),
nil,
nil,
)
badNamedSQLWinFuncInherit := exp.NewSQLWindowFunctionExpression(
exp.NewSQLFunctionExpression("some_func"),
nil,
exp.NewWindowExpression(
exp.NewIdentifierExpression("", "", "w"),
nil,
nil,
nil,
),
)
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: sqlWinFunc, sql: `some_func() OVER ("win")`},
expressionTestCase{val: sqlWinFunc, sql: `some_func() OVER ("win")`, isPrepared: true},
expressionTestCase{val: sqlWinFuncFromWindow, sql: `some_func() OVER "win"`},
expressionTestCase{val: sqlWinFuncFromWindow, sql: `some_func() OVER "win"`, isPrepared: true},
expressionTestCase{val: emptyWinFunc, sql: `some_func() OVER ()`},
expressionTestCase{val: emptyWinFunc, sql: `some_func() OVER ()`, isPrepared: true},
expressionTestCase{val: badNamedSQLWinFuncInherit, err: sqlgen.ErrUnexpectedNamedWindow.Error()},
expressionTestCase{val: badNamedSQLWinFuncInherit, err: sqlgen.ErrUnexpectedNamedWindow.Error(), isPrepared: true},
)
opts := sqlgen.DefaultDialectOptions()
opts.SupportsWindowFunction = false
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", opts),
expressionTestCase{val: sqlWinFunc, err: sqlgen.ErrWindowNotSupported("test").Error()},
expressionTestCase{val: sqlWinFunc, err: sqlgen.ErrWindowNotSupported("test").Error(), isPrepared: true},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_WindowExpression() {
opts := sqlgen.DefaultDialectOptions()
opts.WindowPartitionByFragment = []byte("partition by ")
opts.WindowOrderByFragment = []byte("order by ")
emptySQLWinFunc := exp.NewWindowExpression(nil, nil, nil, nil)
namedSQLWinFunc := exp.NewWindowExpression(
exp.NewIdentifierExpression("", "", "w"), nil, nil, nil,
)
inheritSQLWinFunc := exp.NewWindowExpression(
nil, exp.NewIdentifierExpression("", "", "w"), nil, nil,
)
partitionBySQLWinFunc := exp.NewWindowExpression(
nil, nil, exp.NewColumnListExpression("a", "b"), nil,
)
orderBySQLWinFunc := exp.NewWindowExpression(
nil, nil, nil, exp.NewOrderedColumnList(
exp.NewIdentifierExpression("", "", "a").Asc(),
exp.NewIdentifierExpression("", "", "b").Desc(),
),
)
namedInheritPartitionOrderSQLWinFunc := exp.NewWindowExpression(
exp.NewIdentifierExpression("", "", "w1"),
exp.NewIdentifierExpression("", "", "w2"),
exp.NewColumnListExpression("a", "b"),
exp.NewOrderedColumnList(
exp.NewIdentifierExpression("", "", "a").Asc(),
exp.NewIdentifierExpression("", "", "b").Desc(),
),
)
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", opts),
expressionTestCase{val: emptySQLWinFunc, sql: `()`},
expressionTestCase{val: emptySQLWinFunc, sql: `()`, isPrepared: true},
expressionTestCase{val: namedSQLWinFunc, sql: `"w" AS ()`},
expressionTestCase{val: namedSQLWinFunc, sql: `"w" AS ()`, isPrepared: true},
expressionTestCase{val: inheritSQLWinFunc, sql: `("w")`},
expressionTestCase{val: inheritSQLWinFunc, sql: `("w")`, isPrepared: true},
expressionTestCase{val: partitionBySQLWinFunc, sql: `(partition by "a", "b")`},
expressionTestCase{val: partitionBySQLWinFunc, sql: `(partition by "a", "b")`, isPrepared: true},
expressionTestCase{val: orderBySQLWinFunc, sql: `(order by "a" ASC, "b" DESC)`},
expressionTestCase{val: orderBySQLWinFunc, sql: `(order by "a" ASC, "b" DESC)`, isPrepared: true},
expressionTestCase{
val: namedInheritPartitionOrderSQLWinFunc,
sql: `"w1" AS ("w2" partition by "a", "b" order by "a" ASC, "b" DESC)`,
},
expressionTestCase{
val: namedInheritPartitionOrderSQLWinFunc,
sql: `"w1" AS ("w2" partition by "a", "b" order by "a" ASC, "b" DESC)`,
isPrepared: true,
},
)
opts = sqlgen.DefaultDialectOptions()
opts.SupportsWindowFunction = false
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", opts),
expressionTestCase{val: emptySQLWinFunc, err: sqlgen.ErrWindowNotSupported("test").Error()},
expressionTestCase{val: emptySQLWinFunc, err: sqlgen.ErrWindowNotSupported("test").Error(), isPrepared: true},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_CastExpression() {
cast := exp.NewIdentifierExpression("", "", "a").Cast("DATE")
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: cast, sql: `CAST("a" AS DATE)`},
expressionTestCase{val: cast, sql: `CAST("a" AS DATE)`, isPrepared: true},
)
}
// Generates the sql for the WITH clauses for common table expressions (CTE)
func (esgs *expressionSQLGeneratorSuite) TestGenerate_CommonTableExpressionSlice() {
ae := newTestAppendableExpression(`SELECT * FROM "b"`, emptyArgs, nil, nil)
cteNoArgs := []exp.CommonTableExpression{
exp.NewCommonTableExpression(false, "a", ae),
}
cteArgs := []exp.CommonTableExpression{
exp.NewCommonTableExpression(false, "a(x,y)", ae),
}
cteRecursiveNoArgs := []exp.CommonTableExpression{
exp.NewCommonTableExpression(true, "a", ae),
}
cteRecursiveArgs := []exp.CommonTableExpression{
exp.NewCommonTableExpression(true, "a(x,y)", ae),
}
allCtes := []exp.CommonTableExpression{
exp.NewCommonTableExpression(false, "a", ae),
exp.NewCommonTableExpression(false, "a(x,y)", ae),
}
allRecursiveCtes := []exp.CommonTableExpression{
exp.NewCommonTableExpression(true, "a", ae),
exp.NewCommonTableExpression(true, "a(x,y)", ae),
}
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: cteNoArgs, sql: `WITH a AS (SELECT * FROM "b") `},
expressionTestCase{val: cteNoArgs, sql: `WITH a AS (SELECT * FROM "b") `, isPrepared: true},
expressionTestCase{val: cteArgs, sql: `WITH a(x,y) AS (SELECT * FROM "b") `},
expressionTestCase{val: cteArgs, sql: `WITH a(x,y) AS (SELECT * FROM "b") `, isPrepared: true},
expressionTestCase{val: cteRecursiveNoArgs, sql: `WITH RECURSIVE a AS (SELECT * FROM "b") `},
expressionTestCase{val: cteRecursiveNoArgs, sql: `WITH RECURSIVE a AS (SELECT * FROM "b") `, isPrepared: true},
expressionTestCase{val: cteRecursiveArgs, sql: `WITH RECURSIVE a(x,y) AS (SELECT * FROM "b") `},
expressionTestCase{val: cteRecursiveArgs, sql: `WITH RECURSIVE a(x,y) AS (SELECT * FROM "b") `, isPrepared: true},
expressionTestCase{val: allCtes, sql: `WITH a AS (SELECT * FROM "b"), a(x,y) AS (SELECT * FROM "b") `},
expressionTestCase{val: allCtes, sql: `WITH a AS (SELECT * FROM "b"), a(x,y) AS (SELECT * FROM "b") `, isPrepared: true},
expressionTestCase{val: allRecursiveCtes, sql: `WITH RECURSIVE a AS (SELECT * FROM "b"), a(x,y) AS (SELECT * FROM "b") `},
expressionTestCase{
val: allRecursiveCtes,
sql: `WITH RECURSIVE a AS (SELECT * FROM "b"), a(x,y) AS (SELECT * FROM "b") `,
isPrepared: true,
},
)
opts := sqlgen.DefaultDialectOptions()
opts.SupportsWithCTE = false
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", opts),
expressionTestCase{val: cteNoArgs, err: "db: dialect does not support CTE WITH clause [dialect=test]"},
expressionTestCase{val: cteNoArgs, err: "db: dialect does not support CTE WITH clause [dialect=test]", isPrepared: true},
expressionTestCase{val: cteArgs, err: "db: dialect does not support CTE WITH clause [dialect=test]"},
expressionTestCase{val: cteArgs, err: "db: dialect does not support CTE WITH clause [dialect=test]", isPrepared: true},
expressionTestCase{val: cteRecursiveNoArgs, err: "db: dialect does not support CTE WITH clause [dialect=test]"},
expressionTestCase{val: cteRecursiveNoArgs, err: "db: dialect does not support CTE WITH clause [dialect=test]", isPrepared: true},
expressionTestCase{val: cteRecursiveArgs, err: "db: dialect does not support CTE WITH clause [dialect=test]"},
expressionTestCase{val: cteRecursiveArgs, err: "db: dialect does not support CTE WITH clause [dialect=test]", isPrepared: true},
)
opts = sqlgen.DefaultDialectOptions()
opts.SupportsWithCTERecursive = false
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", opts),
expressionTestCase{val: cteNoArgs, sql: `WITH a AS (SELECT * FROM "b") `},
expressionTestCase{val: cteNoArgs, sql: `WITH a AS (SELECT * FROM "b") `, isPrepared: true},
expressionTestCase{val: cteArgs, sql: `WITH a(x,y) AS (SELECT * FROM "b") `},
expressionTestCase{val: cteArgs, sql: `WITH a(x,y) AS (SELECT * FROM "b") `, isPrepared: true},
expressionTestCase{
val: cteRecursiveNoArgs,
err: "db: dialect does not support CTE WITH RECURSIVE clause [dialect=test]",
},
expressionTestCase{
val: cteRecursiveNoArgs,
err: "db: dialect does not support CTE WITH RECURSIVE clause [dialect=test]",
isPrepared: true,
},
expressionTestCase{
val: cteRecursiveArgs,
err: "db: dialect does not support CTE WITH RECURSIVE clause [dialect=test]",
},
expressionTestCase{
val: cteRecursiveArgs,
err: "db: dialect does not support CTE WITH RECURSIVE clause [dialect=test]",
isPrepared: true,
},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_CommonTableExpression() {
ae := newTestAppendableExpression(`SELECT * FROM "b"`, emptyArgs, nil, nil)
cteNoArgs := exp.NewCommonTableExpression(false, "a", ae)
cteArgs := exp.NewCommonTableExpression(false, "a(x,y)", ae)
cteRecursiveNoArgs := exp.NewCommonTableExpression(true, "a", ae)
cteRecursiveArgs := exp.NewCommonTableExpression(true, "a(x,y)", ae)
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: cteNoArgs, sql: `a AS (SELECT * FROM "b")`},
expressionTestCase{val: cteNoArgs, sql: `a AS (SELECT * FROM "b")`, isPrepared: true},
expressionTestCase{val: cteArgs, sql: `a(x,y) AS (SELECT * FROM "b")`},
expressionTestCase{val: cteArgs, sql: `a(x,y) AS (SELECT * FROM "b")`, isPrepared: true},
expressionTestCase{val: cteRecursiveNoArgs, sql: `a AS (SELECT * FROM "b")`},
expressionTestCase{val: cteRecursiveNoArgs, sql: `a AS (SELECT * FROM "b")`, isPrepared: true},
expressionTestCase{val: cteRecursiveArgs, sql: `a(x,y) AS (SELECT * FROM "b")`},
expressionTestCase{val: cteRecursiveArgs, sql: `a(x,y) AS (SELECT * FROM "b")`, isPrepared: true},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_CompoundExpression() {
ae := newTestAppendableExpression(`SELECT * FROM "b"`, emptyArgs, nil, nil)
u := exp.NewCompoundExpression(exp.UnionCompoundType, ae)
ua := exp.NewCompoundExpression(exp.UnionAllCompoundType, ae)
i := exp.NewCompoundExpression(exp.IntersectCompoundType, ae)
ia := exp.NewCompoundExpression(exp.IntersectAllCompoundType, ae)
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: u, sql: ` UNION (SELECT * FROM "b")`},
expressionTestCase{val: u, sql: ` UNION (SELECT * FROM "b")`, isPrepared: true},
expressionTestCase{val: ua, sql: ` UNION ALL (SELECT * FROM "b")`},
expressionTestCase{val: ua, sql: ` UNION ALL (SELECT * FROM "b")`, isPrepared: true},
expressionTestCase{val: i, sql: ` INTERSECT (SELECT * FROM "b")`},
expressionTestCase{val: i, sql: ` INTERSECT (SELECT * FROM "b")`, isPrepared: true},
expressionTestCase{val: ia, sql: ` INTERSECT ALL (SELECT * FROM "b")`},
expressionTestCase{val: ia, sql: ` INTERSECT ALL (SELECT * FROM "b")`, isPrepared: true},
)
opts := sqlgen.DefaultDialectOptions()
opts.WrapCompoundsInParens = false
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", opts),
expressionTestCase{val: u, sql: ` UNION SELECT * FROM "b"`},
expressionTestCase{val: u, sql: ` UNION SELECT * FROM "b"`, isPrepared: true},
expressionTestCase{val: ua, sql: ` UNION ALL SELECT * FROM "b"`},
expressionTestCase{val: ua, sql: ` UNION ALL SELECT * FROM "b"`, isPrepared: true},
expressionTestCase{val: i, sql: ` INTERSECT SELECT * FROM "b"`},
expressionTestCase{val: i, sql: ` INTERSECT SELECT * FROM "b"`, isPrepared: true},
expressionTestCase{val: ia, sql: ` INTERSECT ALL SELECT * FROM "b"`},
expressionTestCase{val: ia, sql: ` INTERSECT ALL SELECT * FROM "b"`, isPrepared: true},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_IdentifierExpression() {
col := exp.NewIdentifierExpression("", "", "col")
colStar := exp.NewIdentifierExpression("", "", "*")
table := exp.NewIdentifierExpression("", "table", "")
schema := exp.NewIdentifierExpression("schema", "", "")
tableCol := exp.NewIdentifierExpression("", "table", "col")
schemaTableCol := exp.NewIdentifierExpression("schema", "table", "col")
parsedCol := exp.ParseIdentifier("col")
parsedTableCol := exp.ParseIdentifier("table.col")
parsedSchemaTableCol := exp.ParseIdentifier("schema.table.col")
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{
val: exp.NewIdentifierExpression("", "", ""),
err: `db: a empty identifier was encountered, please specify a "schema", "table" or "column"`,
},
expressionTestCase{
val: exp.NewIdentifierExpression("", "", nil),
err: `db: a empty identifier was encountered, please specify a "schema", "table" or "column"`,
},
expressionTestCase{
val: exp.NewIdentifierExpression("", "", false),
err: `db: unexpected col type must be string or LiteralExpression received bool`,
},
expressionTestCase{val: col, sql: `"col"`},
expressionTestCase{val: col, sql: `"col"`, isPrepared: true},
expressionTestCase{val: col.Table("table"), sql: `"table"."col"`},
expressionTestCase{val: col.Table("table"), sql: `"table"."col"`, isPrepared: true},
expressionTestCase{val: col.Table("table").Schema("schema"), sql: `"schema"."table"."col"`},
expressionTestCase{val: col.Table("table").Schema("schema"), sql: `"schema"."table"."col"`, isPrepared: true},
expressionTestCase{val: colStar, sql: `*`},
expressionTestCase{val: colStar, sql: `*`, isPrepared: true},
expressionTestCase{val: colStar.Table("table"), sql: `"table".*`},
expressionTestCase{val: colStar.Table("table"), sql: `"table".*`, isPrepared: true},
expressionTestCase{val: colStar.Table("table").Schema("schema"), sql: `"schema"."table".*`},
expressionTestCase{val: colStar.Table("table").Schema("schema"), sql: `"schema"."table".*`, isPrepared: true},
expressionTestCase{val: table, sql: `"table"`},
expressionTestCase{val: table, sql: `"table"`, isPrepared: true},
expressionTestCase{val: table.Col("col"), sql: `"table"."col"`},
expressionTestCase{val: table.Col("col"), sql: `"table"."col"`, isPrepared: true},
expressionTestCase{val: table.Col(nil), sql: `"table"`},
expressionTestCase{val: table.Col(nil), sql: `"table"`, isPrepared: true},
expressionTestCase{val: table.Col("*"), sql: `"table".*`},
expressionTestCase{val: table.Col("*"), sql: `"table".*`, isPrepared: true},
expressionTestCase{val: table.Schema("schema").Col("col"), sql: `"schema"."table"."col"`},
expressionTestCase{val: table.Schema("schema").Col("col"), sql: `"schema"."table"."col"`, isPrepared: true},
expressionTestCase{val: schema, sql: `"schema"`},
expressionTestCase{val: schema, sql: `"schema"`, isPrepared: true},
expressionTestCase{val: schema.Table("table"), sql: `"schema"."table"`},
expressionTestCase{val: schema.Table("table"), sql: `"schema"."table"`, isPrepared: true},
expressionTestCase{val: schema.Table("table").Col("col"), sql: `"schema"."table"."col"`},
expressionTestCase{val: schema.Table("table").Col("col"), sql: `"schema"."table"."col"`, isPrepared: true},
expressionTestCase{val: schema.Table("table").Col(nil), sql: `"schema"."table"`},
expressionTestCase{val: schema.Table("table").Col(nil), sql: `"schema"."table"`, isPrepared: true},
expressionTestCase{val: schema.Table("table").Col("*"), sql: `"schema"."table".*`},
expressionTestCase{val: schema.Table("table").Col("*"), sql: `"schema"."table".*`, isPrepared: true},
expressionTestCase{val: tableCol, sql: `"table"."col"`},
expressionTestCase{val: tableCol, sql: `"table"."col"`, isPrepared: true},
expressionTestCase{val: schemaTableCol, sql: `"schema"."table"."col"`},
expressionTestCase{val: schemaTableCol, sql: `"schema"."table"."col"`, isPrepared: true},
expressionTestCase{val: parsedCol, sql: `"col"`},
expressionTestCase{val: parsedCol, sql: `"col"`, isPrepared: true},
expressionTestCase{val: parsedTableCol, sql: `"table"."col"`},
expressionTestCase{val: parsedTableCol, sql: `"table"."col"`, isPrepared: true},
expressionTestCase{val: parsedSchemaTableCol, sql: `"schema"."table"."col"`},
expressionTestCase{val: parsedSchemaTableCol, sql: `"schema"."table"."col"`, isPrepared: true},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_LateralExpression() {
lateralExp := exp.NewLateralExpression(newTestAppendableExpression(`SELECT * FROM "test"`, emptyArgs, nil, nil))
do := sqlgen.DefaultDialectOptions()
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", do),
expressionTestCase{val: lateralExp, sql: `LATERAL (SELECT * FROM "test")`},
expressionTestCase{val: lateralExp, sql: `LATERAL (SELECT * FROM "test")`, isPrepared: true},
)
do = sqlgen.DefaultDialectOptions()
do.LateralFragment = []byte("lateral ")
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", do),
expressionTestCase{val: lateralExp, sql: `lateral (SELECT * FROM "test")`},
expressionTestCase{val: lateralExp, sql: `lateral (SELECT * FROM "test")`, isPrepared: true},
)
do = sqlgen.DefaultDialectOptions()
do.SupportsLateral = false
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", do),
expressionTestCase{val: lateralExp, err: "db: dialect does not support lateral expressions [dialect=test]"},
expressionTestCase{val: lateralExp, err: "db: dialect does not support lateral expressions [dialect=test]", isPrepared: true},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_CaseExpression() {
ident := exp.NewIdentifierExpression("", "", "col")
valueCase := exp.NewCaseExpression().
Value(ident).
When(true, "one").
When(false, "two")
valueElseCase := exp.NewCaseExpression().
Value(ident).
When(1, "one").
When(2, "two").
Else("three")
searchCase := exp.NewCaseExpression().
When(ident.Gt(1), exp.NewLiteralExpression("? - 1", ident)).
When(ident.Lt(0), exp.NewLiteralExpression("? + 1", ident))
searchElseCase := exp.NewCaseExpression().
When(ident.Gt(1), exp.NewLiteralExpression("? - 1", ident)).
When(ident.Lt(0), exp.NewLiteralExpression("? + 1", ident)).
Else(ident)
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: valueCase, sql: `CASE "col" WHEN TRUE THEN 'one' WHEN FALSE THEN 'two' END`},
expressionTestCase{
val: valueCase,
sql: `CASE "col" WHEN ? THEN ? WHEN ? THEN ? END`,
isPrepared: true,
args: []interface{}{true, "one", false, "two"},
},
expressionTestCase{val: valueElseCase, sql: `CASE "col" WHEN 1 THEN 'one' WHEN 2 THEN 'two' ELSE 'three' END`},
expressionTestCase{
val: valueElseCase,
sql: `CASE "col" WHEN ? THEN ? WHEN ? THEN ? ELSE ? END`,
isPrepared: true,
args: []interface{}{int64(1), "one", int64(2), "two", "three"},
},
expressionTestCase{val: searchCase, sql: `CASE WHEN ("col" > 1) THEN "col" - 1 WHEN ("col" < 0) THEN "col" + 1 END`},
expressionTestCase{
val: searchCase,
sql: `CASE WHEN ("col" > ?) THEN "col" - 1 WHEN ("col" < ?) THEN "col" + 1 END`,
isPrepared: true,
args: []interface{}{int64(1), int64(0)},
},
expressionTestCase{val: searchElseCase, sql: `CASE WHEN ("col" > 1) THEN "col" - 1 WHEN ("col" < 0) THEN "col" + 1 ELSE "col" END`},
expressionTestCase{
val: searchElseCase,
sql: `CASE WHEN ("col" > ?) THEN "col" - 1 WHEN ("col" < ?) THEN "col" + 1 ELSE "col" END`,
isPrepared: true,
args: []interface{}{int64(1), int64(0)},
},
expressionTestCase{
val: exp.NewCaseExpression(),
err: "db: when conditions not found for case statement",
},
)
opts := sqlgen.DefaultDialectOptions()
opts.CaseFragment = []byte("case ")
opts.WhenFragment = []byte(" when ")
opts.ThenFragment = []byte(" then ")
opts.ElseFragment = []byte(" else ")
opts.EndFragment = []byte(" end")
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", opts),
expressionTestCase{val: valueCase, sql: `case "col" when TRUE then 'one' when FALSE then 'two' end`},
expressionTestCase{
val: valueCase,
sql: `case "col" when ? then ? when ? then ? end`,
isPrepared: true,
args: []interface{}{true, "one", false, "two"},
},
expressionTestCase{val: valueElseCase, sql: `case "col" when 1 then 'one' when 2 then 'two' else 'three' end`},
expressionTestCase{
val: valueElseCase,
sql: `case "col" when ? then ? when ? then ? else ? end`,
isPrepared: true,
args: []interface{}{int64(1), "one", int64(2), "two", "three"},
},
expressionTestCase{val: searchCase, sql: `case when ("col" > 1) then "col" - 1 when ("col" < 0) then "col" + 1 end`},
expressionTestCase{
val: searchCase,
sql: `case when ("col" > ?) then "col" - 1 when ("col" < ?) then "col" + 1 end`,
isPrepared: true,
args: []interface{}{int64(1), int64(0)},
},
expressionTestCase{val: searchElseCase, sql: `case when ("col" > 1) then "col" - 1 when ("col" < 0) then "col" + 1 else "col" end`},
expressionTestCase{
val: searchElseCase,
sql: `case when ("col" > ?) then "col" - 1 when ("col" < ?) then "col" + 1 else "col" end`,
isPrepared: true,
args: []interface{}{int64(1), int64(0)},
},
expressionTestCase{
val: exp.NewCaseExpression(),
err: "db: when conditions not found for case statement",
},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_ExpressionMap() {
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: exp.Ex{}},
expressionTestCase{val: exp.Ex{}, isPrepared: true},
expressionTestCase{val: exp.Ex{"a": 1}, sql: `("a" = 1)`},
expressionTestCase{val: exp.Ex{"a": 1}, sql: `("a" = ?)`, isPrepared: true, args: []interface{}{int64(1)}},
expressionTestCase{val: exp.Ex{"a": true}, sql: `("a" IS TRUE)`},
expressionTestCase{val: exp.Ex{"a": true}, sql: `("a" IS TRUE)`, isPrepared: true},
expressionTestCase{val: exp.Ex{"a": false}, sql: `("a" IS FALSE)`},
expressionTestCase{val: exp.Ex{"a": false}, sql: `("a" IS FALSE)`, isPrepared: true},
expressionTestCase{val: exp.Ex{"a": nil}, sql: `("a" IS NULL)`},
expressionTestCase{val: exp.Ex{"a": nil}, sql: `("a" IS NULL)`, isPrepared: true},
expressionTestCase{val: exp.Ex{"a": []string{"a", "b", "c"}}, sql: `("a" IN ('a', 'b', 'c'))`},
expressionTestCase{
val: exp.Ex{"a": []string{"a", "b", "c"}},
sql: `("a" IN (?, ?, ?))`,
isPrepared: true,
args: []interface{}{"a", "b", "c"},
},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_ExpressionMapWithABadOp() {
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{
val: exp.Ex{"a": exp.Op{"badOp": true}},
err: "db: unsupported expression type badOp",
},
expressionTestCase{
val: exp.Ex{"a": exp.Op{"badOp": true}},
isPrepared: true,
err: "db: unsupported expression type badOp",
},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_ExpressionMapWithNeqOp() {
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: exp.Ex{"a": exp.Op{"neq": 1}}, sql: `("a" != 1)`},
expressionTestCase{val: exp.Ex{"a": exp.Op{"neq": 1}}, sql: `("a" != ?)`, isPrepared: true, args: []interface{}{
int64(1),
}},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_ExpressionMapWithIsNotOp() {
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: exp.Ex{"a": exp.Op{"isnot": true}}, sql: `("a" IS NOT TRUE)`},
expressionTestCase{val: exp.Ex{"a": exp.Op{"isnot": true}}, sql: `("a" IS NOT TRUE)`, isPrepared: true},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_ExpressionMapWithGtOp() {
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: exp.Ex{"a": exp.Op{"gt": 1}}, sql: `("a" > 1)`},
expressionTestCase{val: exp.Ex{"a": exp.Op{"gt": 1}}, sql: `("a" > ?)`, isPrepared: true, args: []interface{}{
int64(1),
}},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_ExpressionMapWithGteOp() {
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: exp.Ex{"a": exp.Op{"gte": 1}}, sql: `("a" >= 1)`},
expressionTestCase{val: exp.Ex{"a": exp.Op{"gte": 1}}, sql: `("a" >= ?)`, isPrepared: true, args: []interface{}{
int64(1),
}},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_ExpressionMapWithLtOp() {
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: exp.Ex{"a": exp.Op{"lt": 1}}, sql: `("a" < 1)`},
expressionTestCase{val: exp.Ex{"a": exp.Op{"lt": 1}}, sql: `("a" < ?)`, isPrepared: true, args: []interface{}{
int64(1),
}},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_ExpressionMapWithLteOp() {
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: exp.Ex{"a": exp.Op{"lte": 1}}, sql: `("a" <= 1)`},
expressionTestCase{val: exp.Ex{"a": exp.Op{"lte": 1}}, sql: `("a" <= ?)`, isPrepared: true, args: []interface{}{
int64(1),
}},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_ExpressionMapWithLikeOp() {
re := regexp.MustCompile("[ab]")
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: exp.Ex{"a": exp.Op{"like": "a%"}}, sql: `("a" LIKE 'a%')`},
expressionTestCase{
val: exp.Ex{"a": exp.Op{"like": "a%"}},
sql: `("a" LIKE ?)`,
isPrepared: true,
args: []interface{}{"a%"},
},
expressionTestCase{val: exp.Ex{"a": exp.Op{"like": re}}, sql: `("a" ~ '[ab]')`},
expressionTestCase{
val: exp.Ex{"a": exp.Op{"like": re}},
sql: `("a" ~ ?)`,
isPrepared: true,
args: []interface{}{"[ab]"},
},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_ExpressionMapWithNotLikeOp() {
re := regexp.MustCompile("[ab]")
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: exp.Ex{"a": exp.Op{"notLike": "a%"}}, sql: `("a" NOT LIKE 'a%')`},
expressionTestCase{
val: exp.Ex{"a": exp.Op{"notLike": "a%"}},
sql: `("a" NOT LIKE ?)`,
isPrepared: true,
args: []interface{}{"a%"},
},
expressionTestCase{val: exp.Ex{"a": exp.Op{"notLike": re}}, sql: `("a" !~ '[ab]')`},
expressionTestCase{
val: exp.Ex{"a": exp.Op{"notLike": re}},
sql: `("a" !~ ?)`,
isPrepared: true,
args: []interface{}{"[ab]"},
},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_ExpressionMapWithILikeOp() {
re := regexp.MustCompile("[ab]")
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: exp.Ex{"a": exp.Op{"iLike": "a%"}}, sql: `("a" ILIKE 'a%')`},
expressionTestCase{
val: exp.Ex{"a": exp.Op{"iLike": "a%"}},
sql: `("a" ILIKE ?)`,
isPrepared: true,
args: []interface{}{"a%"},
},
expressionTestCase{val: exp.Ex{"a": exp.Op{"iLike": re}}, sql: `("a" ~* '[ab]')`},
expressionTestCase{
val: exp.Ex{"a": exp.Op{"iLike": re}},
sql: `("a" ~* ?)`,
isPrepared: true,
args: []interface{}{"[ab]"},
},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_ExpressionMapWithNotILikeOp() {
re := regexp.MustCompile("[ab]")
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: exp.Ex{"a": exp.Op{"notILike": "a%"}}, sql: `("a" NOT ILIKE 'a%')`},
expressionTestCase{
val: exp.Ex{"a": exp.Op{"notILike": "a%"}},
sql: `("a" NOT ILIKE ?)`,
isPrepared: true,
args: []interface{}{"a%"},
},
expressionTestCase{val: exp.Ex{"a": exp.Op{"notILike": re}}, sql: `("a" !~* '[ab]')`},
expressionTestCase{
val: exp.Ex{"a": exp.Op{"notILike": re}},
sql: `("a" !~* ?)`,
isPrepared: true,
args: []interface{}{"[ab]"},
},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_ExpressionMapWithRegExpLikeOp() {
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: exp.Ex{"a": exp.Op{"regexpLike": "[ab]"}}, sql: `("a" ~ '[ab]')`},
expressionTestCase{
val: exp.Ex{"a": exp.Op{"regexpLike": "[ab]"}},
sql: `("a" ~ ?)`,
isPrepared: true,
args: []interface{}{"[ab]"},
},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_ExpressionMapWithRegExpILikeOp() {
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: exp.Ex{"a": exp.Op{"regexpILike": "[ab]"}}, sql: `("a" ~* '[ab]')`},
expressionTestCase{
val: exp.Ex{"a": exp.Op{"regexpILike": "[ab]"}},
sql: `("a" ~* ?)`,
isPrepared: true,
args: []interface{}{"[ab]"},
},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_ExpressionMapWithRegExpNotLikeOp() {
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: exp.Ex{"a": exp.Op{"regexpNotLike": "[ab]"}}, sql: `("a" !~ '[ab]')`},
expressionTestCase{
val: exp.Ex{"a": exp.Op{"regexpNotLike": "[ab]"}},
sql: `("a" !~ ?)`,
isPrepared: true,
args: []interface{}{"[ab]"},
},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_ExpressionMapWithInOp() {
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: exp.Ex{"a": exp.Op{"in": []string{"a", "b", "c"}}}, sql: `("a" IN ('a', 'b', 'c'))`},
expressionTestCase{
val: exp.Ex{"a": exp.Op{"in": []string{"a", "b", "c"}}},
sql: `("a" IN (?, ?, ?))`,
isPrepared: true,
args: []interface{}{"a", "b", "c"},
},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_ExpressionMapWithNotInOp() {
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{
val: exp.Ex{"a": exp.Op{"notIn": []string{"a", "b", "c"}}},
sql: `("a" NOT IN ('a', 'b', 'c'))`,
},
expressionTestCase{
val: exp.Ex{"a": exp.Op{"notIn": []string{"a", "b", "c"}}},
sql: `("a" NOT IN (?, ?, ?))`,
isPrepared: true,
args: []interface{}{"a", "b", "c"},
},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_ExpressionMapBetweenOp() {
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{
val: exp.Ex{"a": exp.Op{"between": exp.NewRangeVal("aaa", "zzz")}},
sql: `("a" BETWEEN 'aaa' AND 'zzz')`,
},
expressionTestCase{
val: exp.Ex{"a": exp.Op{"between": exp.NewRangeVal("aaa", "zzz")}},
sql: `("a" BETWEEN ? AND ?)`,
isPrepared: true,
args: []interface{}{"aaa", "zzz"},
},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_ExpressionMapNotBetweenOp() {
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{
val: exp.Ex{"a": exp.Op{"notBetween": exp.NewRangeVal("aaa", "zzz")}},
sql: `("a" NOT BETWEEN 'aaa' AND 'zzz')`,
},
expressionTestCase{
val: exp.Ex{"a": exp.Op{"notBetween": exp.NewRangeVal("aaa", "zzz")}},
sql: `("a" NOT BETWEEN ? AND ?)`,
isPrepared: true,
args: []interface{}{"aaa", "zzz"},
},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_ExpressionMapIsOp() {
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("test", sqlgen.DefaultDialectOptions()),
expressionTestCase{
val: exp.Ex{"a": exp.Op{"is": nil, "eq": 10}},
sql: `(("a" = 10) OR ("a" IS NULL))`,
},
expressionTestCase{
val: exp.Ex{"a": exp.Op{"is": nil, "eq": 10}},
sql: `(("a" = ?) OR ("a" IS NULL))`,
isPrepared: true,
args: []interface{}{int64(10)},
},
)
}
func (esgs *expressionSQLGeneratorSuite) TestGenerate_ExpressionOrMap() {
esgs.assertCases(
sqlgen.NewExpressionSQLGenerator("default", sqlgen.DefaultDialectOptions()),
expressionTestCase{val: exp.ExOr{}},
expressionTestCase{val: exp.ExOr{}, isPrepared: true},
expressionTestCase{val: exp.ExOr{"a": exp.Op{"regexpLike": "[ab]"}}, sql: `("a" ~ '[ab]')`},
expressionTestCase{
val: exp.ExOr{"a": exp.Op{"regexpLike": "[ab]"}},
sql: `("a" ~ ?)`,
isPrepared: true,
args: []interface{}{"[ab]"},
},
expressionTestCase{val: exp.ExOr{"a": exp.Op{"regexpNotLike": "[ab]"}}, sql: `("a" !~ '[ab]')`},
expressionTestCase{
val: exp.ExOr{"a": exp.Op{"regexpNotLike": "[ab]"}},
sql: `("a" !~ ?)`,
isPrepared: true,
args: []interface{}{"[ab]"},
},
expressionTestCase{val: exp.ExOr{"a": exp.Op{"regexpILike": "[ab]"}}, sql: `("a" ~* '[ab]')`},
expressionTestCase{
val: exp.ExOr{"a": exp.Op{"regexpILike": "[ab]"}},
sql: `("a" ~* ?)`,
isPrepared: true,
args: []interface{}{"[ab]"},
},
expressionTestCase{val: exp.ExOr{"a": exp.Op{"regexpNotILike": "[ab]"}}, sql: `("a" !~* '[ab]')`},
expressionTestCase{
val: exp.ExOr{"a": exp.Op{"regexpNotILike": "[ab]"}},
sql: `("a" !~* ?)`,
isPrepared: true,
args: []interface{}{"[ab]"},
},
expressionTestCase{
val: exp.ExOr{"a": exp.Op{"badOp": true}},
err: "db: unsupported expression type badOp",
},
expressionTestCase{
val: exp.ExOr{"a": exp.Op{"badOp": true}},
isPrepared: true,
err: "db: unsupported expression type badOp",
},
expressionTestCase{val: exp.ExOr{"a": 1, "b": true}, sql: `(("a" = 1) OR ("b" IS TRUE))`},
expressionTestCase{
val: exp.ExOr{"a": 1, "b": true},
sql: `(("a" = ?) OR ("b" IS TRUE))`,
isPrepared: true,
args: []interface{}{int64(1)},
},
expressionTestCase{
val: exp.ExOr{"a": 1, "b": []string{"a", "b", "c"}},
sql: `(("a" = 1) OR ("b" IN ('a', 'b', 'c')))`,
},
expressionTestCase{
val: exp.ExOr{"a": 1, "b": []string{"a", "b", "c"}},
sql: `(("a" = ?) OR ("b" IN (?, ?, ?)))`,
isPrepared: true,
args: []interface{}{int64(1), "a", "b", "c"},
},
)
}
func TestExpressionSQLGenerator(t *testing.T) {
suite.Run(t, new(expressionSQLGeneratorSuite))
}