Files
db/sqlgen/update_sql_generator_test.go
T
whatandClaude 304d553b3c docs: Add CLAUDE.md with codebase guidance
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>
2025-09-27 15:47:28 +08:00

260 lines
9.4 KiB
Go

package sqlgen_test
import (
"testing"
"git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/sqlgen"
"github.com/stretchr/testify/suite"
)
type (
updateTestCase struct {
clause exp.UpdateClauses
sql string
isPrepared bool
args []interface{}
err string
}
updateSQLGeneratorSuite struct {
baseSQLGeneratorSuite
}
)
func (usgs *updateSQLGeneratorSuite) assertCases(usg sqlgen.UpdateSQLGenerator, testCases ...updateTestCase) {
for _, tc := range testCases {
b := sb.NewSQLBuilder(tc.isPrepared)
usg.Generate(b, tc.clause)
switch {
case len(tc.err) > 0:
usgs.assertErrorSQL(b, tc.err)
case tc.isPrepared:
usgs.assertPreparedSQL(b, tc.sql, tc.args)
default:
usgs.assertNotPreparedSQL(b, tc.sql)
}
}
}
func (usgs *updateSQLGeneratorSuite) TestDialect() {
opts := sqlgen.DefaultDialectOptions()
d := sqlgen.NewUpdateSQLGenerator("test", opts)
usgs.Equal("test", d.Dialect())
opts2 := sqlgen.DefaultDialectOptions()
d2 := sqlgen.NewUpdateSQLGenerator("test2", opts2)
usgs.Equal("test2", d2.Dialect())
}
func (usgs *updateSQLGeneratorSuite) TestGenerate_unsupportedFragment() {
opts := sqlgen.DefaultDialectOptions()
opts.UpdateSQLOrder = []sqlgen.SQLFragmentType{sqlgen.InsertBeingSQLFragment}
uc := exp.NewUpdateClauses().
SetTable(exp.NewIdentifierExpression("", "test", "")).
SetSetValues(exp.Record{"a": "b", "b": "c"})
expectedErr := "db: unsupported UPDATE SQL fragment InsertBeingSQLFragment"
usgs.assertCases(
sqlgen.NewUpdateSQLGenerator("test", opts),
updateTestCase{clause: uc, err: expectedErr},
updateTestCase{clause: uc, err: expectedErr, isPrepared: true},
)
}
func (usgs *updateSQLGeneratorSuite) TestGenerate_empty() {
uc := exp.NewUpdateClauses()
usgs.assertCases(
sqlgen.NewUpdateSQLGenerator("test", sqlgen.DefaultDialectOptions()),
updateTestCase{clause: uc, err: sqlgen.ErrNoSourceForUpdate.Error()},
updateTestCase{clause: uc, err: sqlgen.ErrNoSourceForUpdate.Error(), isPrepared: true},
)
}
func (usgs *updateSQLGeneratorSuite) TestGenerate_withBadUpdateValues() {
uc := exp.NewUpdateClauses().
SetTable(exp.NewIdentifierExpression("", "test", "")).
SetSetValues(true)
expectedErr := "db: unsupported update interface type bool"
usgs.assertCases(
sqlgen.NewUpdateSQLGenerator("test", sqlgen.DefaultDialectOptions()),
updateTestCase{clause: uc, err: expectedErr},
updateTestCase{clause: uc, err: expectedErr, isPrepared: true},
)
}
func (usgs *updateSQLGeneratorSuite) TestGenerate_noSetValues() {
uc := exp.NewUpdateClauses().SetTable(exp.NewIdentifierExpression("", "test", ""))
expectedErr := sqlgen.ErrNoSetValuesForUpdate.Error()
usgs.assertCases(
sqlgen.NewUpdateSQLGenerator("test", sqlgen.DefaultDialectOptions()),
updateTestCase{clause: uc, err: expectedErr},
updateTestCase{clause: uc, err: expectedErr, isPrepared: true},
)
}
func (usgs *updateSQLGeneratorSuite) TestGenerate_withFrom() {
uc := exp.NewUpdateClauses().
SetTable(exp.NewIdentifierExpression("", "test", "")).
SetSetValues(exp.Record{"foo": "bar"}).
SetFrom(exp.NewColumnListExpression("other_test"))
ucNullSet := exp.NewUpdateClauses().
SetTable(exp.NewIdentifierExpression("", "test", "")).
SetSetValues(exp.Record{"foo": nil}).
SetFrom(exp.NewColumnListExpression("other_test"))
opts := sqlgen.DefaultDialectOptions()
usgs.assertCases(
sqlgen.NewUpdateSQLGenerator("test", opts),
updateTestCase{clause: uc, sql: `UPDATE "test" SET "foo"='bar' FROM "other_test"`},
updateTestCase{clause: uc, sql: `UPDATE "test" SET "foo"=? FROM "other_test"`, isPrepared: true, args: []interface{}{"bar"}},
updateTestCase{clause: ucNullSet, sql: `UPDATE "test" SET "foo"=NULL FROM "other_test"`},
updateTestCase{clause: ucNullSet, sql: `UPDATE "test" SET "foo"=? FROM "other_test"`, isPrepared: true, args: []interface{}{nil}},
)
opts = sqlgen.DefaultDialectOptions()
opts.UseFromClauseForMultipleUpdateTables = false
usgs.assertCases(
sqlgen.NewUpdateSQLGenerator("test", opts),
updateTestCase{clause: uc, sql: `UPDATE "test","other_test" SET "foo"='bar'`},
updateTestCase{clause: uc, sql: `UPDATE "test","other_test" SET "foo"=?`, isPrepared: true, args: []interface{}{"bar"}},
updateTestCase{clause: ucNullSet, sql: `UPDATE "test","other_test" SET "foo"=NULL`},
updateTestCase{clause: ucNullSet, sql: `UPDATE "test","other_test" SET "foo"=?`, isPrepared: true, args: []interface{}{nil}},
)
opts = sqlgen.DefaultDialectOptions()
opts.SupportsMultipleUpdateTables = false
expectedErr := "db: test dialect does not support multiple tables in UPDATE"
usgs.assertCases(
sqlgen.NewUpdateSQLGenerator("test", opts),
updateTestCase{clause: uc, err: expectedErr},
updateTestCase{clause: uc, err: expectedErr, isPrepared: true},
)
}
func (usgs *updateSQLGeneratorSuite) TestGenerate_withUpdateExpression() {
opts := sqlgen.DefaultDialectOptions()
// make sure the fragments are used
opts.SetFragment = []byte(" set ")
uc := exp.NewUpdateClauses().
SetTable(exp.NewIdentifierExpression("", "test", ""))
ucRecord := uc.SetSetValues(exp.Record{"a": "b", "b": "c"})
ucRecordNullVal := uc.SetSetValues(exp.Record{"a": "b", "b": nil})
ucRecordBoolVals := uc.SetSetValues(exp.Record{"a": true, "b": false})
ucEmptyRecord := uc.SetSetValues(exp.Record{})
usgs.assertCases(
sqlgen.NewUpdateSQLGenerator("test", opts),
updateTestCase{clause: ucRecord, sql: `UPDATE "test" set "a"='b',"b"='c'`},
updateTestCase{clause: ucRecord, sql: `UPDATE "test" set "a"=?,"b"=?`, isPrepared: true, args: []interface{}{"b", "c"}},
updateTestCase{clause: ucRecordNullVal, sql: `UPDATE "test" set "a"='b',"b"=NULL`},
updateTestCase{clause: ucRecordNullVal, sql: `UPDATE "test" set "a"=?,"b"=?`, isPrepared: true, args: []interface{}{"b", nil}},
updateTestCase{clause: ucRecordBoolVals, sql: `UPDATE "test" set "a"=TRUE,"b"=FALSE`},
updateTestCase{clause: ucRecordBoolVals, sql: `UPDATE "test" set "a"=?,"b"=?`, isPrepared: true, args: []interface{}{true, false}},
updateTestCase{clause: ucEmptyRecord, err: sqlgen.ErrNoUpdatedValuesProvided.Error()},
updateTestCase{clause: ucEmptyRecord, err: sqlgen.ErrNoUpdatedValuesProvided.Error(), isPrepared: true},
)
}
func (usgs *updateSQLGeneratorSuite) TestGenerate_withOrder() {
uc := exp.NewUpdateClauses().
SetTable(exp.NewIdentifierExpression("", "test", "")).
SetSetValues(exp.Record{"a": "b", "b": "c"}).
SetOrder(
exp.NewIdentifierExpression("", "", "a").Asc(),
exp.NewIdentifierExpression("", "", "b").Desc(),
)
opts := sqlgen.DefaultDialectOptions()
opts.SupportsOrderByOnUpdate = true
usgs.assertCases(
sqlgen.NewUpdateSQLGenerator("test", opts),
updateTestCase{clause: uc, sql: `UPDATE "test" SET "a"='b',"b"='c' ORDER BY "a" ASC, "b" DESC`},
updateTestCase{
clause: uc,
sql: `UPDATE "test" SET "a"=?,"b"=? ORDER BY "a" ASC, "b" DESC`,
isPrepared: true,
args: []interface{}{"b", "c"},
},
)
opts = sqlgen.DefaultDialectOptions()
opts.SupportsOrderByOnUpdate = false
usgs.assertCases(
sqlgen.NewUpdateSQLGenerator("test", opts),
updateTestCase{clause: uc, sql: `UPDATE "test" SET "a"='b',"b"='c'`},
updateTestCase{clause: uc, sql: `UPDATE "test" SET "a"=?,"b"=?`, isPrepared: true, args: []interface{}{"b", "c"}},
)
}
func (usgs *updateSQLGeneratorSuite) TestGenerate_withLimit() {
uc := exp.NewUpdateClauses().
SetTable(exp.NewIdentifierExpression("", "test", "")).
SetSetValues(exp.Record{"a": "b", "b": "c"}).
SetLimit(10)
opts := sqlgen.DefaultDialectOptions()
opts.SupportsLimitOnUpdate = true
usgs.assertCases(
sqlgen.NewUpdateSQLGenerator("test", opts),
updateTestCase{clause: uc, sql: `UPDATE "test" SET "a"='b',"b"='c' LIMIT 10`},
updateTestCase{clause: uc, sql: `UPDATE "test" SET "a"=?,"b"=? LIMIT ?`, isPrepared: true, args: []interface{}{"b", "c", int64(10)}},
)
opts = sqlgen.DefaultDialectOptions()
opts.SupportsLimitOnUpdate = false
usgs.assertCases(
sqlgen.NewUpdateSQLGenerator("test", opts),
updateTestCase{clause: uc, sql: `UPDATE "test" SET "a"='b',"b"='c'`},
updateTestCase{clause: uc, sql: `UPDATE "test" SET "a"=?,"b"=?`, isPrepared: true, args: []interface{}{"b", "c"}},
)
}
func (usgs *updateSQLGeneratorSuite) TestGenerate_withCommonTables() {
tse := newTestAppendableExpression("select * from foo", emptyArgs, nil, nil)
uc := exp.NewUpdateClauses().
SetTable(exp.NewIdentifierExpression("", "test_cte", "")).
SetSetValues(exp.Record{"a": "b", "b": "c"})
ucCte1 := uc.CommonTablesAppend(exp.NewCommonTableExpression(false, "test_cte", tse))
ucCte2 := uc.CommonTablesAppend(exp.NewCommonTableExpression(true, "test_cte", tse))
usgs.assertCases(
sqlgen.NewUpdateSQLGenerator("test", sqlgen.DefaultDialectOptions()),
updateTestCase{
clause: ucCte1,
sql: `WITH test_cte AS (select * from foo) UPDATE "test_cte" SET "a"='b',"b"='c'`,
},
updateTestCase{
clause: ucCte1,
sql: `WITH test_cte AS (select * from foo) UPDATE "test_cte" SET "a"=?,"b"=?`,
isPrepared: true,
args: []interface{}{"b", "c"},
},
updateTestCase{
clause: ucCte2,
sql: `WITH RECURSIVE test_cte AS (select * from foo) UPDATE "test_cte" SET "a"='b',"b"='c'`,
},
updateTestCase{
clause: ucCte2,
sql: `WITH RECURSIVE test_cte AS (select * from foo) UPDATE "test_cte" SET "a"=?,"b"=?`,
isPrepared: true,
args: []interface{}{"b", "c"},
},
)
}
func TestUpdateSQLGenerator(t *testing.T) {
suite.Run(t, new(updateSQLGeneratorSuite))
}