Add Or and And query mods, add ? placeholders
* Update whereClause builder to support Or and parentheses * ? placeholders are now used instead of $1, $2 etc for where clauses. * Update the whereClause test to add more tests and fix broken ones. * Fix broken golden test files.
This commit is contained in:
parent
af1b647fb4
commit
c3f8cff117
7 changed files with 177 additions and 45 deletions
|
@ -1 +1 @@
|
||||||
DELETE FROM thing happy, upset as "sad", "fun", thing as stuff, "angry" as mad WHERE a=$1 AND b=$2 AND c=$3;
|
DELETE FROM thing happy, upset as "sad", "fun", thing as stuff, "angry" as mad WHERE (a=$1) AND (b=$2) AND (c=$3);
|
|
@ -1 +1 @@
|
||||||
DELETE FROM thing happy, upset as "sad", "fun", thing as stuff, "angry" as mad WHERE (id=$1 and $thing=$2) or stuff=$3;
|
DELETE FROM thing happy, upset as "sad", "fun", thing as stuff, "angry" as mad WHERE ((id=$1 and thing=$2) or stuff=$3);
|
|
@ -54,6 +54,23 @@ func Where(clause string, args ...interface{}) QueryMod {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// And allows you to specify a where clause seperated by an AND for your statement
|
||||||
|
// And is a duplicate of the Where function, but allows for more natural looking
|
||||||
|
// query mod chains, for example: (Where("a=?"), And("b=?"), Or("c=?")))
|
||||||
|
func And(clause string, args ...interface{}) QueryMod {
|
||||||
|
return func(q *boil.Query) {
|
||||||
|
boil.AppendWhere(q, clause, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Or allows you to specify a where clause seperated by an OR for your statement
|
||||||
|
func Or(clause string, args ...interface{}) QueryMod {
|
||||||
|
return func(q *boil.Query) {
|
||||||
|
boil.SetLastWhereAsOr(q)
|
||||||
|
boil.AppendWhere(q, clause, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GroupBy allows you to specify a group by clause for your statement
|
// GroupBy allows you to specify a group by clause for your statement
|
||||||
func GroupBy(clause string) QueryMod {
|
func GroupBy(clause string) QueryMod {
|
||||||
return func(q *boil.Query) {
|
return func(q *boil.Query) {
|
||||||
|
|
|
@ -35,8 +35,9 @@ type Query struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type where struct {
|
type where struct {
|
||||||
clause string
|
clause string
|
||||||
args []interface{}
|
orSeparator bool
|
||||||
|
args []interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
type plainSQL struct {
|
type plainSQL struct {
|
||||||
|
@ -179,6 +180,15 @@ func SetWhere(q *Query, clause string, args ...interface{}) {
|
||||||
q.where = append([]where(nil), where{clause: clause, args: args})
|
q.where = append([]where(nil), where{clause: clause, args: args})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetLastWhereAsOr sets the or separator for the tail where in the slice
|
||||||
|
func SetLastWhereAsOr(q *Query) {
|
||||||
|
if len(q.where) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
q.where[len(q.where)-1].orSeparator = true
|
||||||
|
}
|
||||||
|
|
||||||
// ApplyGroupBy on the query.
|
// ApplyGroupBy on the query.
|
||||||
func ApplyGroupBy(q *Query, clause string) {
|
func ApplyGroupBy(q *Query, clause string) {
|
||||||
q.groupBy = append(q.groupBy, clause)
|
q.groupBy = append(q.groupBy, clause)
|
||||||
|
|
|
@ -175,14 +175,43 @@ func whereClause(q *Query) (string, []interface{}) {
|
||||||
|
|
||||||
buf.WriteString(" WHERE ")
|
buf.WriteString(" WHERE ")
|
||||||
for i := 0; i < len(q.where); i++ {
|
for i := 0; i < len(q.where); i++ {
|
||||||
buf.WriteString(fmt.Sprintf("%s", q.where[i].clause))
|
buf.WriteString(fmt.Sprintf("(%s)", q.where[i].clause))
|
||||||
args = append(args, q.where[i].args...)
|
args = append(args, q.where[i].args...)
|
||||||
if i < len(q.where)-1 {
|
|
||||||
|
// break on the last loop
|
||||||
|
if i == len(q.where)-1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if q.where[i].orSeparator {
|
||||||
|
buf.WriteString(" OR ")
|
||||||
|
} else {
|
||||||
buf.WriteString(" AND ")
|
buf.WriteString(" AND ")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf.String(), args
|
whereStr := buf.String()
|
||||||
|
paramBuf := &bytes.Buffer{}
|
||||||
|
paramIndex := 0
|
||||||
|
|
||||||
|
for counter := 1; ; counter++ {
|
||||||
|
if paramIndex >= len(whereStr) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
whereStr = whereStr[paramIndex:]
|
||||||
|
paramIndex = strings.IndexByte(whereStr, '?')
|
||||||
|
|
||||||
|
if paramIndex == -1 {
|
||||||
|
paramBuf.WriteString(whereStr)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
paramBuf.WriteString(whereStr[:paramIndex] + fmt.Sprintf("$%d", counter))
|
||||||
|
paramIndex++
|
||||||
|
}
|
||||||
|
|
||||||
|
return paramBuf.String(), args
|
||||||
}
|
}
|
||||||
|
|
||||||
// identifierMapping creates a map of all identifiers to potential model names
|
// identifierMapping creates a map of all identifiers to potential model names
|
||||||
|
|
|
@ -48,16 +48,16 @@ func TestBuildQuery(t *testing.T) {
|
||||||
delete: true,
|
delete: true,
|
||||||
from: []string{"thing happy", `upset as "sad"`, "fun", "thing as stuff", `"angry" as mad`},
|
from: []string{"thing happy", `upset as "sad"`, "fun", "thing as stuff", `"angry" as mad`},
|
||||||
where: []where{
|
where: []where{
|
||||||
where{clause: "a=$1", args: []interface{}{}},
|
where{clause: "a=?", args: []interface{}{}},
|
||||||
where{clause: "b=$2", args: []interface{}{}},
|
where{clause: "b=?", args: []interface{}{}},
|
||||||
where{clause: "c=$3", args: []interface{}{}},
|
where{clause: "c=?", args: []interface{}{}},
|
||||||
},
|
},
|
||||||
}, nil},
|
}, nil},
|
||||||
{&Query{
|
{&Query{
|
||||||
delete: true,
|
delete: true,
|
||||||
from: []string{"thing happy", `upset as "sad"`, "fun", "thing as stuff", `"angry" as mad`},
|
from: []string{"thing happy", `upset as "sad"`, "fun", "thing as stuff", `"angry" as mad`},
|
||||||
where: []where{
|
where: []where{
|
||||||
where{clause: "(id=$1 and $thing=$2) or stuff=$3", args: []interface{}{}},
|
where{clause: "(id=? and thing=?) or stuff=?", args: []interface{}{}},
|
||||||
},
|
},
|
||||||
}, nil},
|
}, nil},
|
||||||
}
|
}
|
||||||
|
@ -180,76 +180,119 @@ func TestWriteStars(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWhereClause(t *testing.T) {
|
func TestWhereClause(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
q Query
|
q Query
|
||||||
expect string
|
expect string
|
||||||
}{
|
}{
|
||||||
// Where("a=$1")
|
// Or("a=?")
|
||||||
{
|
{
|
||||||
q: Query{
|
q: Query{
|
||||||
where: []where{
|
where: []where{where{clause: "a=?", orSeparator: true}},
|
||||||
where{clause: "a=$1"},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
expect: " WHERE a=$1",
|
expect: " WHERE (a=$1)",
|
||||||
},
|
},
|
||||||
// Where("a=$1 OR b=$2")
|
// Where("a=?")
|
||||||
{
|
{
|
||||||
q: Query{
|
q: Query{
|
||||||
where: []where{
|
where: []where{where{clause: "a=?"}},
|
||||||
where{clause: "a=$1 OR b=$2"},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
expect: " WHERE a=$1 OR b=$2",
|
expect: " WHERE (a=$1)",
|
||||||
},
|
},
|
||||||
// Where("a=$1", "b=$2")
|
// Where("(a=?)")
|
||||||
{
|
{
|
||||||
q: Query{
|
q: Query{
|
||||||
where: []where{
|
where: []where{where{clause: "(a=?)"}},
|
||||||
where{clause: "a=$1"},
|
|
||||||
where{clause: "b=$2"},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
expect: " WHERE a=$1 AND b=$2",
|
expect: " WHERE ((a=$1))",
|
||||||
},
|
},
|
||||||
// Where("(a=$1 AND b=$2) OR c=$3")
|
// Where("((a=? OR b=?))")
|
||||||
{
|
{
|
||||||
q: Query{
|
q: Query{
|
||||||
where: []where{
|
where: []where{where{clause: "((a=? OR b=?))"}},
|
||||||
where{clause: "(a=$1 AND b=$2) OR c=$3"},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
expect: " WHERE (a=$1 AND b=$2) OR c=$3",
|
expect: " WHERE (((a=$1 OR b=$2)))",
|
||||||
},
|
},
|
||||||
// Where("a=$1 OR b=$2", "c=$3 OR d=$4 OR e=$5")
|
// Where("(a=?)", Or("(b=?)")
|
||||||
{
|
{
|
||||||
q: Query{
|
q: Query{
|
||||||
where: []where{
|
where: []where{
|
||||||
where{clause: "(a=$1 OR b=$2)"},
|
where{clause: "(a=?)", orSeparator: true},
|
||||||
where{clause: "(c=$3 OR d=$4 OR e=$5)"},
|
where{clause: "(b=?)"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expect: " WHERE (a=$1 OR b=$2) AND (c=$3 OR d=$4 OR e=$5)",
|
expect: " WHERE ((a=$1)) OR ((b=$2))",
|
||||||
},
|
},
|
||||||
// Where("(a=$1 AND b=$2) OR (c=$3 AND d=$4 AND e=$5) OR f=$6 OR f=$7")
|
// Where("a=? OR b=?")
|
||||||
{
|
{
|
||||||
q: Query{
|
q: Query{
|
||||||
where: []where{
|
where: []where{where{clause: "a=? OR b=?"}},
|
||||||
where{clause: "(a=$1 AND b=$2) OR (c=$3 AND d=$4 AND e=$5) OR f=$6 OR g=$7"},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
expect: " WHERE (a=$1 AND b=$2) OR (c=$3 AND d=$4 AND e=$5) OR f=$6 OR g=$7",
|
expect: " WHERE (a=$1 OR b=$2)",
|
||||||
},
|
},
|
||||||
// Where("(a=$1 AND b=$2) OR (c=$3 AND d=$4 OR e=$5) OR f=$6 OR g=$7")
|
// Where("a=?"), Where("b=?")
|
||||||
|
{
|
||||||
|
q: Query{
|
||||||
|
where: []where{where{clause: "a=?"}, where{clause: "b=?"}},
|
||||||
|
},
|
||||||
|
expect: " WHERE (a=$1) AND (b=$2)",
|
||||||
|
},
|
||||||
|
// Where("(a=? AND b=?) OR c=?")
|
||||||
|
{
|
||||||
|
q: Query{
|
||||||
|
where: []where{where{clause: "(a=? AND b=?) OR c=?"}},
|
||||||
|
},
|
||||||
|
expect: " WHERE ((a=$1 AND b=$2) OR c=$3)",
|
||||||
|
},
|
||||||
|
// Where("a=? OR b=?"), Where("c=? OR d=? OR e=?")
|
||||||
{
|
{
|
||||||
q: Query{
|
q: Query{
|
||||||
where: []where{
|
where: []where{
|
||||||
where{clause: "(a=$1 AND b=$2) OR (c=$3 AND d=$4 OR e=$5) OR f=$6 OR g=$7"},
|
where{clause: "(a=? OR b=?)"},
|
||||||
|
where{clause: "(c=? OR d=? OR e=?)"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expect: " WHERE (a=$1 AND b=$2) OR (c=$3 AND d=$4 OR e=$5) OR f=$6 OR g=$7",
|
expect: " WHERE ((a=$1 OR b=$2)) AND ((c=$3 OR d=$4 OR e=$5))",
|
||||||
|
},
|
||||||
|
// Where("(a=? AND b=?) OR (c=? AND d=? AND e=?) OR f=? OR f=?")
|
||||||
|
{
|
||||||
|
q: Query{
|
||||||
|
where: []where{
|
||||||
|
where{clause: "(a=? AND b=?) OR (c=? AND d=? AND e=?) OR f=? OR g=?"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expect: " WHERE ((a=$1 AND b=$2) OR (c=$3 AND d=$4 AND e=$5) OR f=$6 OR g=$7)",
|
||||||
|
},
|
||||||
|
// Where("(a=? AND b=?) OR (c=? AND d=? OR e=?) OR f=? OR g=?")
|
||||||
|
{
|
||||||
|
q: Query{
|
||||||
|
where: []where{
|
||||||
|
where{clause: "(a=? AND b=?) OR (c=? AND d=? OR e=?) OR f=? OR g=?"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expect: " WHERE ((a=$1 AND b=$2) OR (c=$3 AND d=$4 OR e=$5) OR f=$6 OR g=$7)",
|
||||||
|
},
|
||||||
|
// Where("a=? or b=?"), Or("c=? and d=?"), Or("e=? or f=?")
|
||||||
|
{
|
||||||
|
q: Query{
|
||||||
|
where: []where{
|
||||||
|
where{clause: "a=? or b=?", orSeparator: true},
|
||||||
|
where{clause: "c=? and d=?", orSeparator: true},
|
||||||
|
where{clause: "e=? or f=?", orSeparator: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expect: " WHERE (a=$1 or b=$2) OR (c=$3 and d=$4) OR (e=$5 or f=$6)",
|
||||||
|
},
|
||||||
|
// Where("a=? or b=?"), Or("c=? and d=?"), Or("e=? or f=?")
|
||||||
|
{
|
||||||
|
q: Query{
|
||||||
|
where: []where{
|
||||||
|
where{clause: "a=? or b=?"},
|
||||||
|
where{clause: "c=? and d=?"},
|
||||||
|
where{clause: "e=? or f=?"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expect: " WHERE (a=$1 or b=$2) AND (c=$3 and d=$4) AND (e=$5 or f=$6)",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -87,6 +87,39 @@ func TestWhere(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSetLastWhereAsOr(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
q := &Query{}
|
||||||
|
|
||||||
|
AppendWhere(q, "")
|
||||||
|
|
||||||
|
if q.where[0].orSeparator {
|
||||||
|
t.Errorf("Do not want or separator")
|
||||||
|
}
|
||||||
|
|
||||||
|
SetLastWhereAsOr(q)
|
||||||
|
|
||||||
|
if len(q.where) != 1 {
|
||||||
|
t.Errorf("Want len 1")
|
||||||
|
}
|
||||||
|
if !q.where[0].orSeparator {
|
||||||
|
t.Errorf("Want or separator")
|
||||||
|
}
|
||||||
|
|
||||||
|
AppendWhere(q, "")
|
||||||
|
SetLastWhereAsOr(q)
|
||||||
|
|
||||||
|
if len(q.where) != 2 {
|
||||||
|
t.Errorf("Want len 2")
|
||||||
|
}
|
||||||
|
if q.where[0].orSeparator != true {
|
||||||
|
t.Errorf("Expected true")
|
||||||
|
}
|
||||||
|
if q.where[1].orSeparator != true {
|
||||||
|
t.Errorf("Expected true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGroupBy(t *testing.T) {
|
func TestGroupBy(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue