package sqlgen_test import ( "testing" "github.com/doug-martin/goqu/v9/exp" "github.com/doug-martin/goqu/v9/internal/sb" "github.com/doug-martin/goqu/v9/sqlgen" "github.com/stretchr/testify/suite" ) type ( commonSQLTestCase struct { gen func(builder sb.SQLBuilder) sql string isPrepared bool err string args []interface{} } commonSQLGeneratorSuite struct { baseSQLGeneratorSuite } ) func (csgs *commonSQLGeneratorSuite) assertCases(testCases ...commonSQLTestCase) { for _, tc := range testCases { b := sb.NewSQLBuilder(tc.isPrepared) tc.gen(b) switch { case len(tc.err) > 0: csgs.assertErrorSQL(b, tc.err) case tc.isPrepared: csgs.assertPreparedSQL(b, tc.sql, tc.args) default: csgs.assertNotPreparedSQL(b, tc.sql) } } } func (csgs *commonSQLGeneratorSuite) TestReturningSQL() { returningGen := func(csgs sqlgen.CommonSQLGenerator) func(sb.SQLBuilder) { return func(sb sb.SQLBuilder) { csgs.ReturningSQL(sb, exp.NewColumnListExpression("a", "b")) } } returningNoColsGen := func(csgs sqlgen.CommonSQLGenerator) func(sb.SQLBuilder) { return func(sb sb.SQLBuilder) { csgs.ReturningSQL(sb, exp.NewColumnListExpression()) } } returningNilExpGen := func(csgs sqlgen.CommonSQLGenerator) func(sb.SQLBuilder) { return func(sb sb.SQLBuilder) { csgs.ReturningSQL(sb, nil) } } opts := sqlgen.DefaultDialectOptions() opts.SupportsReturn = true csgs1 := sqlgen.NewCommonSQLGenerator("test", opts) opts2 := sqlgen.DefaultDialectOptions() opts2.SupportsReturn = false csgs2 := sqlgen.NewCommonSQLGenerator("test", opts2) csgs.assertCases( commonSQLTestCase{gen: returningGen(csgs1), sql: ` RETURNING "a", "b"`}, commonSQLTestCase{gen: returningGen(csgs1), sql: ` RETURNING "a", "b"`, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: returningNoColsGen(csgs1), sql: ``}, commonSQLTestCase{gen: returningNoColsGen(csgs1), sql: ``, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: returningNilExpGen(csgs1), sql: ``}, commonSQLTestCase{gen: returningNilExpGen(csgs1), sql: ``, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: returningGen(csgs2), err: `goqu: dialect does not support RETURNING clause [dialect=test]`}, commonSQLTestCase{gen: returningGen(csgs2), err: `goqu: dialect does not support RETURNING clause [dialect=test]`}, ) } func (csgs *commonSQLGeneratorSuite) TestFromSQL() { fromGen := func(csgs sqlgen.CommonSQLGenerator) func(sb.SQLBuilder) { return func(sb sb.SQLBuilder) { csgs.FromSQL(sb, exp.NewColumnListExpression("a", "b")) } } fromNoColsGen := func(csgs sqlgen.CommonSQLGenerator) func(sb.SQLBuilder) { return func(sb sb.SQLBuilder) { csgs.FromSQL(sb, exp.NewColumnListExpression()) } } fromNilExpGen := func(csgs sqlgen.CommonSQLGenerator) func(sb.SQLBuilder) { return func(sb sb.SQLBuilder) { csgs.FromSQL(sb, nil) } } csg := sqlgen.NewCommonSQLGenerator("test", sqlgen.DefaultDialectOptions()) opts := sqlgen.DefaultDialectOptions() opts.FromFragment = []byte(" from") csgFromFrag := sqlgen.NewCommonSQLGenerator("test", opts) csgs.assertCases( commonSQLTestCase{gen: fromGen(csg), sql: ` FROM "a", "b"`}, commonSQLTestCase{gen: fromGen(csg), sql: ` FROM "a", "b"`, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: fromNoColsGen(csg), sql: ``}, commonSQLTestCase{gen: fromNoColsGen(csg), sql: ``, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: fromNilExpGen(csg), sql: ``}, commonSQLTestCase{gen: fromNilExpGen(csg), sql: ``, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: fromGen(csgFromFrag), sql: ` from "a", "b"`}, commonSQLTestCase{gen: fromGen(csgFromFrag), sql: ` from "a", "b"`, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: fromNoColsGen(csgFromFrag), sql: ``}, commonSQLTestCase{gen: fromNoColsGen(csgFromFrag), sql: ``, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: fromNilExpGen(csgFromFrag), sql: ``}, commonSQLTestCase{gen: fromNilExpGen(csgFromFrag), sql: ``, isPrepared: true, args: emptyArgs}, ) } func (csgs *commonSQLGeneratorSuite) TestWhereSQL() { whereAndGen := func(csgs sqlgen.CommonSQLGenerator, exps ...exp.Expression) func(sb.SQLBuilder) { return func(sb sb.SQLBuilder) { csgs.WhereSQL(sb, exp.NewExpressionList(exp.AndType, exps...)) } } whereOrGen := func(csgs sqlgen.CommonSQLGenerator, exps ...exp.Expression) func(sb.SQLBuilder) { return func(sb sb.SQLBuilder) { csgs.WhereSQL(sb, exp.NewExpressionList(exp.OrType, exps...)) } } csg := sqlgen.NewCommonSQLGenerator("test", sqlgen.DefaultDialectOptions()) opts := sqlgen.DefaultDialectOptions() opts.WhereFragment = []byte(" where ") csgWhereFrag := sqlgen.NewCommonSQLGenerator("test", opts) w := exp.Ex{"a": "b"} w2 := exp.Ex{"b": "c"} csgs.assertCases( commonSQLTestCase{gen: whereAndGen(csg), sql: ``}, commonSQLTestCase{gen: whereAndGen(csg), sql: ``, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: whereAndGen(csg, w), sql: ` WHERE ("a" = 'b')`}, commonSQLTestCase{gen: whereAndGen(csg, w), sql: ` WHERE ("a" = ?)`, isPrepared: true, args: []interface{}{"b"}}, commonSQLTestCase{gen: whereAndGen(csg, w, w2), sql: ` WHERE (("a" = 'b') AND ("b" = 'c'))`}, commonSQLTestCase{gen: whereAndGen(csg, w, w2), sql: ` WHERE (("a" = ?) AND ("b" = ?))`, isPrepared: true, args: []interface{}{"b", "c"}}, commonSQLTestCase{gen: whereOrGen(csg), sql: ``}, commonSQLTestCase{gen: whereOrGen(csg), sql: ``, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: whereOrGen(csg, w), sql: ` WHERE ("a" = 'b')`}, commonSQLTestCase{gen: whereOrGen(csg, w), sql: ` WHERE ("a" = ?)`, isPrepared: true, args: []interface{}{"b"}}, commonSQLTestCase{gen: whereOrGen(csg, w, w2), sql: ` WHERE (("a" = 'b') OR ("b" = 'c'))`}, commonSQLTestCase{gen: whereOrGen(csg, w, w2), sql: ` WHERE (("a" = ?) OR ("b" = ?))`, isPrepared: true, args: []interface{}{"b", "c"}}, commonSQLTestCase{gen: whereAndGen(csgWhereFrag), sql: ``}, commonSQLTestCase{gen: whereAndGen(csgWhereFrag), sql: ``, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: whereAndGen(csgWhereFrag, w), sql: ` where ("a" = 'b')`}, commonSQLTestCase{gen: whereAndGen(csgWhereFrag, w), sql: ` where ("a" = ?)`, isPrepared: true, args: []interface{}{"b"}}, commonSQLTestCase{gen: whereAndGen(csgWhereFrag, w, w2), sql: ` where (("a" = 'b') AND ("b" = 'c'))`}, commonSQLTestCase{ gen: whereAndGen(csgWhereFrag, w, w2), sql: ` where (("a" = ?) AND ("b" = ?))`, isPrepared: true, args: []interface{}{"b", "c"}, }, commonSQLTestCase{gen: whereOrGen(csgWhereFrag), sql: ``}, commonSQLTestCase{gen: whereOrGen(csgWhereFrag), sql: ``, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: whereOrGen(csgWhereFrag, w), sql: ` where ("a" = 'b')`}, commonSQLTestCase{gen: whereOrGen(csgWhereFrag, w), sql: ` where ("a" = ?)`, isPrepared: true, args: []interface{}{"b"}}, commonSQLTestCase{gen: whereOrGen(csgWhereFrag, w, w2), sql: ` where (("a" = 'b') OR ("b" = 'c'))`}, commonSQLTestCase{ gen: whereOrGen(csgWhereFrag, w, w2), sql: ` where (("a" = ?) OR ("b" = ?))`, isPrepared: true, args: []interface{}{"b", "c"}, }, ) } func (csgs *commonSQLGeneratorSuite) TestOrderSQL() { orderGen := func(csgs sqlgen.CommonSQLGenerator, o ...exp.OrderedExpression) func(sb.SQLBuilder) { return func(sb sb.SQLBuilder) { csgs.OrderSQL(sb, exp.NewOrderedColumnList(o...)) } } csg := sqlgen.NewCommonSQLGenerator("test", sqlgen.DefaultDialectOptions()) opts := sqlgen.DefaultDialectOptions() // override fragments to ensure they are used opts.OrderByFragment = []byte(" order by ") opts.AscFragment = []byte(" asc") opts.DescFragment = []byte(" desc") opts.NullsFirstFragment = []byte(" nulls first") opts.NullsLastFragment = []byte(" nulls last") csgCustom := sqlgen.NewCommonSQLGenerator("test", opts) ident := exp.NewIdentifierExpression("", "", "a") oa := ident.Asc() oanf := ident.Asc().NullsFirst() oanl := ident.Asc().NullsLast() od := ident.Desc() odnf := ident.Desc().NullsFirst() odnl := ident.Desc().NullsLast() csgs.assertCases( commonSQLTestCase{gen: orderGen(csg), sql: ``}, commonSQLTestCase{gen: orderGen(csg), sql: ``, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: orderGen(csg, oa), sql: ` ORDER BY "a" ASC`}, commonSQLTestCase{gen: orderGen(csg, oa), sql: ` ORDER BY "a" ASC`, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: orderGen(csg, oanf), sql: ` ORDER BY "a" ASC NULLS FIRST`}, commonSQLTestCase{gen: orderGen(csg, oanf), sql: ` ORDER BY "a" ASC NULLS FIRST`, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: orderGen(csg, oanl), sql: ` ORDER BY "a" ASC NULLS LAST`}, commonSQLTestCase{gen: orderGen(csg, oanl), sql: ` ORDER BY "a" ASC NULLS LAST`, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: orderGen(csg, od), sql: ` ORDER BY "a" DESC`}, commonSQLTestCase{gen: orderGen(csg, od), sql: ` ORDER BY "a" DESC`, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: orderGen(csg, odnf), sql: ` ORDER BY "a" DESC NULLS FIRST`}, commonSQLTestCase{gen: orderGen(csg, odnf), sql: ` ORDER BY "a" DESC NULLS FIRST`, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: orderGen(csg, odnl), sql: ` ORDER BY "a" DESC NULLS LAST`}, commonSQLTestCase{gen: orderGen(csg, odnl), sql: ` ORDER BY "a" DESC NULLS LAST`, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: orderGen(csg, oa, od), sql: ` ORDER BY "a" ASC, "a" DESC`}, commonSQLTestCase{gen: orderGen(csg, oa, od), sql: ` ORDER BY "a" ASC, "a" DESC`, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: orderGen(csgCustom), sql: ``}, commonSQLTestCase{gen: orderGen(csgCustom), sql: ``, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: orderGen(csgCustom, oa), sql: ` order by "a" asc`}, commonSQLTestCase{gen: orderGen(csgCustom, oa), sql: ` order by "a" asc`, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: orderGen(csgCustom, oanf), sql: ` order by "a" asc nulls first`}, commonSQLTestCase{gen: orderGen(csgCustom, oanf), sql: ` order by "a" asc nulls first`, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: orderGen(csgCustom, oanl), sql: ` order by "a" asc nulls last`}, commonSQLTestCase{gen: orderGen(csgCustom, oanl), sql: ` order by "a" asc nulls last`, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: orderGen(csgCustom, od), sql: ` order by "a" desc`}, commonSQLTestCase{gen: orderGen(csgCustom, od), sql: ` order by "a" desc`, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: orderGen(csgCustom, odnf), sql: ` order by "a" desc nulls first`}, commonSQLTestCase{gen: orderGen(csgCustom, odnf), sql: ` order by "a" desc nulls first`, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: orderGen(csgCustom, odnl), sql: ` order by "a" desc nulls last`}, commonSQLTestCase{gen: orderGen(csgCustom, odnl), sql: ` order by "a" desc nulls last`, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: orderGen(csgCustom, oa, od), sql: ` order by "a" asc, "a" desc`}, commonSQLTestCase{gen: orderGen(csgCustom, oa, od), sql: ` order by "a" asc, "a" desc`, isPrepared: true, args: emptyArgs}, ) } func (csgs *commonSQLGeneratorSuite) TestLimitSQL() { limitGen := func(csgs sqlgen.CommonSQLGenerator, l interface{}) func(sb.SQLBuilder) { return func(sb sb.SQLBuilder) { csgs.LimitSQL(sb, l) } } csg := sqlgen.NewCommonSQLGenerator("test", sqlgen.DefaultDialectOptions()) opts := sqlgen.DefaultDialectOptions() opts.LimitFragment = []byte(" limit ") csgCustom := sqlgen.NewCommonSQLGenerator("test", opts) l := int64(10) la := exp.NewLiteralExpression("ALL") csgs.assertCases( commonSQLTestCase{gen: limitGen(csg, nil), sql: ``}, commonSQLTestCase{gen: limitGen(csg, nil), sql: ``, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: limitGen(csg, l), sql: ` LIMIT 10`}, commonSQLTestCase{gen: limitGen(csg, l), sql: ` LIMIT ?`, isPrepared: true, args: []interface{}{l}}, commonSQLTestCase{gen: limitGen(csg, la), sql: ` LIMIT ALL`}, commonSQLTestCase{gen: limitGen(csg, la), sql: ` LIMIT ALL`, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: limitGen(csgCustom, nil), sql: ``}, commonSQLTestCase{gen: limitGen(csgCustom, nil), sql: ``, isPrepared: true, args: emptyArgs}, commonSQLTestCase{gen: limitGen(csgCustom, l), sql: ` limit 10`}, commonSQLTestCase{gen: limitGen(csgCustom, l), sql: ` limit ?`, isPrepared: true, args: []interface{}{l}}, commonSQLTestCase{gen: limitGen(csgCustom, la), sql: ` limit ALL`}, commonSQLTestCase{gen: limitGen(csgCustom, la), sql: ` limit ALL`, isPrepared: true, args: emptyArgs}, ) } func (csgs *commonSQLGeneratorSuite) TestUpdateExpressionSQL() { updateGen := func(csgs sqlgen.CommonSQLGenerator, ues ...exp.UpdateExpression) func(sb.SQLBuilder) { return func(sb sb.SQLBuilder) { csgs.UpdateExpressionSQL(sb, ues...) } } csg := sqlgen.NewCommonSQLGenerator("test", sqlgen.DefaultDialectOptions()) ue := exp.NewIdentifierExpression("", "", "col").Set("a") ue2 := exp.NewIdentifierExpression("", "", "col2").Set("b") csgs.assertCases( commonSQLTestCase{gen: updateGen(csg), err: sqlgen.ErrNoUpdatedValuesProvided.Error()}, commonSQLTestCase{gen: updateGen(csg), err: sqlgen.ErrNoUpdatedValuesProvided.Error()}, commonSQLTestCase{gen: updateGen(csg, ue), sql: `"col"='a'`}, commonSQLTestCase{gen: updateGen(csg, ue), sql: `"col"=?`, isPrepared: true, args: []interface{}{"a"}}, commonSQLTestCase{gen: updateGen(csg, ue, ue2), sql: `"col"='a',"col2"='b'`}, commonSQLTestCase{gen: updateGen(csg, ue, ue2), sql: `"col"=?,"col2"=?`, isPrepared: true, args: []interface{}{"a", "b"}}, ) } func TestCommonSQLGenerator(t *testing.T) { suite.Run(t, new(commonSQLGeneratorSuite)) }