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
|
||||
func GroupBy(clause string) QueryMod {
|
||||
return func(q *boil.Query) {
|
||||
|
|
|
@ -35,8 +35,9 @@ type Query struct {
|
|||
}
|
||||
|
||||
type where struct {
|
||||
clause string
|
||||
args []interface{}
|
||||
clause string
|
||||
orSeparator bool
|
||||
args []interface{}
|
||||
}
|
||||
|
||||
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})
|
||||
}
|
||||
|
||||
// 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.
|
||||
func ApplyGroupBy(q *Query, clause string) {
|
||||
q.groupBy = append(q.groupBy, clause)
|
||||
|
|
|
@ -175,14 +175,43 @@ func whereClause(q *Query) (string, []interface{}) {
|
|||
|
||||
buf.WriteString(" WHERE ")
|
||||
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...)
|
||||
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 ")
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -48,16 +48,16 @@ func TestBuildQuery(t *testing.T) {
|
|||
delete: true,
|
||||
from: []string{"thing happy", `upset as "sad"`, "fun", "thing as stuff", `"angry" as mad`},
|
||||
where: []where{
|
||||
where{clause: "a=$1", args: []interface{}{}},
|
||||
where{clause: "b=$2", args: []interface{}{}},
|
||||
where{clause: "c=$3", args: []interface{}{}},
|
||||
where{clause: "a=?", args: []interface{}{}},
|
||||
where{clause: "b=?", args: []interface{}{}},
|
||||
where{clause: "c=?", args: []interface{}{}},
|
||||
},
|
||||
}, nil},
|
||||
{&Query{
|
||||
delete: true,
|
||||
from: []string{"thing happy", `upset as "sad"`, "fun", "thing as stuff", `"angry" as mad`},
|
||||
where: []where{
|
||||
where{clause: "(id=$1 and $thing=$2) or stuff=$3", args: []interface{}{}},
|
||||
where{clause: "(id=? and thing=?) or stuff=?", args: []interface{}{}},
|
||||
},
|
||||
}, nil},
|
||||
}
|
||||
|
@ -180,76 +180,119 @@ func TestWriteStars(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestWhereClause(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
q Query
|
||||
expect string
|
||||
}{
|
||||
// Where("a=$1")
|
||||
// Or("a=?")
|
||||
{
|
||||
q: Query{
|
||||
where: []where{
|
||||
where{clause: "a=$1"},
|
||||
},
|
||||
where: []where{where{clause: "a=?", orSeparator: true}},
|
||||
},
|
||||
expect: " WHERE a=$1",
|
||||
expect: " WHERE (a=$1)",
|
||||
},
|
||||
// Where("a=$1 OR b=$2")
|
||||
// Where("a=?")
|
||||
{
|
||||
q: Query{
|
||||
where: []where{
|
||||
where{clause: "a=$1 OR b=$2"},
|
||||
},
|
||||
where: []where{where{clause: "a=?"}},
|
||||
},
|
||||
expect: " WHERE a=$1 OR b=$2",
|
||||
expect: " WHERE (a=$1)",
|
||||
},
|
||||
// Where("a=$1", "b=$2")
|
||||
// Where("(a=?)")
|
||||
{
|
||||
q: Query{
|
||||
where: []where{
|
||||
where{clause: "a=$1"},
|
||||
where{clause: "b=$2"},
|
||||
},
|
||||
where: []where{where{clause: "(a=?)"}},
|
||||
},
|
||||
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{
|
||||
where: []where{
|
||||
where{clause: "(a=$1 AND b=$2) OR c=$3"},
|
||||
},
|
||||
where: []where{where{clause: "((a=? OR b=?))"}},
|
||||
},
|
||||
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{
|
||||
where: []where{
|
||||
where{clause: "(a=$1 OR b=$2)"},
|
||||
where{clause: "(c=$3 OR d=$4 OR e=$5)"},
|
||||
where{clause: "(a=?)", orSeparator: true},
|
||||
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{
|
||||
where: []where{
|
||||
where{clause: "(a=$1 AND b=$2) OR (c=$3 AND d=$4 AND e=$5) OR f=$6 OR g=$7"},
|
||||
},
|
||||
where: []where{where{clause: "a=? OR b=?"}},
|
||||
},
|
||||
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{
|
||||
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) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue