Create comprehensive documentation for future Claude Code instances working in this repository, including: - Development commands for testing, building, and code quality - Core architecture overview of the SQL query builder system - Directory structure and component explanations - Testing patterns and conventions - Key dependencies and their purposes 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
616 lines
24 KiB
Go
616 lines
24 KiB
Go
package sqlgen_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
dbv2 "git.fsdpf.net/go/db"
|
|
"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"
|
|
)
|
|
|
|
type (
|
|
selectTestCase struct {
|
|
clause exp.SelectClauses
|
|
sql string
|
|
isPrepared bool
|
|
args []interface{}
|
|
err string
|
|
}
|
|
selectSQLGeneratorSuite struct {
|
|
baseSQLGeneratorSuite
|
|
}
|
|
)
|
|
|
|
func (ssgs *selectSQLGeneratorSuite) assertCases(ssg sqlgen.SelectSQLGenerator, testCases ...selectTestCase) {
|
|
for _, tc := range testCases {
|
|
b := sb.NewSQLBuilder(tc.isPrepared)
|
|
ssg.Generate(b, tc.clause)
|
|
switch {
|
|
case len(tc.err) > 0:
|
|
ssgs.assertErrorSQL(b, tc.err)
|
|
case tc.isPrepared:
|
|
ssgs.assertPreparedSQL(b, tc.sql, tc.args)
|
|
default:
|
|
ssgs.assertNotPreparedSQL(b, tc.sql)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (ssgs *selectSQLGeneratorSuite) TestDialect() {
|
|
opts := sqlgen.DefaultDialectOptions()
|
|
d := sqlgen.NewSelectSQLGenerator("test", opts)
|
|
ssgs.Equal("test", d.Dialect())
|
|
|
|
opts2 := sqlgen.DefaultDialectOptions()
|
|
d2 := sqlgen.NewSelectSQLGenerator("test2", opts2)
|
|
ssgs.Equal("test2", d2.Dialect())
|
|
}
|
|
|
|
func (ssgs *selectSQLGeneratorSuite) TestGenerate() {
|
|
opts := sqlgen.DefaultDialectOptions()
|
|
opts.SelectClause = []byte("select")
|
|
opts.StarRune = '#'
|
|
|
|
sc := exp.NewSelectClauses().SetFrom(exp.NewColumnListExpression("test"))
|
|
scWithCols := sc.SetSelect(exp.NewColumnListExpression("a", "b"))
|
|
|
|
ident := exp.NewIdentifierExpression("", "", "a")
|
|
scWithBooExpAliased := sc.SetSelect(
|
|
exp.NewColumnListExpression(
|
|
ident.Eq(1).As("x"),
|
|
ident.IsNull().As("y"),
|
|
),
|
|
)
|
|
|
|
ssgs.assertCases(
|
|
sqlgen.NewSelectSQLGenerator("test", opts),
|
|
selectTestCase{clause: sc, sql: `select # FROM "test"`},
|
|
selectTestCase{clause: sc, sql: `select # FROM "test"`, isPrepared: true},
|
|
|
|
selectTestCase{clause: scWithCols, sql: `select "a", "b" FROM "test"`},
|
|
selectTestCase{clause: scWithCols, sql: `select "a", "b" FROM "test"`, isPrepared: true},
|
|
|
|
selectTestCase{
|
|
clause: scWithBooExpAliased,
|
|
sql: `select ("a" = 1) AS "x", ("a" IS NULL) AS "y" FROM "test"`,
|
|
},
|
|
selectTestCase{
|
|
clause: scWithBooExpAliased,
|
|
sql: `select ("a" = ?) AS "x", ("a" IS NULL) AS "y" FROM "test"`,
|
|
isPrepared: true,
|
|
args: []interface{}{int64(1)},
|
|
},
|
|
)
|
|
}
|
|
|
|
func (ssgs *selectSQLGeneratorSuite) TestGenerate_UnsupportedFragment() {
|
|
opts := sqlgen.DefaultDialectOptions()
|
|
opts.SelectSQLOrder = []sqlgen.SQLFragmentType{sqlgen.InsertBeingSQLFragment}
|
|
|
|
sc := exp.NewSelectClauses().SetFrom(exp.NewColumnListExpression("test"))
|
|
expectedErr := "db: unsupported SELECT SQL fragment InsertBeingSQLFragment"
|
|
ssgs.assertCases(
|
|
sqlgen.NewSelectSQLGenerator("test", opts),
|
|
selectTestCase{clause: sc, err: expectedErr},
|
|
selectTestCase{clause: sc, err: expectedErr, isPrepared: true},
|
|
)
|
|
}
|
|
|
|
func (ssgs *selectSQLGeneratorSuite) TestGenerate_WithErroredBuilder() {
|
|
opts := sqlgen.DefaultDialectOptions()
|
|
opts.SelectSQLOrder = []sqlgen.SQLFragmentType{sqlgen.InsertBeingSQLFragment}
|
|
d := sqlgen.NewSelectSQLGenerator("test", opts)
|
|
|
|
b := sb.NewSQLBuilder(true).SetError(errors.New("test error"))
|
|
c := exp.NewSelectClauses().SetFrom(exp.NewColumnListExpression("test"))
|
|
d.Generate(b, c)
|
|
ssgs.assertErrorSQL(b, `db: test error`)
|
|
}
|
|
|
|
func (ssgs *selectSQLGeneratorSuite) TestGenerate_withSelectedColumns() {
|
|
opts := sqlgen.DefaultDialectOptions()
|
|
// make sure the fragments are used
|
|
opts.SelectClause = []byte("select")
|
|
opts.StarRune = '#'
|
|
opts.SupportsDistinctOn = true
|
|
|
|
sc := exp.NewSelectClauses()
|
|
scCols := sc.SetSelect(exp.NewColumnListExpression("a", "b"))
|
|
scFuncs := sc.SetSelect(exp.NewColumnListExpression(
|
|
exp.NewSQLFunctionExpression("COUNT", exp.Star()),
|
|
exp.NewSQLFunctionExpression("RANK"),
|
|
))
|
|
|
|
we := exp.NewWindowExpression(
|
|
nil,
|
|
nil,
|
|
exp.NewColumnListExpression("a", "b"),
|
|
exp.NewOrderedColumnList(exp.ParseIdentifier("c").Asc()),
|
|
)
|
|
scFuncsPartition := sc.SetSelect(exp.NewColumnListExpression(
|
|
exp.NewSQLFunctionExpression("COUNT", exp.Star()).Over(we),
|
|
exp.NewSQLFunctionExpression("RANK").Over(we.Inherit("w")),
|
|
))
|
|
|
|
ssgs.assertCases(
|
|
sqlgen.NewSelectSQLGenerator("test", opts),
|
|
selectTestCase{clause: sc, sql: `select #`},
|
|
selectTestCase{clause: sc, sql: `select #`, isPrepared: true},
|
|
|
|
selectTestCase{clause: scCols, sql: `select "a", "b"`},
|
|
selectTestCase{clause: scCols, sql: `select "a", "b"`, isPrepared: true},
|
|
|
|
selectTestCase{clause: scFuncs, sql: `select COUNT(*), RANK()`},
|
|
selectTestCase{clause: scFuncs, sql: `select COUNT(*), RANK()`, isPrepared: true},
|
|
|
|
selectTestCase{
|
|
clause: scFuncsPartition,
|
|
sql: `select COUNT(*) OVER (PARTITION BY "a", "b" ORDER BY "c" ASC), RANK() OVER ("w" PARTITION BY "a", "b" ORDER BY "c" ASC)`,
|
|
},
|
|
selectTestCase{
|
|
clause: scFuncsPartition,
|
|
sql: `select COUNT(*) OVER (PARTITION BY "a", "b" ORDER BY "c" ASC), RANK() OVER ("w" PARTITION BY "a", "b" ORDER BY "c" ASC)`,
|
|
isPrepared: true,
|
|
},
|
|
)
|
|
}
|
|
|
|
func (ssgs *selectSQLGeneratorSuite) TestGenerate_withDistinct() {
|
|
opts := sqlgen.DefaultDialectOptions()
|
|
// make sure the fragments are used
|
|
opts.SelectClause = []byte("select")
|
|
opts.StarRune = '#'
|
|
opts.DistinctFragment = []byte("distinct")
|
|
opts.OnFragment = []byte(" on ")
|
|
opts.SupportsDistinctOn = true
|
|
|
|
sc := exp.NewSelectClauses().SetDistinct(exp.NewColumnListExpression())
|
|
scDistinctOn := sc.SetDistinct(exp.NewColumnListExpression("a", "b"))
|
|
|
|
ssgs.assertCases(
|
|
sqlgen.NewSelectSQLGenerator("test", opts),
|
|
selectTestCase{clause: sc, sql: `select distinct #`},
|
|
selectTestCase{clause: sc, sql: `select distinct #`, isPrepared: true},
|
|
|
|
selectTestCase{clause: scDistinctOn, sql: `select distinct on ("a", "b") #`},
|
|
selectTestCase{clause: scDistinctOn, sql: `select distinct on ("a", "b") #`, isPrepared: true},
|
|
)
|
|
|
|
opts = sqlgen.DefaultDialectOptions()
|
|
opts.SupportsDistinctOn = false
|
|
expectedErr := "db: dialect does not support DISTINCT ON clause [dialect=test]"
|
|
ssgs.assertCases(
|
|
sqlgen.NewSelectSQLGenerator("test", opts),
|
|
selectTestCase{clause: sc, sql: `SELECT DISTINCT *`},
|
|
selectTestCase{clause: sc, sql: `SELECT DISTINCT *`, isPrepared: true},
|
|
|
|
selectTestCase{clause: scDistinctOn, err: expectedErr},
|
|
selectTestCase{clause: scDistinctOn, err: expectedErr, isPrepared: true},
|
|
)
|
|
}
|
|
|
|
func (ssgs *selectSQLGeneratorSuite) TestGenerate_withFromSQL() {
|
|
opts := sqlgen.DefaultDialectOptions()
|
|
opts.FromFragment = []byte(" from")
|
|
|
|
sc := exp.NewSelectClauses()
|
|
scFrom := sc.SetFrom(exp.NewColumnListExpression("a", "b"))
|
|
ssgs.assertCases(
|
|
sqlgen.NewSelectSQLGenerator("test", opts),
|
|
selectTestCase{clause: sc, sql: `SELECT *`},
|
|
selectTestCase{clause: sc, sql: `SELECT *`, isPrepared: true},
|
|
|
|
selectTestCase{clause: scFrom, sql: `SELECT * from "a", "b"`},
|
|
selectTestCase{clause: scFrom, sql: `SELECT * from "a", "b"`, isPrepared: true},
|
|
)
|
|
}
|
|
|
|
func (ssgs *selectSQLGeneratorSuite) TestGenerate_withJoin() {
|
|
opts := sqlgen.DefaultDialectOptions()
|
|
// override fragements to make sure dialect is used
|
|
opts.UsingFragment = []byte(" using ")
|
|
opts.OnFragment = []byte(" on ")
|
|
opts.JoinTypeLookup = map[exp.JoinType][]byte{
|
|
exp.LeftJoinType: []byte(" left join "),
|
|
exp.NaturalJoinType: []byte(" natural join "),
|
|
}
|
|
|
|
sc := exp.NewSelectClauses().SetFrom(exp.NewColumnListExpression("test"))
|
|
ti := exp.NewIdentifierExpression("", "test2", "")
|
|
uj := exp.NewUnConditionedJoinExpression(exp.NaturalJoinType, ti)
|
|
cjo := exp.NewConditionedJoinExpression(exp.LeftJoinType, ti, exp.NewJoinOnCondition(exp.Ex{"a": "foo"}))
|
|
cju := exp.NewConditionedJoinExpression(exp.LeftJoinType, ti, exp.NewJoinUsingCondition("a"))
|
|
rj := exp.NewConditionedJoinExpression(exp.RightJoinType, ti, exp.NewJoinUsingCondition(exp.NewIdentifierExpression("", "", "a")))
|
|
badJoin := exp.NewConditionedJoinExpression(exp.LeftJoinType, ti, exp.NewJoinUsingCondition())
|
|
|
|
expectedRjError := "db: dialect does not support RightJoinType"
|
|
expectedJoinCondError := "db: join condition required for conditioned join LeftJoinType"
|
|
ssgs.assertCases(
|
|
sqlgen.NewSelectSQLGenerator("test", opts),
|
|
selectTestCase{clause: sc.JoinsAppend(uj), sql: `SELECT * FROM "test" natural join "test2"`},
|
|
selectTestCase{clause: sc.JoinsAppend(uj), sql: `SELECT * FROM "test" natural join "test2"`, isPrepared: true},
|
|
|
|
selectTestCase{clause: sc.JoinsAppend(cjo), sql: `SELECT * FROM "test" left join "test2" on ("a" = 'foo')`},
|
|
selectTestCase{
|
|
clause: sc.JoinsAppend(cjo),
|
|
sql: `SELECT * FROM "test" left join "test2" on ("a" = ?)`,
|
|
isPrepared: true,
|
|
args: []interface{}{"foo"},
|
|
},
|
|
|
|
selectTestCase{clause: sc.JoinsAppend(cju), sql: `SELECT * FROM "test" left join "test2" using ("a")`},
|
|
selectTestCase{clause: sc.JoinsAppend(cju), sql: `SELECT * FROM "test" left join "test2" using ("a")`, isPrepared: true},
|
|
|
|
selectTestCase{
|
|
clause: sc.JoinsAppend(uj).JoinsAppend(cjo).JoinsAppend(cju),
|
|
sql: `SELECT * FROM "test" natural join "test2" left join "test2" on ("a" = 'foo') left join "test2" using ("a")`,
|
|
},
|
|
selectTestCase{
|
|
clause: sc.JoinsAppend(uj).JoinsAppend(cjo).JoinsAppend(cju),
|
|
sql: `SELECT * FROM "test" natural join "test2" left join "test2" on ("a" = ?) left join "test2" using ("a")`,
|
|
isPrepared: true,
|
|
args: []interface{}{"foo"},
|
|
},
|
|
|
|
selectTestCase{clause: sc.JoinsAppend(rj), err: expectedRjError},
|
|
selectTestCase{clause: sc.JoinsAppend(rj), err: expectedRjError, isPrepared: true},
|
|
|
|
selectTestCase{clause: sc.JoinsAppend(badJoin), err: expectedJoinCondError},
|
|
selectTestCase{clause: sc.JoinsAppend(badJoin), err: expectedJoinCondError, isPrepared: true},
|
|
)
|
|
}
|
|
|
|
func (ssgs *selectSQLGeneratorSuite) TestGenerate_withWhere() {
|
|
opts := sqlgen.DefaultDialectOptions()
|
|
opts.WhereFragment = []byte(" where ")
|
|
|
|
sc := exp.NewSelectClauses().SetFrom(exp.NewColumnListExpression("test"))
|
|
w := exp.Ex{"a": "b"}
|
|
w2 := exp.Ex{"b": "c"}
|
|
scWhere1 := sc.WhereAppend(w)
|
|
scWhere2 := sc.WhereAppend(w, w2)
|
|
|
|
ssgs.assertCases(
|
|
sqlgen.NewSelectSQLGenerator("test", opts),
|
|
selectTestCase{clause: scWhere1, sql: `SELECT * FROM "test" where ("a" = 'b')`},
|
|
selectTestCase{clause: scWhere1, sql: `SELECT * FROM "test" where ("a" = ?)`, isPrepared: true, args: []interface{}{"b"}},
|
|
|
|
selectTestCase{clause: scWhere2, sql: `SELECT * FROM "test" where (("a" = 'b') AND ("b" = 'c'))`},
|
|
selectTestCase{
|
|
clause: scWhere2,
|
|
sql: `SELECT * FROM "test" where (("a" = ?) AND ("b" = ?))`,
|
|
isPrepared: true,
|
|
args: []interface{}{"b", "c"},
|
|
},
|
|
)
|
|
}
|
|
|
|
func (ssgs *selectSQLGeneratorSuite) TestGenerate_withGroupBy() {
|
|
opts := sqlgen.DefaultDialectOptions()
|
|
opts.GroupByFragment = []byte(" group by ")
|
|
|
|
sc := exp.NewSelectClauses().SetFrom(exp.NewColumnListExpression("test"))
|
|
scGroup := sc.SetGroupBy(exp.NewColumnListExpression("a"))
|
|
scGroupMulti := sc.SetGroupBy(exp.NewColumnListExpression("a", "b"))
|
|
|
|
ssgs.assertCases(
|
|
sqlgen.NewSelectSQLGenerator("test", opts),
|
|
selectTestCase{clause: scGroup, sql: `SELECT * FROM "test" group by "a"`},
|
|
selectTestCase{clause: scGroup, sql: `SELECT * FROM "test" group by "a"`, isPrepared: true},
|
|
|
|
selectTestCase{clause: scGroupMulti, sql: `SELECT * FROM "test" group by "a", "b"`},
|
|
selectTestCase{clause: scGroupMulti, sql: `SELECT * FROM "test" group by "a", "b"`, isPrepared: true},
|
|
)
|
|
}
|
|
|
|
func (ssgs *selectSQLGeneratorSuite) TestGenerate_withHaving() {
|
|
opts := sqlgen.DefaultDialectOptions()
|
|
opts.HavingFragment = []byte(" having ")
|
|
|
|
sc := exp.NewSelectClauses().SetFrom(exp.NewColumnListExpression("test"))
|
|
w := exp.Ex{"a": "b"}
|
|
w2 := exp.Ex{"b": "c"}
|
|
scHaving1 := sc.HavingAppend(w)
|
|
scHaving2 := sc.HavingAppend(w, w2)
|
|
|
|
ssgs.assertCases(
|
|
sqlgen.NewSelectSQLGenerator("test", opts),
|
|
selectTestCase{clause: scHaving1, sql: `SELECT * FROM "test" having ("a" = 'b')`},
|
|
selectTestCase{clause: scHaving1, sql: `SELECT * FROM "test" having ("a" = ?)`, isPrepared: true, args: []interface{}{"b"}},
|
|
|
|
selectTestCase{clause: scHaving2, sql: `SELECT * FROM "test" having (("a" = 'b') AND ("b" = 'c'))`},
|
|
selectTestCase{
|
|
clause: scHaving2,
|
|
sql: `SELECT * FROM "test" having (("a" = ?) AND ("b" = ?))`,
|
|
isPrepared: true,
|
|
args: []interface{}{"b", "c"},
|
|
},
|
|
)
|
|
}
|
|
|
|
func (ssgs *selectSQLGeneratorSuite) TestGenerate_withWindow() {
|
|
opts := sqlgen.DefaultDialectOptions()
|
|
opts.WindowFragment = []byte(" window ")
|
|
opts.WindowPartitionByFragment = []byte("partition by ")
|
|
opts.WindowOrderByFragment = []byte("order by ")
|
|
|
|
sc := exp.NewSelectClauses().SetFrom(exp.NewColumnListExpression("test"))
|
|
we1 := exp.NewWindowExpression(
|
|
exp.NewIdentifierExpression("", "", "w"),
|
|
nil,
|
|
nil,
|
|
nil,
|
|
)
|
|
wePartitionBy := we1.PartitionBy("a", "b")
|
|
weOrderBy := we1.OrderBy("a", "b")
|
|
|
|
weOrderAndPartitionBy := we1.PartitionBy("a", "b").OrderBy("a", "b")
|
|
|
|
weInherits := exp.NewWindowExpression(
|
|
exp.NewIdentifierExpression("", "", "w2"),
|
|
exp.NewIdentifierExpression("", "", "w"),
|
|
nil,
|
|
nil,
|
|
)
|
|
weInheritsPartitionBy := weInherits.PartitionBy("c", "d")
|
|
weInheritsOrderBy := weInherits.OrderBy("c", "d")
|
|
|
|
weInheritsOrderAndPartitionBy := weInherits.PartitionBy("c", "d").OrderBy("c", "d")
|
|
|
|
scNoName := sc.WindowsAppend(exp.NewWindowExpression(nil, nil, nil, nil))
|
|
|
|
scWindow1 := sc.WindowsAppend(we1)
|
|
scWindow2 := sc.WindowsAppend(wePartitionBy)
|
|
scWindow3 := sc.WindowsAppend(weOrderBy)
|
|
scWindow4 := sc.WindowsAppend(weOrderAndPartitionBy)
|
|
|
|
scWindow5 := sc.WindowsAppend(we1, weInherits)
|
|
scWindow6 := sc.WindowsAppend(we1, weInheritsPartitionBy)
|
|
scWindow7 := sc.WindowsAppend(we1, weInheritsOrderBy)
|
|
scWindow8 := sc.WindowsAppend(we1, weInheritsOrderAndPartitionBy)
|
|
|
|
ssgs.assertCases(
|
|
sqlgen.NewSelectSQLGenerator("test", opts),
|
|
|
|
selectTestCase{clause: scNoName, err: sqlgen.ErrNoWindowName.Error()},
|
|
selectTestCase{clause: scNoName, err: sqlgen.ErrNoWindowName.Error(), isPrepared: true},
|
|
|
|
selectTestCase{clause: scWindow1, sql: `SELECT * FROM "test" window "w" AS ()`},
|
|
selectTestCase{clause: scWindow1, sql: `SELECT * FROM "test" window "w" AS ()`, isPrepared: true},
|
|
|
|
selectTestCase{clause: scWindow2, sql: `SELECT * FROM "test" window "w" AS (partition by "a", "b")`},
|
|
selectTestCase{
|
|
clause: scWindow2,
|
|
sql: `SELECT * FROM "test" window "w" AS (partition by "a", "b")`,
|
|
isPrepared: true,
|
|
},
|
|
|
|
selectTestCase{clause: scWindow3, sql: `SELECT * FROM "test" window "w" AS (order by "a", "b")`},
|
|
selectTestCase{
|
|
clause: scWindow3,
|
|
sql: `SELECT * FROM "test" window "w" AS (order by "a", "b")`,
|
|
isPrepared: true,
|
|
},
|
|
|
|
selectTestCase{
|
|
clause: scWindow4,
|
|
sql: `SELECT * FROM "test" window "w" AS (partition by "a", "b" order by "a", "b")`,
|
|
},
|
|
selectTestCase{
|
|
clause: scWindow4,
|
|
sql: `SELECT * FROM "test" window "w" AS (partition by "a", "b" order by "a", "b")`,
|
|
isPrepared: true,
|
|
},
|
|
|
|
selectTestCase{
|
|
clause: scWindow5,
|
|
sql: `SELECT * FROM "test" window "w" AS (), "w2" AS ("w")`,
|
|
},
|
|
selectTestCase{
|
|
clause: scWindow5,
|
|
sql: `SELECT * FROM "test" window "w" AS (), "w2" AS ("w")`,
|
|
isPrepared: true,
|
|
},
|
|
|
|
selectTestCase{
|
|
clause: scWindow6,
|
|
sql: `SELECT * FROM "test" window "w" AS (), "w2" AS ("w" partition by "c", "d")`,
|
|
},
|
|
selectTestCase{
|
|
clause: scWindow6,
|
|
sql: `SELECT * FROM "test" window "w" AS (), "w2" AS ("w" partition by "c", "d")`,
|
|
isPrepared: true,
|
|
},
|
|
|
|
selectTestCase{
|
|
clause: scWindow7,
|
|
sql: `SELECT * FROM "test" window "w" AS (), "w2" AS ("w" order by "c", "d")`,
|
|
},
|
|
selectTestCase{
|
|
clause: scWindow7,
|
|
sql: `SELECT * FROM "test" window "w" AS (), "w2" AS ("w" order by "c", "d")`,
|
|
isPrepared: true,
|
|
},
|
|
|
|
selectTestCase{
|
|
clause: scWindow8,
|
|
sql: `SELECT * FROM "test" window "w" AS (), "w2" AS ("w" partition by "c", "d" order by "c", "d")`,
|
|
},
|
|
selectTestCase{
|
|
clause: scWindow8,
|
|
sql: `SELECT * FROM "test" window "w" AS (), "w2" AS ("w" partition by "c", "d" order by "c", "d")`,
|
|
isPrepared: true,
|
|
},
|
|
)
|
|
|
|
opts = sqlgen.DefaultDialectOptions()
|
|
opts.SupportsWindowFunction = false
|
|
ssgs.assertCases(
|
|
sqlgen.NewSelectSQLGenerator("test", opts),
|
|
|
|
selectTestCase{clause: scWindow1, err: sqlgen.ErrWindowNotSupported("test").Error()},
|
|
selectTestCase{clause: scWindow1, err: sqlgen.ErrWindowNotSupported("test").Error(), isPrepared: true},
|
|
)
|
|
}
|
|
|
|
func (ssgs *selectSQLGeneratorSuite) TestGenerate_withOrder() {
|
|
sc := exp.NewSelectClauses().SetFrom(exp.NewColumnListExpression("test")).
|
|
SetOrder(
|
|
exp.NewIdentifierExpression("", "", "a").Asc(),
|
|
exp.NewIdentifierExpression("", "", "b").Desc(),
|
|
)
|
|
ssgs.assertCases(
|
|
sqlgen.NewSelectSQLGenerator("test", sqlgen.DefaultDialectOptions()),
|
|
selectTestCase{clause: sc, sql: `SELECT * FROM "test" ORDER BY "a" ASC, "b" DESC`},
|
|
selectTestCase{clause: sc, sql: `SELECT * FROM "test" ORDER BY "a" ASC, "b" DESC`, isPrepared: true},
|
|
)
|
|
}
|
|
|
|
func (ssgs *selectSQLGeneratorSuite) TestGenerate_withLimit() {
|
|
sc := exp.NewSelectClauses().SetFrom(exp.NewColumnListExpression("test")).
|
|
SetLimit(10)
|
|
ssgs.assertCases(
|
|
sqlgen.NewSelectSQLGenerator("test", sqlgen.DefaultDialectOptions()),
|
|
selectTestCase{clause: sc, sql: `SELECT * FROM "test" LIMIT 10`},
|
|
selectTestCase{clause: sc, sql: `SELECT * FROM "test" LIMIT ?`, isPrepared: true, args: []interface{}{int64(10)}},
|
|
)
|
|
}
|
|
|
|
func (ssgs *selectSQLGeneratorSuite) TestGenerate_withOffset() {
|
|
sc := exp.NewSelectClauses().SetFrom(exp.NewColumnListExpression("test")).
|
|
SetOffset(10)
|
|
ssgs.assertCases(
|
|
sqlgen.NewSelectSQLGenerator("test", sqlgen.DefaultDialectOptions()),
|
|
selectTestCase{clause: sc, sql: `SELECT * FROM "test" OFFSET 10`},
|
|
selectTestCase{clause: sc, sql: `SELECT * FROM "test" OFFSET ?`, isPrepared: true, args: []interface{}{int64(10)}},
|
|
)
|
|
}
|
|
|
|
func (ssgs *selectSQLGeneratorSuite) TestGenerate_withCommonTables() {
|
|
tse := newTestAppendableExpression("select * from foo", emptyArgs, nil, nil)
|
|
|
|
sc := exp.NewSelectClauses().SetFrom(exp.NewColumnListExpression("test_cte"))
|
|
scCte1 := sc.CommonTablesAppend(exp.NewCommonTableExpression(false, "test_cte", tse))
|
|
scCte2 := sc.CommonTablesAppend(exp.NewCommonTableExpression(true, "test_cte", tse))
|
|
|
|
ssgs.assertCases(
|
|
sqlgen.NewSelectSQLGenerator("test", sqlgen.DefaultDialectOptions()),
|
|
selectTestCase{clause: scCte1, sql: `WITH test_cte AS (select * from foo) SELECT * FROM "test_cte"`},
|
|
selectTestCase{clause: scCte1, sql: `WITH test_cte AS (select * from foo) SELECT * FROM "test_cte"`, isPrepared: true},
|
|
|
|
selectTestCase{clause: scCte2, sql: `WITH RECURSIVE test_cte AS (select * from foo) SELECT * FROM "test_cte"`},
|
|
selectTestCase{clause: scCte2, sql: `WITH RECURSIVE test_cte AS (select * from foo) SELECT * FROM "test_cte"`, isPrepared: true},
|
|
)
|
|
}
|
|
|
|
func (ssgs *selectSQLGeneratorSuite) TestGenerate_withCompounds() {
|
|
tse := newTestAppendableExpression("select * from foo", emptyArgs, nil, nil)
|
|
sc := exp.NewSelectClauses().SetFrom(exp.NewColumnListExpression("test")).
|
|
CompoundsAppend(exp.NewCompoundExpression(exp.UnionCompoundType, tse)).
|
|
CompoundsAppend(exp.NewCompoundExpression(exp.IntersectCompoundType, tse))
|
|
|
|
expectedSQL := `SELECT * FROM "test" UNION (select * from foo) INTERSECT (select * from foo)`
|
|
ssgs.assertCases(
|
|
sqlgen.NewSelectSQLGenerator("test", sqlgen.DefaultDialectOptions()),
|
|
selectTestCase{clause: sc, sql: expectedSQL},
|
|
selectTestCase{clause: sc, sql: expectedSQL, isPrepared: true},
|
|
)
|
|
}
|
|
|
|
func (ssgs *selectSQLGeneratorSuite) TestToSelectSQL_withFor() {
|
|
opts := sqlgen.DefaultDialectOptions()
|
|
opts.ForUpdateFragment = []byte(" for update ")
|
|
opts.ForNoKeyUpdateFragment = []byte(" for no key update ")
|
|
opts.ForShareFragment = []byte(" for share ")
|
|
opts.ForKeyShareFragment = []byte(" for key share ")
|
|
opts.OfFragment = []byte("of ")
|
|
opts.NowaitFragment = []byte("nowait")
|
|
opts.SkipLockedFragment = []byte("skip locked")
|
|
|
|
sc := exp.NewSelectClauses().SetFrom(exp.NewColumnListExpression("test"))
|
|
scFnW := sc.SetLock(exp.NewLock(exp.ForNolock, exp.Wait))
|
|
scFnNw := sc.SetLock(exp.NewLock(exp.ForNolock, exp.NoWait))
|
|
scFnSl := sc.SetLock(exp.NewLock(exp.ForNolock, exp.SkipLocked))
|
|
scFnSlOf := sc.SetLock(exp.NewLock(exp.ForNolock, exp.SkipLocked, dbv2.T("my_table")))
|
|
|
|
scFsW := sc.SetLock(exp.NewLock(exp.ForShare, exp.Wait))
|
|
scFsNw := sc.SetLock(exp.NewLock(exp.ForShare, exp.NoWait))
|
|
scFsSl := sc.SetLock(exp.NewLock(exp.ForShare, exp.SkipLocked))
|
|
scFsSlOf := sc.SetLock(exp.NewLock(exp.ForShare, exp.SkipLocked, dbv2.T("my_table")))
|
|
scFsSlOfMulti := sc.SetLock(exp.NewLock(exp.ForShare, exp.SkipLocked, dbv2.T("my_table"), dbv2.T("table2")))
|
|
|
|
scFksW := sc.SetLock(exp.NewLock(exp.ForKeyShare, exp.Wait))
|
|
scFksNw := sc.SetLock(exp.NewLock(exp.ForKeyShare, exp.NoWait))
|
|
scFksSl := sc.SetLock(exp.NewLock(exp.ForKeyShare, exp.SkipLocked))
|
|
|
|
scFuW := sc.SetLock(exp.NewLock(exp.ForUpdate, exp.Wait))
|
|
scFuNw := sc.SetLock(exp.NewLock(exp.ForUpdate, exp.NoWait))
|
|
scFuSl := sc.SetLock(exp.NewLock(exp.ForUpdate, exp.SkipLocked))
|
|
|
|
scFkuW := sc.SetLock(exp.NewLock(exp.ForNoKeyUpdate, exp.Wait))
|
|
scFkuNw := sc.SetLock(exp.NewLock(exp.ForNoKeyUpdate, exp.NoWait))
|
|
scFkuSl := sc.SetLock(exp.NewLock(exp.ForNoKeyUpdate, exp.SkipLocked))
|
|
ssgs.assertCases(
|
|
sqlgen.NewSelectSQLGenerator("test", opts),
|
|
selectTestCase{clause: scFnW, sql: `SELECT * FROM "test"`},
|
|
selectTestCase{clause: scFnW, sql: `SELECT * FROM "test"`, isPrepared: true},
|
|
|
|
selectTestCase{clause: scFnNw, sql: `SELECT * FROM "test"`},
|
|
selectTestCase{clause: scFnNw, sql: `SELECT * FROM "test"`, isPrepared: true},
|
|
|
|
selectTestCase{clause: scFnSl, sql: `SELECT * FROM "test"`},
|
|
selectTestCase{clause: scFnSl, sql: `SELECT * FROM "test"`, isPrepared: true},
|
|
selectTestCase{clause: scFnSlOf, sql: `SELECT * FROM "test"`},
|
|
selectTestCase{clause: scFnSlOf, sql: `SELECT * FROM "test"`, isPrepared: true, args: []interface{}{}},
|
|
|
|
selectTestCase{clause: scFsW, sql: `SELECT * FROM "test" for share `},
|
|
selectTestCase{clause: scFsW, sql: `SELECT * FROM "test" for share `, isPrepared: true},
|
|
|
|
selectTestCase{clause: scFsNw, sql: `SELECT * FROM "test" for share nowait`},
|
|
selectTestCase{clause: scFsNw, sql: `SELECT * FROM "test" for share nowait`, isPrepared: true},
|
|
|
|
selectTestCase{clause: scFsSl, sql: `SELECT * FROM "test" for share skip locked`},
|
|
selectTestCase{clause: scFsSl, sql: `SELECT * FROM "test" for share skip locked`, isPrepared: true},
|
|
|
|
selectTestCase{clause: scFsSlOf, sql: `SELECT * FROM "test" for share of "my_table" skip locked`},
|
|
selectTestCase{clause: scFsSlOf, sql: `SELECT * FROM "test" for share of "my_table" skip locked`, isPrepared: true},
|
|
|
|
selectTestCase{clause: scFsSlOfMulti, sql: `SELECT * FROM "test" for share of "my_table", "table2" skip locked`},
|
|
selectTestCase{clause: scFsSlOfMulti, sql: `SELECT * FROM "test" for share of "my_table", "table2" skip locked`, isPrepared: true},
|
|
|
|
selectTestCase{clause: scFksW, sql: `SELECT * FROM "test" for key share `},
|
|
selectTestCase{clause: scFksW, sql: `SELECT * FROM "test" for key share `, isPrepared: true},
|
|
|
|
selectTestCase{clause: scFksNw, sql: `SELECT * FROM "test" for key share nowait`},
|
|
selectTestCase{clause: scFksNw, sql: `SELECT * FROM "test" for key share nowait`, isPrepared: true},
|
|
|
|
selectTestCase{clause: scFksSl, sql: `SELECT * FROM "test" for key share skip locked`},
|
|
selectTestCase{clause: scFksSl, sql: `SELECT * FROM "test" for key share skip locked`, isPrepared: true},
|
|
|
|
selectTestCase{clause: scFuW, sql: `SELECT * FROM "test" for update `},
|
|
selectTestCase{clause: scFuW, sql: `SELECT * FROM "test" for update `, isPrepared: true},
|
|
|
|
selectTestCase{clause: scFuNw, sql: `SELECT * FROM "test" for update nowait`},
|
|
selectTestCase{clause: scFuNw, sql: `SELECT * FROM "test" for update nowait`, isPrepared: true},
|
|
|
|
selectTestCase{clause: scFuSl, sql: `SELECT * FROM "test" for update skip locked`},
|
|
selectTestCase{clause: scFuSl, sql: `SELECT * FROM "test" for update skip locked`, isPrepared: true},
|
|
|
|
selectTestCase{clause: scFkuW, sql: `SELECT * FROM "test" for no key update `},
|
|
selectTestCase{clause: scFkuW, sql: `SELECT * FROM "test" for no key update `, isPrepared: true},
|
|
|
|
selectTestCase{clause: scFkuNw, sql: `SELECT * FROM "test" for no key update nowait`},
|
|
selectTestCase{clause: scFkuNw, sql: `SELECT * FROM "test" for no key update nowait`, isPrepared: true},
|
|
|
|
selectTestCase{clause: scFkuSl, sql: `SELECT * FROM "test" for no key update skip locked`},
|
|
selectTestCase{clause: scFkuSl, sql: `SELECT * FROM "test" for no key update skip locked`, isPrepared: true},
|
|
)
|
|
}
|
|
|
|
func TestSelectSQLGenerator(t *testing.T) {
|
|
suite.Run(t, new(selectSQLGeneratorSuite))
|
|
}
|