package sqlbuilder import ( "fmt" "regexp" "strings" "testing" "git.hexq.cn/tiglog/mydb" "github.com/stretchr/testify/assert" ) var ( reInvisibleChars = regexp.MustCompile(`[\s\r\n\t]+`) ) func TestSelect(t *testing.T) { b := &sqlBuilder{t: newTemplateWithUtils(&testTemplate)} assert := assert.New(t) assert.Equal( `SELECT DATE()`, b.Select(mydb.Func("DATE")).String(), ) assert.Equal( `SELECT DATE() FOR UPDATE`, b.Select(mydb.Func("DATE")).Amend(func(query string) string { return query + " FOR UPDATE" }).String(), ) assert.Equal( `SELECT * FROM "artist"`, b.SelectFrom("artist").String(), ) assert.Equal( `SELECT DISTINCT "bcolor" FROM "artist"`, b.Select().Distinct("bcolor").From("artist").String(), ) assert.Equal( `SELECT DISTINCT * FROM "artist"`, b.Select().Distinct().From("artist").String(), ) assert.Equal( `SELECT DISTINCT ON("col1"), "col2" FROM "artist"`, b.Select().Distinct(mydb.Raw(`ON("col1")`), "col2").From("artist").String(), ) assert.Equal( `SELECT DISTINCT ON("col1") AS col1, "col2" FROM "artist"`, b.Select().Distinct(mydb.Raw(`ON("col1") AS col1`)).Distinct("col2").From("artist").String(), ) assert.Equal( `SELECT DISTINCT ON("col1") AS col1, "col2", "col3", "col4", "col5" FROM "artist"`, b.Select().Distinct(mydb.Raw(`ON("col1") AS col1`)).Columns("col2", "col3").Distinct("col4", "col5").From("artist").String(), ) assert.Equal( `SELECT DISTINCT ON(SELECT foo FROM bar) col1, "col2", "col3", "col4", "col5" FROM "artist"`, b.Select().Distinct(mydb.Raw(`ON(?) col1`, mydb.Raw(`SELECT foo FROM bar`))).Columns("col2", "col3").Distinct("col4", "col5").From("artist").String(), ) { q0 := b.Select("foo").From("bar") assert.Equal( `SELECT DISTINCT ON (SELECT "foo" FROM "bar") col1, "col2", "col3", "col4", "col5" FROM "artist"`, b.Select().Distinct(mydb.Raw(`ON ? col1`, q0)).Columns("col2", "col3").Distinct("col4", "col5").From("artist").String(), ) } assert.Equal( `SELECT DISTINCT ON (SELECT foo FROM bar, SELECT baz from qux) col1, "col2", "col3", "col4", "col5" FROM "artist"`, b.Select().Distinct(mydb.Raw(`ON ? col1`, []interface{}{mydb.Raw(`SELECT foo FROM bar`), mydb.Raw(`SELECT baz from qux`)})).Columns("col2", "col3").Distinct("col4", "col5").From("artist").String(), ) { q := b.Select(). Distinct(mydb.Raw(`ON ? col1`, []*mydb.RawExpr{mydb.Raw(`SELECT foo FROM bar WHERE id = ?`, 1), mydb.Raw(`SELECT baz from qux WHERE id = 2`)})). Columns("col2", "col3"). Distinct("col4", "col5").From("artist"). Where("id", 3) assert.Equal( `SELECT DISTINCT ON (SELECT foo FROM bar WHERE id = $1, SELECT baz from qux WHERE id = 2) col1, "col2", "col3", "col4", "col5" FROM "artist" WHERE ("id" = $2)`, q.String(), ) assert.Equal( []interface{}{1, 3}, q.Arguments(), ) } { rawCase := mydb.Raw("CASE WHEN id IN ? THEN 0 ELSE 1 END", []int{1000, 2000}) sel := b.SelectFrom("artist").OrderBy(rawCase) assert.Equal( `SELECT * FROM "artist" ORDER BY CASE WHEN id IN ($1, $2) THEN 0 ELSE 1 END`, sel.String(), ) assert.Equal( []interface{}{1000, 2000}, sel.Arguments(), ) } { rawCase := mydb.Raw("CASE WHEN id IN ? THEN 0 ELSE 1 END", []int{1000}) sel := b.SelectFrom("artist").OrderBy(rawCase) assert.Equal( `SELECT * FROM "artist" ORDER BY CASE WHEN id IN ($1) THEN 0 ELSE 1 END`, sel.String(), ) assert.Equal( []interface{}{1000}, sel.Arguments(), ) } { rawCase := mydb.Raw("CASE WHEN id IN ? THEN 0 ELSE 1 END", []int{}) sel := b.SelectFrom("artist").OrderBy(rawCase) assert.Equal( `SELECT * FROM "artist" ORDER BY CASE WHEN id IN (NULL) THEN 0 ELSE 1 END`, sel.String(), ) assert.Equal( []interface{}(nil), sel.Arguments(), ) } { rawCase := mydb.Raw("CASE WHEN id IN (NULL) THEN 0 ELSE 1 END") sel := b.SelectFrom("artist").OrderBy(rawCase) assert.Equal( `SELECT * FROM "artist" ORDER BY CASE WHEN id IN (NULL) THEN 0 ELSE 1 END`, sel.String(), ) assert.Equal( []interface{}(nil), rawCase.Arguments(), ) } { rawCase := mydb.Raw("CASE WHEN id IN (?, ?) THEN 0 ELSE 1 END", 1000, 2000) sel := b.SelectFrom("artist").OrderBy(rawCase) assert.Equal( `SELECT * FROM "artist" ORDER BY CASE WHEN id IN ($1, $2) THEN 0 ELSE 1 END`, sel.String(), ) assert.Equal( []interface{}{1000, 2000}, rawCase.Arguments(), ) } { sel := b.Select(mydb.Func("DISTINCT", "name")).From("artist") assert.Equal( `SELECT DISTINCT($1) FROM "artist"`, sel.String(), ) assert.Equal( []interface{}{"name"}, sel.Arguments(), ) } assert.Equal( `SELECT * FROM "artist" WHERE (1 = $1)`, b.Select().From("artist").Where(mydb.Cond{1: 1}).String(), ) assert.Equal( `SELECT * FROM "artist" WHERE (1 = ANY($1))`, b.Select().From("artist").Where(mydb.Cond{1: mydb.Func("ANY", "name")}).String(), ) assert.Equal( `SELECT * FROM "artist" WHERE (1 = ANY(column))`, b.Select().From("artist").Where(mydb.Cond{1: mydb.Func("ANY", mydb.Raw("column"))}).String(), ) { q := b.Select().From("artist").Where(mydb.Cond{"id NOT IN": []int{0, -1}}) assert.Equal( `SELECT * FROM "artist" WHERE ("id" NOT IN ($1, $2))`, q.String(), ) assert.Equal( []interface{}{0, -1}, q.Arguments(), ) } { q := b.Select().From("artist").Where(mydb.Cond{"id NOT IN": []int{-1}}) assert.Equal( `SELECT * FROM "artist" WHERE ("id" NOT IN ($1))`, q.String(), ) assert.Equal( []interface{}{-1}, q.Arguments(), ) } assert.Equal( `SELECT * FROM "artist" WHERE ("id" IN ($1, $2))`, b.Select().From("artist").Where(mydb.Cond{"id IN": []int{0, -1}}).String(), ) assert.Equal( `SELECT * FROM "artist" WHERE (("id" = $1 OR "id" = $2 OR "id" = $3))`, b.Select().From("artist").Where( mydb.Or( mydb.Cond{"id": 1}, mydb.Cond{"id": 2}, mydb.Cond{"id": 3}, ), ).String(), ) { q := b.Select().From("artist").Where( mydb.Or( mydb.And(mydb.Cond{"a": 1}, mydb.Cond{"b": 2}, mydb.Cond{"c": 3}), mydb.And(mydb.Cond{"d": 1}, mydb.Cond{"e": 2}, mydb.Cond{"f": 3}), ), ) assert.Equal( `SELECT * FROM "artist" WHERE ((("a" = $1 AND "b" = $2 AND "c" = $3) OR ("d" = $4 AND "e" = $5 AND "f" = $6)))`, q.String(), ) assert.Equal( []interface{}{1, 2, 3, 1, 2, 3}, q.Arguments(), ) } { q := b.Select().From("artist").Where( mydb.Or( mydb.And(mydb.Cond{"a": 1, "b": 2, "c": 3}), mydb.And(mydb.Cond{"f": 6, "e": 5, "d": 4}), ), ) assert.Equal( `SELECT * FROM "artist" WHERE ((("a" = $1 AND "b" = $2 AND "c" = $3) OR ("d" = $4 AND "e" = $5 AND "f" = $6)))`, q.String(), ) assert.Equal( []interface{}{1, 2, 3, 4, 5, 6}, q.Arguments(), ) } assert.Equal( `SELECT * FROM "artist" WHERE ((("id" = $1 OR "id" = $2 OR "id" IS NULL) OR ("name" = $3 OR "name" = $4)))`, b.Select().From("artist").Where( mydb.Or( mydb.Or( mydb.Cond{"id": 1}, mydb.Cond{"id": 2}, mydb.Cond{"id IS": nil}, ), mydb.Or( mydb.Cond{"name": "John"}, mydb.Cond{"name": "Peter"}, ), ), ).String(), ) assert.Equal( `SELECT * FROM "artist" WHERE ((("id" = $1 OR "id" = $2 OR "id" = $3 OR "id" = $4) AND ("name" = $5 AND "last_name" = $6) AND "age" > $7))`, b.Select().From("artist").Where( mydb.And( mydb.Or( mydb.Cond{"id": 1}, mydb.Cond{"id": 2}, mydb.Cond{"id": 3}, ).Or( mydb.Cond{"id": 4}, ), mydb.Or(), mydb.And( mydb.Cond{"name": "John"}, mydb.Cond{"last_name": "Smith"}, ), mydb.And(), ).And( mydb.Cond{"age >": "20"}, ), ).String(), ) assert.Equal( `SELECT * FROM "artist"`, b.Select().From("artist").String(), ) assert.Equal( `SELECT * FROM "artist" ORDER BY "name" DESC`, b.Select().From("artist").OrderBy("name DESC").String(), ) { sel := b.Select().From("artist").OrderBy(mydb.Raw("id = ?", 1), "name DESC") assert.Equal( `SELECT * FROM "artist" ORDER BY id = $1 , "name" DESC`, sel.String(), ) assert.Equal( []interface{}{1}, sel.Arguments(), ) } { sel := b.Select().From("artist").OrderBy(mydb.Func("RAND")) assert.Equal( `SELECT * FROM "artist" ORDER BY RAND()`, sel.String(), ) assert.Equal( []interface{}(nil), sel.Arguments(), ) } assert.Equal( `SELECT * FROM "artist" ORDER BY RAND()`, b.Select().From("artist").OrderBy(mydb.Raw("RAND()")).String(), ) assert.Equal( `SELECT * FROM "artist" ORDER BY "name" DESC`, b.Select().From("artist").OrderBy("-name").String(), ) assert.Equal( `SELECT * FROM "artist" ORDER BY "name" ASC`, b.Select().From("artist").OrderBy("name").String(), ) assert.Equal( `SELECT * FROM "artist" ORDER BY "name" ASC`, b.Select().From("artist").OrderBy("name ASC").String(), ) assert.Equal( `SELECT * FROM "artist" OFFSET 5`, b.Select().From("artist").Limit(-1).Offset(5).String(), ) assert.Equal( `SELECT * FROM "artist" LIMIT 1 OFFSET 5`, b.Select().From("artist").Limit(1).Offset(5).String(), ) assert.Equal( `SELECT "id" FROM "artist"`, b.Select("id").From("artist").String(), ) assert.Equal( `SELECT "id", "name" FROM "artist"`, b.Select("id", "name").From("artist").String(), ) assert.Equal( `SELECT * FROM "artist" WHERE ("name" = $1)`, b.SelectFrom("artist").Where("name", "Haruki").String(), ) assert.Equal( `SELECT * FROM "artist" WHERE (name LIKE $1)`, b.SelectFrom("artist").Where("name LIKE ?", `%F%`).String(), ) assert.Equal( `SELECT "id" FROM "artist" WHERE (name LIKE $1 OR name LIKE $2)`, b.Select("id").From("artist").Where(`name LIKE ? OR name LIKE ?`, `%Miya%`, `F%`).String(), ) assert.Equal( `SELECT * FROM "artist" WHERE ("id" > $1)`, b.SelectFrom("artist").Where("id >", 2).String(), ) assert.Equal( `SELECT * FROM "artist" WHERE (id <= 2 AND name != $1)`, b.SelectFrom("artist").Where("id <= 2 AND name != ?", "A").String(), ) assert.Equal( `SELECT * FROM "artist" WHERE ("id" IN ($1, $2, $3, $4))`, b.SelectFrom("artist").Where("id IN", []int{1, 9, 8, 7}).String(), ) assert.Equal( `SELECT * FROM "artist" WHERE (id IN ($1, $2, $3, $4) AND foo = $5 AND bar IN ($6, $7, $8))`, b.SelectFrom("artist").Where("id IN ? AND foo = ? AND bar IN ?", []int{1, 9, 8, 7}, 28, []int{1, 2, 3}).String(), ) assert.Equal( `SELECT * FROM "artist" WHERE (name IS NOT NULL)`, b.SelectFrom("artist").Where("name IS NOT NULL").String(), ) assert.Equal( `SELECT * FROM "artist" AS "a", "publication" AS "p" WHERE (p.author_id = a.id) LIMIT 1`, b.Select().From("artist a", "publication as p").Where("p.author_id = a.id").Limit(1).String(), ) assert.Equal( `SELECT "id" FROM "artist" NATURAL JOIN "publication"`, b.Select("id").From("artist").Join("publication").String(), ) assert.Equal( `SELECT * FROM "artist" AS "a" JOIN "publication" AS "p" ON (p.author_id = a.id) LIMIT 1`, b.SelectFrom("artist a").Join("publication p").On("p.author_id = a.id").Limit(1).String(), ) assert.Equal( `SELECT * FROM "artist" AS "a" JOIN "publication" AS "p" ON (p.author_id = a.id) WHERE ("a"."id" = $1) LIMIT 1`, b.SelectFrom("artist a").Join("publication p").On("p.author_id = a.id").Where("a.id", 2).Limit(1).String(), ) assert.Equal( `SELECT * FROM "artist" JOIN "publication" AS "p" ON (p.author_id = a.id) WHERE (a.id = 2) LIMIT 1`, b.SelectFrom("artist").Join("publication p").On("p.author_id = a.id").Where("a.id = 2").Limit(1).String(), ) assert.Equal( `SELECT * FROM "artist" AS "a" JOIN "publication" AS "p" ON (p.title LIKE $1 OR p.title LIKE $2) WHERE (a.id = $3) LIMIT 1`, b.SelectFrom("artist a").Join("publication p").On("p.title LIKE ? OR p.title LIKE ?", "%Totoro%", "%Robot%").Where("a.id = ?", 2).Limit(1).String(), ) assert.Equal( `SELECT * FROM "artist" AS "a" JOIN "publication" AS "p" ON (p.title LIKE $1 OR p.title LIKE $2) WHERE (a.id = $3 AND a.sub_id = $4) LIMIT 1`, b.SelectFrom("artist a").Join("publication p").On("p.title LIKE ? OR p.title LIKE ?", "%Totoro%", "%Robot%").Where("a.id = ?", 2).Where("a.sub_id = ?", 3).Limit(1).String(), ) assert.Equal( `SELECT * FROM "artist" AS "a" JOIN "publication" AS "p" ON (p.title LIKE $1 OR p.title LIKE $2) WHERE (a.id = $3 AND a.id = $4) LIMIT 1`, b.SelectFrom("artist a").Join("publication p").On("p.title LIKE ? OR p.title LIKE ?", "%Totoro%", "%Robot%").Where("a.id = ?", 2).And("a.id = ?", 3).Limit(1).String(), ) assert.Equal( `SELECT * FROM "artist" AS "a" LEFT JOIN "publication" AS "p1" ON (p1.id = a.id) RIGHT JOIN "publication" AS "p2" ON (p2.id = a.id)`, b.SelectFrom("artist a"). LeftJoin("publication p1").On("p1.id = a.id"). RightJoin("publication p2").On("p2.id = a.id"). String(), ) assert.Equal( `SELECT * FROM "artist" CROSS JOIN "publication"`, b.SelectFrom("artist").CrossJoin("publication").String(), ) assert.Equal( `SELECT * FROM "artist" JOIN "publication" USING ("id")`, b.SelectFrom("artist").Join("publication").Using("id").String(), ) assert.Equal( `SELECT * FROM "artist" WHERE ("id" IS NULL)`, b.SelectFrom("artist").Where(mydb.Cond{"id": nil}).String(), ) assert.Equal( `SELECT * FROM "artist" WHERE ("id" IN (NULL))`, b.SelectFrom("artist").Where(mydb.Cond{"id": []int64{}}).String(), ) assert.Equal( `SELECT * FROM "artist" WHERE ("id" IN ($1))`, b.SelectFrom("artist").Where(mydb.Cond{"id": []int64{0}}).String(), ) assert.Equal( `SELECT COUNT(*) AS total FROM "user" AS "u" JOIN (SELECT DISTINCT user_id FROM user_profile) AS up ON (u.id = up.user_id)`, b.Select(mydb.Raw(`COUNT(*) AS total`)).From("user u").Join(mydb.Raw("(SELECT DISTINCT user_id FROM user_profile) AS up")).On("u.id = up.user_id").String(), ) { q0 := b.Select("user_id").Distinct().From("user_profile") assert.Equal( `SELECT COUNT(*) AS total FROM "user" AS "u" JOIN (SELECT DISTINCT "user_id" FROM "user_profile") AS up ON (u.id = up.user_id)`, b.Select(mydb.Raw(`COUNT(*) AS total`)).From("user u").Join(mydb.Raw("? AS up", q0)).On("u.id = up.user_id").String(), ) } { q0 := b.Select("user_id").Distinct().From("user_profile").Where("t", []int{1, 2, 4, 5}) assert.Equal( []interface{}{1, 2, 4, 5}, q0.Arguments(), ) q1 := b.Select(mydb.Raw(`COUNT(*) AS total`)).From("user u").Join(mydb.Raw("? AS up", q0)).On("u.id = up.user_id AND foo = ?", 8) assert.Equal( `SELECT COUNT(*) AS total FROM "user" AS "u" JOIN (SELECT DISTINCT "user_id" FROM "user_profile" WHERE ("t" IN ($1, $2, $3, $4))) AS up ON (u.id = up.user_id AND foo = $5)`, q1.String(), ) assert.Equal( []interface{}{1, 2, 4, 5, 8}, q1.Arguments(), ) } assert.Equal( `SELECT DATE()`, b.Select(mydb.Raw("DATE()")).String(), ) { sel := b.Select(mydb.Raw("CONCAT(?, ?)", "foo", "bar")) assert.Equal( `SELECT CONCAT($1, $2)`, sel.String(), ) assert.Equal( []interface{}{"foo", "bar"}, sel.Arguments(), ) } { sel := b.SelectFrom("foo").Where(mydb.Cond{"bar": mydb.Raw("1")}) assert.Equal( `SELECT * FROM "foo" WHERE ("bar" = 1)`, sel.String(), ) assert.Equal( []interface{}(nil), sel.Arguments(), ) } { sel := b.SelectFrom("foo").Where(mydb.Cond{mydb.Raw("1"): 1}) assert.Equal( `SELECT * FROM "foo" WHERE (1 = $1)`, sel.String(), ) assert.Equal( []interface{}{1}, sel.Arguments(), ) } { sel := b.SelectFrom("foo").Where(mydb.Cond{mydb.Raw("1"): mydb.Raw("1")}) assert.Equal( `SELECT * FROM "foo" WHERE (1 = 1)`, sel.String(), ) assert.Equal( []interface{}(nil), sel.Arguments(), ) } { sel := b.SelectFrom("foo").Where(mydb.Raw("1 = 1")) assert.Equal( `SELECT * FROM "foo" WHERE (1 = 1)`, sel.String(), ) assert.Equal( []interface{}(nil), sel.Arguments(), ) } { sel := b.SelectFrom("foo").Where(mydb.Cond{"bar": 1}, mydb.Cond{"baz": mydb.Raw("CONCAT(?, ?)", "foo", "bar")}) assert.Equal( `SELECT * FROM "foo" WHERE ("bar" = $1 AND "baz" = CONCAT($2, $3))`, sel.String(), ) assert.Equal( []interface{}{1, "foo", "bar"}, sel.Arguments(), ) } { sel := b.SelectFrom("foo").Where(mydb.Cond{"bar": 1}, mydb.Raw("? = ANY(col)", "name")) assert.Equal( `SELECT * FROM "foo" WHERE ("bar" = $1 AND $2 = ANY(col))`, sel.String(), ) assert.Equal( []interface{}{1, "name"}, sel.Arguments(), ) } { sel := b.SelectFrom("foo").Where(mydb.Cond{"bar": 1}, mydb.Cond{"name": mydb.Raw("ANY(col)")}) assert.Equal( `SELECT * FROM "foo" WHERE ("bar" = $1 AND "name" = ANY(col))`, sel.String(), ) assert.Equal( []interface{}{1}, sel.Arguments(), ) } { sel := b.SelectFrom("foo").Where(mydb.Cond{"bar": 1}, mydb.Cond{mydb.Raw("CONCAT(?, ?)", "a", "b"): mydb.Raw("ANY(col)")}) assert.Equal( `SELECT * FROM "foo" WHERE ("bar" = $1 AND CONCAT($2, $3) = ANY(col))`, sel.String(), ) assert.Equal( []interface{}{1, "a", "b"}, sel.Arguments(), ) } { sel := b.SelectFrom("foo").Where("bar", 2).And(mydb.Cond{"baz": 1}) assert.Equal( `SELECT * FROM "foo" WHERE ("bar" = $1 AND "baz" = $2)`, sel.String(), ) assert.Equal( []interface{}{2, 1}, sel.Arguments(), ) } { sel := b.SelectFrom("foo").And(mydb.Cond{"bar": 1}) assert.Equal( `SELECT * FROM "foo" WHERE ("bar" = $1)`, sel.String(), ) assert.Equal( []interface{}{1}, sel.Arguments(), ) } { sel := b.SelectFrom("foo").Where("bar", 2).And(mydb.Cond{"baz": 1}) assert.Equal( `SELECT * FROM "foo" WHERE ("bar" = $1 AND "baz" = $2)`, sel.String(), ) assert.Equal( []interface{}{2, 1}, sel.Arguments(), ) } { sel := b.SelectFrom("foo").Where("bar", 2).Where(mydb.Cond{"baz": 1}) assert.Equal( `SELECT * FROM "foo" WHERE ("bar" = $1 AND "baz" = $2)`, sel.String(), ) assert.Equal( []interface{}{2, 1}, sel.Arguments(), ) } { sel := b.SelectFrom("foo").Where(mydb.Raw("bar->'baz' = ?", true)) assert.Equal( `SELECT * FROM "foo" WHERE (bar->'baz' = $1)`, sel.String(), ) assert.Equal( []interface{}{true}, sel.Arguments(), ) } { sel := b.SelectFrom("foo").Where(mydb.Cond{}).And(mydb.Cond{}) assert.Equal( `SELECT * FROM "foo"`, sel.String(), ) assert.Equal( []interface{}(nil), sel.Arguments(), ) } { sel := b.SelectFrom("foo").Where("bar = 1").And(mydb.Or( mydb.Raw("fieldA ILIKE ?", `%a%`), mydb.Raw("fieldB ILIKE ?", `%b%`), )) assert.Equal( `SELECT * FROM "foo" WHERE (bar = 1 AND (fieldA ILIKE $1 OR fieldB ILIKE $2))`, sel.String(), ) assert.Equal( []interface{}{`%a%`, `%b%`}, sel.Arguments(), ) } { sel := b.SelectFrom("foo").Where("group_id", 1).And("user_id", 2) assert.Equal( `SELECT * FROM "foo" WHERE ("group_id" = $1 AND "user_id" = $2)`, sel.String(), ) assert.Equal( []interface{}{1, 2}, sel.Arguments(), ) } { s := `SUM(CASE WHEN foo in ? THEN 1 ELSE 0 END) AS _sum` sel := b.Select("c1").Columns(mydb.Raw(s, []int{5, 4, 3, 2})).From("foo").Where("bar = ?", 1) assert.Equal( `SELECT "c1", SUM(CASE WHEN foo in ($1, $2, $3, $4) THEN 1 ELSE 0 END) AS _sum FROM "foo" WHERE (bar = $5)`, sel.String(), ) assert.Equal( []interface{}{5, 4, 3, 2, 1}, sel.Arguments(), ) } { s := `SUM(CASE WHEN foo in ? THEN 1 ELSE 0 END) AS _sum` sel := b.Select("c1").Columns(mydb.Raw(s, []int{5, 4, 3, 2})).From("foo").Where("bar = ?", 1) sel2 := b.SelectFrom(sel).As("subquery").Where(mydb.Cond{"foo": "bar"}).OrderBy("subquery.seq") assert.Equal( `SELECT * FROM (SELECT "c1", SUM(CASE WHEN foo in ($1, $2, $3, $4) THEN 1 ELSE 0 END) AS _sum FROM "foo" WHERE (bar = $5)) AS "subquery" WHERE ("foo" = $6) ORDER BY "subquery"."seq" ASC`, sel2.String(), ) assert.Equal( []interface{}{5, 4, 3, 2, 1, "bar"}, sel2.Arguments(), ) } { series := b.Select( mydb.Raw("start + interval ? - interval '1s' AS end", "1 day"), ).From( b.Select( mydb.Raw("generate_series(?::timestamp, ?::timestamp, ?::interval) AS start", 1, 2, 3), ), ).As("series") assert.Equal( []interface{}{"1 day", 1, 2, 3}, series.Arguments(), ) distinct := b.Select().Distinct( mydb.Raw(`ON(dt.email) SUBSTRING(email,(POSITION('@' in email) + 1),252) AS email_domain`), "dt.event_type AS event_type", mydb.Raw("count(dt.*) AS count"), "intervals.start AS start", "intervals.end AS start", ).From("email_events AS dt"). RightJoin("intervals").On("dt.ts BETWEEN intervals.stast AND intervals.END AND dt.hub_id = ? AND dt.object_id = ?", 67, 68). GroupBy("email_domain", "event_type", "start"). OrderBy("email", "start", "event_type") sq, args := Preprocess( `WITH intervals AS ? ?`, []interface{}{ series, distinct, }, ) assert.Equal( stripWhitespace(` WITH intervals AS (SELECT start + interval ? - interval '1s' AS end FROM (SELECT generate_series(?::timestamp, ?::timestamp, ?::interval) AS start) AS "series") (SELECT DISTINCT ON(dt.email) SUBSTRING(email,(POSITION('@' in email) + 1),252) AS email_domain, "dt"."event_type" AS "event_type", count(dt.*) AS count, "intervals"."start" AS "start", "intervals"."end" AS "start" FROM "email_events" AS "dt" RIGHT JOIN "intervals" ON (dt.ts BETWEEN intervals.stast AND intervals.END AND dt.hub_id = ? AND dt.object_id = ?) GROUP BY "email_domain", "event_type", "start" ORDER BY "email" ASC, "start" ASC, "event_type" ASC)`), stripWhitespace(sq), ) assert.Equal( []interface{}{"1 day", 1, 2, 3, 67, 68}, args, ) } { sq := b. Select("user_id"). From("user_access"). Where(mydb.Cond{"hub_id": 3}) // Don't reassign _ = sq.And(mydb.Cond{"role": []int{1, 2}}) assert.Equal( `SELECT "user_id" FROM "user_access" WHERE ("hub_id" = $1)`, sq.String(), ) assert.Equal( []interface{}{3}, sq.Arguments(), ) // Reassign sq = sq.And(mydb.Cond{"role": []int{1, 2}}) assert.Equal( `SELECT "user_id" FROM "user_access" WHERE ("hub_id" = $1 AND "role" IN ($2, $3))`, sq.String(), ) assert.Equal( []interface{}{3, 1, 2}, sq.Arguments(), ) cond := mydb.Or( mydb.Raw("a.id IN ?", sq), ) cond = cond.Or(mydb.Cond{"ml.mailing_list_id": []int{4, 5, 6}}) sel := b. Select(mydb.Raw("DISTINCT ON(a.id) a.id"), mydb.Raw("COALESCE(NULLIF(ml.name,''), a.name) as name"), "a.email"). From("mailing_list_recipients ml"). FullJoin("accounts a").On("a.id = ml.user_id"). Where(cond) search := "word" sel = sel.And(mydb.Or( mydb.Raw("COALESCE(NULLIF(ml.name,''), a.name) ILIKE ?", fmt.Sprintf("%%%s%%", search)), mydb.Cond{"a.email ILIKE": fmt.Sprintf("%%%s%%", search)}, )) assert.Equal( `SELECT DISTINCT ON(a.id) a.id, COALESCE(NULLIF(ml.name,''), a.name) as name, "a"."email" FROM "mailing_list_recipients" AS "ml" FULL JOIN "accounts" AS "a" ON (a.id = ml.user_id) WHERE ((a.id IN (SELECT "user_id" FROM "user_access" WHERE ("hub_id" = $1 AND "role" IN ($2, $3))) OR "ml"."mailing_list_id" IN ($4, $5, $6)) AND (COALESCE(NULLIF(ml.name,''), a.name) ILIKE $7 OR "a"."email" ILIKE $8))`, sel.String(), ) assert.Equal( []interface{}{3, 1, 2, 4, 5, 6, `%word%`, `%word%`}, sel.Arguments(), ) { sel := b.Select(mydb.Func("FOO", "A", "B", 1)).From("accounts").Where(mydb.Cond{"time": mydb.Func("FUNCTION", "20170103", "YYYYMMDD", 1, "E")}) assert.Equal( `SELECT FOO($1, $2, $3) FROM "accounts" WHERE ("time" = FUNCTION($4, $5, $6, $7))`, sel.String(), ) assert.Equal( []interface{}{"A", "B", 1, "20170103", "YYYYMMDD", 1, "E"}, sel.Arguments(), ) } { sel := b.Select(mydb.Func("FOO", "A", "B", 1)).From("accounts").Where(mydb.Cond{mydb.Func("FUNCTION", "20170103", "YYYYMMDD", 1, "E"): mydb.Func("FOO", 1)}) assert.Equal( `SELECT FOO($1, $2, $3) FROM "accounts" WHERE (FUNCTION($4, $5, $6, $7) = FOO($8))`, sel.String(), ) assert.Equal( []interface{}{"A", "B", 1, "20170103", "YYYYMMDD", 1, "E", 1}, sel.Arguments(), ) } } } func TestInsert(t *testing.T) { b := &sqlBuilder{t: newTemplateWithUtils(&testTemplate)} assert := assert.New(t) assert.Equal( `INSERT INTO "artist" VALUES ($1, $2), ($3, $4), ($5, $6)`, b.InsertInto("artist"). Values(10, "Ryuichi Sakamoto"). Values(11, "Alondra de la Parra"). Values(12, "Haruki Murakami"). String(), ) assert.Equal( `INSERT INTO "artist" ("id", "name") VALUES ($1, $2)`, b.InsertInto("artist").Values(map[string]string{"id": "12", "name": "Chavela Vargas"}).String(), ) assert.Equal( `INSERT INTO "artist" ("id", "name") VALUES ($1, $2) RETURNING "id"`, b.InsertInto("artist").Values(map[string]string{"id": "12", "name": "Chavela Vargas"}).Returning("id").String(), ) assert.Equal( `INSERT INTO "artist" ("id", "name") VALUES ($1, $2) RETURNING "id"`, b.InsertInto("artist").Values(map[string]string{"id": "12", "name": "Chavela Vargas"}).Amend(func(query string) string { return query + ` RETURNING "id"` }).String(), ) assert.Equal( `INSERT INTO "artist" ("id", "name") VALUES ($1, $2)`, b.InsertInto("artist").Values(map[string]interface{}{"name": "Chavela Vargas", "id": 12}).String(), ) assert.Equal( `INSERT INTO "artist" ("id", "name") VALUES ($1, $2)`, b.InsertInto("artist").Values(struct { ID int `db:"id"` Name string `db:"name"` }{12, "Chavela Vargas"}).String(), ) { type artistStruct struct { ID int `db:"id,omitempty"` Name string `db:"name,omitempty"` } assert.Equal( `INSERT INTO "artist" ("id", "name") VALUES ($1, $2), ($3, $4), ($5, $6)`, b.InsertInto("artist"). Values(artistStruct{12, "Chavela Vargas"}). Values(artistStruct{13, "Alondra de la Parra"}). Values(artistStruct{14, "Haruki Murakami"}). String(), ) } { type artistStruct struct { ID int `db:"id,omitempty"` Name string `db:"name,omitempty"` } q := b.InsertInto("artist"). Values(artistStruct{0, ""}). Values(artistStruct{12, "Chavela Vargas"}). Values(artistStruct{0, "Alondra de la Parra"}). Values(artistStruct{14, ""}). Values(artistStruct{0, ""}) assert.Equal( `INSERT INTO "artist" ("id", "name") VALUES (DEFAULT, DEFAULT), ($1, $2), (DEFAULT, $3), ($4, DEFAULT), (DEFAULT, DEFAULT)`, q.String(), ) assert.Equal( []interface{}{12, "Chavela Vargas", "Alondra de la Parra", 14}, q.Arguments(), ) } { type artistStruct struct { ID int `db:"id,omitempty"` Name string `db:"name,omitempty"` } assert.Equal( `INSERT INTO "artist" ("name") VALUES ($1)`, b.InsertInto("artist"). Values(artistStruct{Name: "Chavela Vargas"}). String(), ) assert.Equal( `INSERT INTO "artist" ("id") VALUES ($1)`, b.InsertInto("artist"). Values(artistStruct{ID: 1}). String(), ) } { type artistStruct struct { ID int `db:"id,omitempty"` Name string `db:"name,omitempty"` } { q := b.InsertInto("artist").Values(artistStruct{Name: "Chavela Vargas"}) assert.Equal( `INSERT INTO "artist" ("name") VALUES ($1)`, q.String(), ) assert.Equal( []interface{}{"Chavela Vargas"}, q.Arguments(), ) } { q := b.InsertInto("artist").Values(artistStruct{Name: "Chavela Vargas"}).Values(artistStruct{Name: "Alondra de la Parra"}) assert.Equal( `INSERT INTO "artist" ("id", "name") VALUES (DEFAULT, $1), (DEFAULT, $2)`, q.String(), ) assert.Equal( []interface{}{"Chavela Vargas", "Alondra de la Parra"}, q.Arguments(), ) } { q := b.InsertInto("artist").Values(artistStruct{ID: 1}) assert.Equal( `INSERT INTO "artist" ("id") VALUES ($1)`, q.String(), ) assert.Equal( []interface{}{1}, q.Arguments(), ) } { q := b.InsertInto("artist").Values(artistStruct{ID: 1}).Values(artistStruct{ID: 2}) assert.Equal( `INSERT INTO "artist" ("id", "name") VALUES ($1, DEFAULT), ($2, DEFAULT)`, q.String(), ) assert.Equal( []interface{}{1, 2}, q.Arguments(), ) } } { intRef := func(i int) *int { if i == 0 { return nil } return &i } strRef := func(s string) *string { if s == "" { return nil } return &s } type artistStruct struct { ID *int `db:"id,omitempty"` Name *string `db:"name,omitempty"` } q := b.InsertInto("artist"). Values(artistStruct{intRef(0), strRef("")}). Values(artistStruct{intRef(12), strRef("Chavela Vargas")}). Values(artistStruct{intRef(0), strRef("Alondra de la Parra")}). Values(artistStruct{intRef(14), strRef("")}). Values(artistStruct{intRef(0), strRef("")}) assert.Equal( `INSERT INTO "artist" ("id", "name") VALUES (DEFAULT, DEFAULT), ($1, $2), (DEFAULT, $3), ($4, DEFAULT), (DEFAULT, DEFAULT)`, q.String(), ) assert.Equal( []interface{}{intRef(12), strRef("Chavela Vargas"), strRef("Alondra de la Parra"), intRef(14)}, q.Arguments(), ) } assert.Equal( `INSERT INTO "artist" ("name", "id") VALUES ($1, $2)`, b.InsertInto("artist").Columns("name", "id").Values("Chavela Vargas", 12).String(), ) assert.Equal( `INSERT INTO "artist" VALUES (default)`, b.InsertInto("artist").String(), ) } func TestUpdate(t *testing.T) { b := &sqlBuilder{t: newTemplateWithUtils(&testTemplate)} assert := assert.New(t) assert.Equal( `UPDATE "artist" SET "name" = $1`, b.Update("artist").Set("name", "Artist").String(), ) assert.Equal( `UPDATE "artist" SET "name" = $1 RETURNING "name"`, b.Update("artist").Set("name", "Artist").Amend(func(query string) string { return query + ` RETURNING "name"` }).String(), ) { idSlice := []int64{8, 7, 6} q := b.Update("artist").Set(mydb.Cond{"some_column": 10}).Where(mydb.Cond{"id": 1}, mydb.Cond{"another_val": idSlice}) assert.Equal( `UPDATE "artist" SET "some_column" = $1 WHERE ("id" = $2 AND "another_val" IN ($3, $4, $5))`, q.String(), ) assert.Equal( []interface{}{10, 1, int64(8), int64(7), int64(6)}, q.Arguments(), ) } { idSlice := []int64{} q := b.Update("artist").Set(mydb.Cond{"some_column": 10}).Where(mydb.Cond{"id": 1}, mydb.Cond{"another_val": idSlice}) assert.Equal( `UPDATE "artist" SET "some_column" = $1 WHERE ("id" = $2 AND "another_val" IN (NULL))`, q.String(), ) assert.Equal( []interface{}{10, 1}, q.Arguments(), ) } { idSlice := []int64{} q := b.Update("artist").Where(mydb.Cond{"id": 1}, mydb.Cond{"another_val": idSlice}).Set(mydb.Cond{"some_column": 10}) assert.Equal( `UPDATE "artist" SET "some_column" = $1 WHERE ("id" = $2 AND "another_val" IN (NULL))`, q.String(), ) assert.Equal( []interface{}{10, 1}, q.Arguments(), ) } assert.Equal( `UPDATE "artist" SET "name" = $1 WHERE ("id" < $2)`, b.Update("artist").Set("name = ?", "Artist").Where("id <", 5).String(), ) assert.Equal( `UPDATE "artist" SET "name" = $1 WHERE ("id" < $2)`, b.Update("artist").Set(map[string]string{"name": "Artist"}).Where(mydb.Cond{"id <": 5}).String(), ) assert.Equal( `UPDATE "artist" SET "name" = $1 WHERE ("id" < $2)`, b.Update("artist").Set(struct { Nombre string `db:"name"` }{"Artist"}).Where(mydb.Cond{"id <": 5}).String(), ) assert.Equal( `UPDATE "artist" SET "name" = $1 WHERE ("id" < $2)`, b.Update("artist").Where(mydb.Cond{"id <": 5}).Set(struct { Nombre string `db:"name"` }{"Artist"}).String(), ) assert.Equal( `UPDATE "artist" SET "name" = $1, "last_name" = $2 WHERE ("id" < $3)`, b.Update("artist").Set(struct { Nombre string `db:"name"` }{"Artist"}).Set(map[string]string{"last_name": "Foo"}).Where(mydb.Cond{"id <": 5}).String(), ) assert.Equal( `UPDATE "artist" SET "name" = $1 || ' ' || $2 || id, "id" = id + $3 WHERE (id > $4)`, b.Update("artist").Set( "name = ? || ' ' || ? || id", "Artist", "#", "id = id + ?", 10, ).Where("id > ?", 0).String(), ) { q := b.Update("posts").Set("column = ?", "foo") assert.Equal( `UPDATE "posts" SET "column" = $1`, q.String(), ) assert.Equal( []interface{}{"foo"}, q.Arguments(), ) } { q := b.Update("posts").Set(mydb.Raw("column = ?", "foo")) assert.Equal( `UPDATE "posts" SET column = $1`, q.String(), ) assert.Equal( []interface{}{"foo"}, q.Arguments(), ) } { q := b.Update("posts").Set("foo = bar") assert.Equal( []interface{}(nil), q.Arguments(), ) assert.Equal( `UPDATE "posts" SET "foo" = bar`, q.String(), ) } { q := b.Update("posts").Set( mydb.Cond{"tags": mydb.Raw("array_remove(tags, ?)", "foo")}, ).Where(mydb.Raw("hub_id = ? AND ? = ANY(tags) AND ? = ANY(tags)", 1, "bar", "baz")) assert.Equal( `UPDATE "posts" SET "tags" = array_remove(tags, $1) WHERE (hub_id = $2 AND $3 = ANY(tags) AND $4 = ANY(tags))`, q.String(), ) assert.Equal( []interface{}{"foo", 1, "bar", "baz"}, q.Arguments(), ) } } func TestDelete(t *testing.T) { bt := WithTemplate(&testTemplate) assert := assert.New(t) assert.Equal( `DELETE FROM "artist" WHERE (name = $1)`, bt.DeleteFrom("artist").Where("name = ?", "Chavela Vargas").String(), ) assert.Equal( `DELETE FROM "artist" WHERE (name = $1) RETURNING 1`, bt.DeleteFrom("artist").Where("name = ?", "Chavela Vargas").Amend(func(query string) string { return fmt.Sprintf("%s RETURNING 1", query) }).String(), ) assert.Equal( `DELETE FROM "artist" WHERE (id > 5)`, bt.DeleteFrom("artist").Where("id > 5").String(), ) } func TestPaginate(t *testing.T) { b := &sqlBuilder{t: newTemplateWithUtils(&testTemplate)} assert := assert.New(t) // Limit, offset assert.Equal( `SELECT * FROM "artist" LIMIT 10`, b.Select().From("artist").Paginate(10).Page(1).String(), ) assert.Equal( `SELECT * FROM "artist" LIMIT 10 OFFSET 10`, b.Select().From("artist").Paginate(10).Page(2).String(), ) assert.Equal( `SELECT * FROM "artist" LIMIT 5 OFFSET 110`, b.Select().From("artist").Paginate(5).Page(23).String(), ) // Cursor assert.Equal( `SELECT * FROM "artist" ORDER BY "id" ASC LIMIT 10`, b.Select().From("artist").Paginate(10).Cursor("id").String(), ) { q := b.Select().From("artist").Paginate(10).Cursor("id").NextPage(3) assert.Equal( `SELECT * FROM "artist" WHERE ("id" > $1) ORDER BY "id" ASC LIMIT 10`, q.String(), ) assert.Equal( []interface{}{3}, q.Arguments(), ) } { q := b.Select().From("artist").Paginate(10).Cursor("id").PrevPage(30) assert.Equal( `SELECT * FROM (SELECT * FROM "artist" WHERE ("id" < $1) ORDER BY "id" DESC LIMIT 10) AS p0 ORDER BY "id" ASC`, q.String(), ) assert.Equal( []interface{}{30}, q.Arguments(), ) } // Cursor reversed assert.Equal( `SELECT * FROM "artist" ORDER BY "id" DESC LIMIT 10`, b.Select().From("artist").Paginate(10).Cursor("-id").String(), ) { q := b.Select().From("artist").Paginate(10).Cursor("-id").NextPage(3) assert.Equal( `SELECT * FROM "artist" WHERE ("id" < $1) ORDER BY "id" DESC LIMIT 10`, q.String(), ) assert.Equal( []interface{}{3}, q.Arguments(), ) } { q := b.Select().From("artist").Paginate(10).Cursor("-id").PrevPage(30) assert.Equal( `SELECT * FROM (SELECT * FROM "artist" WHERE ("id" > $1) ORDER BY "id" ASC LIMIT 10) AS p0 ORDER BY "id" DESC`, q.String(), ) assert.Equal( []interface{}{30}, q.Arguments(), ) } } func BenchmarkDelete1(b *testing.B) { bt := WithTemplate(&testTemplate) for n := 0; n < b.N; n++ { _ = bt.DeleteFrom("artist").Where("name = ?", "Chavela Vargas").Limit(1).String() } } func BenchmarkDelete2(b *testing.B) { bt := WithTemplate(&testTemplate) for n := 0; n < b.N; n++ { _ = bt.DeleteFrom("artist").Where("id > 5").String() } } func BenchmarkInsert1(b *testing.B) { bt := WithTemplate(&testTemplate) for n := 0; n < b.N; n++ { _ = bt.InsertInto("artist").Values(10, "Ryuichi Sakamoto").Values(11, "Alondra de la Parra").Values(12, "Haruki Murakami").String() } } func BenchmarkInsert2(b *testing.B) { bt := WithTemplate(&testTemplate) for n := 0; n < b.N; n++ { _ = bt.InsertInto("artist").Values(map[string]string{"id": "12", "name": "Chavela Vargas"}).String() } } func BenchmarkInsert3(b *testing.B) { bt := WithTemplate(&testTemplate) for n := 0; n < b.N; n++ { _ = bt.InsertInto("artist").Values(map[string]string{"id": "12", "name": "Chavela Vargas"}).Returning("id").String() } } func BenchmarkInsert4(b *testing.B) { bt := WithTemplate(&testTemplate) for n := 0; n < b.N; n++ { _ = bt.InsertInto("artist").Values(map[string]interface{}{"name": "Chavela Vargas", "id": 12}).String() } } func BenchmarkInsert5(b *testing.B) { bt := WithTemplate(&testTemplate) for n := 0; n < b.N; n++ { _ = bt.InsertInto("artist").Values(struct { ID int `db:"id"` Name string `db:"name"` }{12, "Chavela Vargas"}).String() } } func BenchmarkSelect1(b *testing.B) { bt := WithTemplate(&testTemplate) for n := 0; n < b.N; n++ { _ = bt.Select().From("artist").OrderBy("name DESC").String() } } func BenchmarkSelect2(b *testing.B) { bt := WithTemplate(&testTemplate) for n := 0; n < b.N; n++ { _ = bt.Select("id").From("artist").Where(`name LIKE ? OR name LIKE ?`, `%Miya%`, `F%`).String() } } func BenchmarkSelect3(b *testing.B) { bt := WithTemplate(&testTemplate) for n := 0; n < b.N; n++ { _ = bt.Select().From("artist a", "publication as p").Where("p.author_id = a.id").Limit(1).String() } } func BenchmarkSelect4(b *testing.B) { bt := WithTemplate(&testTemplate) for n := 0; n < b.N; n++ { _ = bt.SelectFrom("artist").Join("publication p").On("p.author_id = a.id").Where("a.id = 2").Limit(1).String() } } func BenchmarkSelect5(b *testing.B) { t := WithTemplate(&testTemplate) b.ResetTimer() for n := 0; n < b.N; n++ { _ = t.SelectFrom("artist a"). LeftJoin("publication p1").On("p1.id = a.id"). RightJoin("publication p2").On("p2.id = a.id"). String() } } func BenchmarkUpdate1(b *testing.B) { bt := WithTemplate(&testTemplate) for n := 0; n < b.N; n++ { _ = bt.Update("artist").Set("name", "Artist").String() } } func BenchmarkUpdate2(b *testing.B) { bt := WithTemplate(&testTemplate) for n := 0; n < b.N; n++ { _ = bt.Update("artist").Set("name = ?", "Artist").Where("id <", 5).String() } } func BenchmarkUpdate3(b *testing.B) { bt := WithTemplate(&testTemplate) for n := 0; n < b.N; n++ { _ = bt.Update("artist").Set(struct { Nombre string `db:"name"` }{"Artist"}).Set(map[string]string{"last_name": "Foo"}).Where(mydb.Cond{"id <": 5}).String() } } func BenchmarkUpdate4(b *testing.B) { bt := WithTemplate(&testTemplate) for n := 0; n < b.N; n++ { _ = bt.Update("artist").Set(map[string]string{"name": "Artist"}).Where(mydb.Cond{"id <": 5}).String() } } func BenchmarkUpdate5(b *testing.B) { bt := WithTemplate(&testTemplate) for n := 0; n < b.N; n++ { _ = bt.Update("artist").Set( "name = ? || ' ' || ? || id", "Artist", "#", "id = id + ?", 10, ).Where("id > ?", 0).String() } } func stripWhitespace(in string) string { q := reInvisibleChars.ReplaceAllString(in, ` `) return strings.TrimSpace(q) }