Finish WHERE IN feature
* Fix bug with appendOr/And ordering
This commit is contained in:
parent
3cdfc6237a
commit
d28b3f4b62
5 changed files with 252 additions and 174 deletions
|
@ -1 +1 @@
|
|||
UPDATE thing happy, "fun", "stuff" SET ("col2", "fun"."col3", "col1") = ($1,$2,$3) WHERE (aa=$4 or bb=$5 or cc=$6) OR (dd=$7 or ee=$8 or ff=$9 and gg=$10) LIMIT 5;
|
||||
UPDATE thing happy, "fun", "stuff" SET ("col2", "fun"."col3", "col1") = ($1,$2,$3) WHERE (aa=$4 or bb=$5 or cc=$6) AND (dd=$7 or ee=$8 or ff=$9 and gg=$10) LIMIT 5;
|
|
@ -63,8 +63,8 @@ func And(clause string, args ...interface{}) QueryMod {
|
|||
// 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...)
|
||||
boil.SetLastWhereAsOr(q)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,8 +90,8 @@ func AndIn(clause string, args ...interface{}) QueryMod {
|
|||
// an OR for your where statement
|
||||
func OrIn(clause string, args ...interface{}) QueryMod {
|
||||
return func(q *boil.Query) {
|
||||
boil.SetLastInAsOr(q)
|
||||
boil.AppendIn(q, clause, args...)
|
||||
boil.SetLastInAsOr(q)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
var (
|
||||
rgxIdentifier = regexp.MustCompile(`^(?i)"?[a-z_][_a-z0-9]*"?(?:\."?[_a-z][_a-z0-9]*"?)*$`)
|
||||
rgxInClause = regexp.MustCompile(`^(?i)(.*[\s|\)|\?])IN([\s|\(|\?].*)$`)
|
||||
)
|
||||
|
||||
func buildQuery(q *Query) (string, []interface{}) {
|
||||
|
@ -77,7 +78,8 @@ func buildSelectQuery(q *Query) (*bytes.Buffer, []interface{}) {
|
|||
fmt.Fprintf(joinBuf, " INNER JOIN %s", j.clause)
|
||||
args = append(args, j.args...)
|
||||
}
|
||||
fmt.Fprintf(buf, convertQuestionMarks(joinBuf.String(), argsLen+1))
|
||||
resp, _ := convertQuestionMarks(joinBuf.String(), argsLen+1)
|
||||
fmt.Fprintf(buf, resp)
|
||||
strmangle.PutBuffer(joinBuf)
|
||||
}
|
||||
|
||||
|
@ -188,7 +190,8 @@ func writeModifiers(q *Query, buf *bytes.Buffer, args *[]interface{}) {
|
|||
fmt.Fprintf(havingBuf, j.clause)
|
||||
*args = append(*args, j.args...)
|
||||
}
|
||||
fmt.Fprintf(buf, convertQuestionMarks(havingBuf.String(), argsLen+1))
|
||||
resp, _ := convertQuestionMarks(havingBuf.String(), argsLen+1)
|
||||
fmt.Fprintf(buf, resp)
|
||||
strmangle.PutBuffer(havingBuf)
|
||||
}
|
||||
|
||||
|
@ -267,25 +270,26 @@ func whereClause(q *Query, startAt int) (string, []interface{}) {
|
|||
var args []interface{}
|
||||
|
||||
buf.WriteString(" WHERE ")
|
||||
for i := 0; i < len(q.where); i++ {
|
||||
buf.WriteString(fmt.Sprintf("(%s)", q.where[i].clause))
|
||||
args = append(args, q.where[i].args...)
|
||||
|
||||
// break on the last loop
|
||||
if i == len(q.where)-1 {
|
||||
break
|
||||
for i, where := range q.where {
|
||||
if i != 0 {
|
||||
if where.orSeparator {
|
||||
buf.WriteString(" OR ")
|
||||
} else {
|
||||
buf.WriteString(" AND ")
|
||||
}
|
||||
}
|
||||
|
||||
if q.where[i].orSeparator {
|
||||
buf.WriteString(" OR ")
|
||||
} else {
|
||||
buf.WriteString(" AND ")
|
||||
}
|
||||
buf.WriteString(fmt.Sprintf("(%s)", where.clause))
|
||||
args = append(args, where.args...)
|
||||
}
|
||||
|
||||
return convertQuestionMarks(buf.String(), startAt), args
|
||||
resp, _ := convertQuestionMarks(buf.String(), startAt)
|
||||
return resp, args
|
||||
}
|
||||
|
||||
// inClause parses an in slice and converts it into a
|
||||
// single IN clause, like:
|
||||
// WHERE ("a", "b") IN (($1,$2),($3,$4)).
|
||||
func inClause(q *Query, startAt int) (string, []interface{}) {
|
||||
if len(q.in) == 0 {
|
||||
return "", nil
|
||||
|
@ -298,28 +302,93 @@ func inClause(q *Query, startAt int) (string, []interface{}) {
|
|||
if len(q.where) == 0 {
|
||||
buf.WriteString(" WHERE ")
|
||||
}
|
||||
for i := 0; i < len(q.in); i++ {
|
||||
|
||||
for i, in := range q.in {
|
||||
ln := len(in.args)
|
||||
// We only prefix the OR and AND separators after the first
|
||||
// clause has been generated UNLESS there is already a where
|
||||
// clause that we have to add on to.
|
||||
if i != 0 || len(q.where) > 0 {
|
||||
if in.orSeparator {
|
||||
buf.WriteString(" OR ")
|
||||
} else {
|
||||
buf.WriteString(" AND ")
|
||||
}
|
||||
}
|
||||
|
||||
matches := rgxInClause.FindStringSubmatch(in.clause)
|
||||
// If we can't find any matches attempt a simple replace with 1 group.
|
||||
// Clauses that fit this criteria will not be able to contain ? in their
|
||||
// column name side, however if this case is being hit then the regexp
|
||||
// probably needs adjustment, or the user is passing in invalid clauses.
|
||||
if matches == nil {
|
||||
clause, count := convertInQuestionMarks(in.clause, startAt, 1, ln)
|
||||
buf.WriteString(clause)
|
||||
startAt = startAt + count
|
||||
} else {
|
||||
leftSide := strings.TrimSpace(matches[1])
|
||||
rightSide := strings.TrimSpace(matches[2])
|
||||
// If matches are found, we have to parse the left side (column side)
|
||||
// of the clause to determine how many columns they are using.
|
||||
// This number determines the groupAt for the convert function.
|
||||
cols := strings.Split(leftSide, ",")
|
||||
cols = strmangle.IdentQuoteSlice(cols)
|
||||
groupAt := len(cols)
|
||||
|
||||
leftClause, leftCount := convertQuestionMarks(strings.Join(cols, ","), startAt)
|
||||
rightClause, rightCount := convertInQuestionMarks(rightSide, startAt+leftCount, groupAt, ln-leftCount)
|
||||
buf.WriteString(leftClause)
|
||||
buf.WriteString(" IN ")
|
||||
buf.WriteString(rightClause)
|
||||
startAt = startAt + leftCount + rightCount
|
||||
}
|
||||
|
||||
args = append(args, in.args...)
|
||||
}
|
||||
|
||||
// regexp split thing so we have left side and right side
|
||||
// split on )IN( / \sIN\s, combine them
|
||||
|
||||
// buf.WriteString(convertQuestionMarks(leftSide, startAt))
|
||||
// buf.WriteString(" IN ")
|
||||
// buf.WriteString(convertInQuestionMarks(rightSide, total, group, startAt+offset))
|
||||
|
||||
return "", args
|
||||
return buf.String(), args
|
||||
}
|
||||
|
||||
func convertInQuestionMarks(clause string, total, groupAt, startAt int) string {
|
||||
return ""
|
||||
// convertInQuestionMarks finds the first unescaped occurence of ? and swaps it
|
||||
// with a list of numbered placeholders, starting at startAt.
|
||||
// It uses groupAt to determine how many placeholders should be in each group,
|
||||
// for example, groupAt 2 would result in: (($1,$2),($3,$4))
|
||||
// and groupAt 1 would result in ($1,$2,$3,$4)
|
||||
func convertInQuestionMarks(clause string, startAt, groupAt, total int) (string, int) {
|
||||
if startAt == 0 || len(clause) == 0 {
|
||||
panic("Not a valid start number.")
|
||||
}
|
||||
|
||||
paramBuf := strmangle.GetBuffer()
|
||||
defer strmangle.PutBuffer(paramBuf)
|
||||
|
||||
foundAt := -1
|
||||
for i := 0; i < len(clause); i++ {
|
||||
if (clause[i] == '?' && i == 0) || (clause[i] == '?' && clause[i-1] != '\\') {
|
||||
foundAt = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if foundAt == -1 {
|
||||
return strings.Replace(clause, `\?`, "?", -1), 0
|
||||
}
|
||||
|
||||
paramBuf.WriteString(clause[:foundAt])
|
||||
paramBuf.WriteByte('(')
|
||||
paramBuf.WriteString(strmangle.Placeholders(total, startAt, groupAt))
|
||||
paramBuf.WriteByte(')')
|
||||
paramBuf.WriteString(clause[foundAt+1:])
|
||||
|
||||
// Remove all backslashes from escaped question-marks
|
||||
ret := strings.Replace(paramBuf.String(), `\?`, "?", -1)
|
||||
return ret, total
|
||||
}
|
||||
|
||||
// convertQuestionMarks converts each occurence of ? with $<number>
|
||||
// where <number> is an incrementing digit starting at startAt.
|
||||
// If question-mark (?) is escaped using back-slash (\), it will be ignored.
|
||||
func convertQuestionMarks(clause string, startAt int) string {
|
||||
func convertQuestionMarks(clause string, startAt int) (string, int) {
|
||||
if startAt == 0 {
|
||||
panic("Not a valid start number.")
|
||||
}
|
||||
|
@ -327,6 +396,7 @@ func convertQuestionMarks(clause string, startAt int) string {
|
|||
paramBuf := strmangle.GetBuffer()
|
||||
defer strmangle.PutBuffer(paramBuf)
|
||||
paramIndex := 0
|
||||
total := 0
|
||||
|
||||
for {
|
||||
if paramIndex >= len(clause) {
|
||||
|
@ -349,11 +419,12 @@ func convertQuestionMarks(clause string, startAt int) string {
|
|||
}
|
||||
|
||||
paramBuf.WriteString(clause[:paramIndex] + fmt.Sprintf("$%d", startAt))
|
||||
total++
|
||||
startAt++
|
||||
paramIndex++
|
||||
}
|
||||
|
||||
return paramBuf.String()
|
||||
return paramBuf.String(), total
|
||||
}
|
||||
|
||||
// identifierMapping creates a map of all identifiers to potential model names
|
||||
|
|
|
@ -247,8 +247,8 @@ func TestWhereClause(t *testing.T) {
|
|||
{
|
||||
q: Query{
|
||||
where: []where{
|
||||
where{clause: "(a=?)", orSeparator: true},
|
||||
where{clause: "(b=?)"},
|
||||
where{clause: "(a=?)"},
|
||||
where{clause: "(b=?)", orSeparator: true},
|
||||
},
|
||||
},
|
||||
expect: " WHERE ((a=$1)) OR ((b=$2))",
|
||||
|
@ -318,11 +318,11 @@ func TestWhereClause(t *testing.T) {
|
|||
q: Query{
|
||||
where: []where{
|
||||
where{clause: "a=? or b=?"},
|
||||
where{clause: "c=? and d=?"},
|
||||
where{clause: "c=? and d=?", orSeparator: true},
|
||||
where{clause: "e=? or f=?"},
|
||||
},
|
||||
},
|
||||
expect: " WHERE (a=$1 or b=$2) AND (c=$3 and d=$4) AND (e=$5 or f=$6)",
|
||||
expect: " WHERE (a=$1 or b=$2) OR (c=$3 and d=$4) AND (e=$5 or f=$6)",
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -340,129 +340,132 @@ func TestInClause(t *testing.T) {
|
|||
tests := []struct {
|
||||
q Query
|
||||
expect string
|
||||
args []interface{}
|
||||
}{
|
||||
// Or("a=?")
|
||||
{
|
||||
q: Query{
|
||||
in: []in{{clause: "a in ?", args: []interface{}{}, orSeparator: true}},
|
||||
},
|
||||
expect: ` WHERE "a" IN ()`,
|
||||
},
|
||||
{
|
||||
q: Query{
|
||||
in: []in{{clause: "a in ?", args: []interface{}{1}, orSeparator: true}},
|
||||
},
|
||||
expect: " WHERE a IN ($1)",
|
||||
expect: ` WHERE "a" IN ($1)`,
|
||||
args: []interface{}{1},
|
||||
},
|
||||
{
|
||||
q: Query{
|
||||
in: []in{{clause: "a in ?", args: []interface{}{1, 2, 3}}},
|
||||
},
|
||||
expect: " WHERE a IN ($1,$2,$3)",
|
||||
expect: ` WHERE "a" IN ($1,$2,$3)`,
|
||||
args: []interface{}{1, 2, 3},
|
||||
},
|
||||
{
|
||||
q: Query{
|
||||
in: []in{{clause: "? in ?", args: []interface{}{1, 2, 3}}},
|
||||
},
|
||||
expect: " WHERE $1 IN ($2,$3)",
|
||||
args: []interface{}{1, 2, 3},
|
||||
},
|
||||
{
|
||||
q: Query{
|
||||
in: []in{{clause: "( ? , ? ) in ( ? )", orSeparator: true, args: []interface{}{"a", "b", 1, 2, 3, 4}}},
|
||||
},
|
||||
expect: " WHERE ( $1 , $2 ) IN ( (($3,$4),($5,$6)) )",
|
||||
args: []interface{}{"a", "b", 1, 2, 3, 4},
|
||||
},
|
||||
{
|
||||
q: Query{
|
||||
in: []in{{clause: `("a")in(?)`, orSeparator: true, args: []interface{}{1, 2, 3}}},
|
||||
},
|
||||
expect: ` WHERE ("a") IN (($1,$2,$3))`,
|
||||
args: []interface{}{1, 2, 3},
|
||||
},
|
||||
{
|
||||
q: Query{
|
||||
in: []in{{clause: `("a")in?`, args: []interface{}{1}}},
|
||||
},
|
||||
expect: ` WHERE ("a") IN ($1)`,
|
||||
args: []interface{}{1},
|
||||
},
|
||||
{
|
||||
q: Query{
|
||||
where: []where{
|
||||
{clause: "a=?", args: []interface{}{1}},
|
||||
},
|
||||
in: []in{
|
||||
{clause: `?,?,"name" in ?`, orSeparator: true, args: []interface{}{"c", "d", 3, 4, 5, 6, 7, 8}},
|
||||
{clause: `?,?,"name" in ?`, orSeparator: true, args: []interface{}{"e", "f", 9, 10, 11, 12, 13, 14}},
|
||||
},
|
||||
},
|
||||
expect: ` OR $1,$2,"name" IN (($3,$4,$5),($6,$7,$8)) OR $9,$10,"name" IN (($11,$12,$13),($14,$15,$16))`,
|
||||
args: []interface{}{"c", "d", 3, 4, 5, 6, 7, 8, "e", "f", 9, 10, 11, 12, 13, 14},
|
||||
},
|
||||
{
|
||||
q: Query{
|
||||
in: []in{
|
||||
{clause: `("a")in`, args: []interface{}{1}},
|
||||
{clause: `("a")in?`, orSeparator: true, args: []interface{}{1}},
|
||||
},
|
||||
},
|
||||
expect: ` WHERE ("a")in OR ("a") IN ($1)`,
|
||||
args: []interface{}{1, 1},
|
||||
},
|
||||
{
|
||||
q: Query{
|
||||
in: []in{
|
||||
{clause: `\?,\? in \?`, args: []interface{}{1}},
|
||||
{clause: `\?,\?in \?`, orSeparator: true, args: []interface{}{1}},
|
||||
},
|
||||
},
|
||||
expect: ` WHERE ?,? IN ? OR ?,? IN ?`,
|
||||
args: []interface{}{1, 1},
|
||||
},
|
||||
{
|
||||
q: Query{
|
||||
in: []in{
|
||||
{clause: `("a")in`, args: []interface{}{1}},
|
||||
{clause: `("a") in thing`, args: []interface{}{1, 2, 3}},
|
||||
{clause: `("a")in?`, orSeparator: true, args: []interface{}{4, 5, 6}},
|
||||
},
|
||||
},
|
||||
expect: ` WHERE ("a")in AND ("a") IN thing OR ("a") IN ($1,$2,$3)`,
|
||||
args: []interface{}{1, 1, 2, 3, 4, 5, 6},
|
||||
},
|
||||
{
|
||||
q: Query{
|
||||
in: []in{
|
||||
{clause: `("a")in?`, orSeparator: true, args: []interface{}{4, 5, 6}},
|
||||
{clause: `("a") in thing`, args: []interface{}{1, 2, 3}},
|
||||
{clause: `("a")in`, args: []interface{}{1}},
|
||||
},
|
||||
},
|
||||
expect: ` WHERE ("a") IN ($1,$2,$3) AND ("a") IN thing AND ("a")in`,
|
||||
args: []interface{}{4, 5, 6, 1, 2, 3, 1},
|
||||
},
|
||||
{
|
||||
q: Query{
|
||||
in: []in{
|
||||
{clause: `("a")in?`, orSeparator: true, args: []interface{}{4, 5, 6}},
|
||||
{clause: `("a")in`, args: []interface{}{1}},
|
||||
{clause: `("a") in thing`, args: []interface{}{1, 2, 3}},
|
||||
},
|
||||
},
|
||||
expect: ` WHERE ("a") IN ($1,$2,$3) AND ("a")in AND ("a") IN thing`,
|
||||
args: []interface{}{4, 5, 6, 1, 1, 2, 3},
|
||||
},
|
||||
// // Where("a=?")
|
||||
// {
|
||||
// q: Query{
|
||||
// where: []where{where{clause: "a=?"}},
|
||||
// },
|
||||
// expect: " WHERE (a=$1)",
|
||||
// },
|
||||
// // Where("(a=?)")
|
||||
// {
|
||||
// q: Query{
|
||||
// where: []where{where{clause: "(a=?)"}},
|
||||
// },
|
||||
// expect: " WHERE ((a=$1))",
|
||||
// },
|
||||
// // Where("((a=? OR b=?))")
|
||||
// {
|
||||
// q: Query{
|
||||
// where: []where{where{clause: "((a=? OR b=?))"}},
|
||||
// },
|
||||
// expect: " WHERE (((a=$1 OR b=$2)))",
|
||||
// },
|
||||
// // Where("(a=?)", Or("(b=?)")
|
||||
// {
|
||||
// q: Query{
|
||||
// where: []where{
|
||||
// where{clause: "(a=?)", orSeparator: true},
|
||||
// where{clause: "(b=?)"},
|
||||
// },
|
||||
// },
|
||||
// expect: " WHERE ((a=$1)) OR ((b=$2))",
|
||||
// },
|
||||
// // Where("a=? OR b=?")
|
||||
// {
|
||||
// q: Query{
|
||||
// where: []where{where{clause: "a=? OR b=?"}},
|
||||
// },
|
||||
// expect: " WHERE (a=$1 OR b=$2)",
|
||||
// },
|
||||
// // 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=? OR b=?)"},
|
||||
// where{clause: "(c=? OR d=? OR e=?)"},
|
||||
// },
|
||||
// },
|
||||
// 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)",
|
||||
// },
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
result, _ := inClause(&test.q, 1)
|
||||
result, args := inClause(&test.q, 1)
|
||||
if result != test.expect {
|
||||
t.Errorf("%d) Mismatch between expect and result:\n%s\n%s\n", i, test.expect, result)
|
||||
}
|
||||
if !reflect.DeepEqual(args, test.args) {
|
||||
t.Errorf("%d) Mismatch between expected args:\n%#v\n%#v\n", i, test.args, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -473,35 +476,41 @@ func TestConvertQuestionMarks(t *testing.T) {
|
|||
clause string
|
||||
start int
|
||||
expect string
|
||||
count int
|
||||
}{
|
||||
{clause: "hello friend", start: 1, expect: "hello friend"},
|
||||
{clause: "thing=?", start: 2, expect: "thing=$2"},
|
||||
{clause: "thing=? and stuff=? and happy=?", start: 2, expect: "thing=$2 and stuff=$3 and happy=$4"},
|
||||
{clause: `thing \? stuff`, start: 2, expect: `thing ? stuff`},
|
||||
{clause: `thing \? stuff and happy \? fun`, start: 2, expect: `thing ? stuff and happy ? fun`},
|
||||
{clause: "hello friend", start: 1, expect: "hello friend", count: 0},
|
||||
{clause: "thing=?", start: 2, expect: "thing=$2", count: 1},
|
||||
{clause: "thing=? and stuff=? and happy=?", start: 2, expect: "thing=$2 and stuff=$3 and happy=$4", count: 3},
|
||||
{clause: `thing \? stuff`, start: 2, expect: `thing ? stuff`, count: 0},
|
||||
{clause: `thing \? stuff and happy \? fun`, start: 2, expect: `thing ? stuff and happy ? fun`, count: 0},
|
||||
{
|
||||
clause: `thing \? stuff ? happy \? and mad ? fun \? \? \?`,
|
||||
start: 2,
|
||||
expect: `thing ? stuff $2 happy ? and mad $3 fun ? ? ?`,
|
||||
count: 2,
|
||||
},
|
||||
{
|
||||
clause: `thing ? stuff ? happy \? fun \? ? ?`,
|
||||
start: 1,
|
||||
expect: `thing $1 stuff $2 happy ? fun ? $3 $4`,
|
||||
count: 4,
|
||||
},
|
||||
{clause: `?`, start: 1, expect: `$1`},
|
||||
{clause: `???`, start: 1, expect: `$1$2$3`},
|
||||
{clause: `?`, start: 1, expect: `$1`, count: 1},
|
||||
{clause: `???`, start: 1, expect: `$1$2$3`, count: 3},
|
||||
{clause: `\?`, start: 1, expect: `?`},
|
||||
{clause: `\?\?\?`, start: 1, expect: `???`},
|
||||
{clause: `\??\??\??`, start: 1, expect: `?$1?$2?$3`},
|
||||
{clause: `?\??\??\?`, start: 1, expect: `$1?$2?$3?`},
|
||||
{clause: `\??\??\??`, start: 1, expect: `?$1?$2?$3`, count: 3},
|
||||
{clause: `?\??\??\?`, start: 1, expect: `$1?$2?$3?`, count: 3},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
res := convertQuestionMarks(test.clause, test.start)
|
||||
res, count := convertQuestionMarks(test.clause, test.start)
|
||||
if res != test.expect {
|
||||
t.Errorf("%d) Mismatch between expect and result:\n%s\n%s\n", i, test.expect, res)
|
||||
}
|
||||
if count != test.count {
|
||||
t.Errorf("%d) Expected count %d, got %d", i, test.count, count)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -511,36 +520,34 @@ func TestConvertInQuestionMarks(t *testing.T) {
|
|||
tests := []struct {
|
||||
clause string
|
||||
start int
|
||||
group int
|
||||
total int
|
||||
expect string
|
||||
}{
|
||||
{clause: "hello friend", start: 1, expect: "hello friend"},
|
||||
{clause: "thing=?", start: 2, expect: "thing=$2"},
|
||||
{clause: "thing=? and stuff=? and happy=?", start: 2, expect: "thing=$2 and stuff=$3 and happy=$4"},
|
||||
{clause: `thing \? stuff`, start: 2, expect: `thing ? stuff`},
|
||||
{clause: `thing \? stuff and happy \? fun`, start: 2, expect: `thing ? stuff and happy ? fun`},
|
||||
{
|
||||
clause: `thing \? stuff ? happy \? and mad ? fun \? \? \?`,
|
||||
start: 2,
|
||||
expect: `thing ? stuff $2 happy ? and mad $3 fun ? ? ?`,
|
||||
},
|
||||
{
|
||||
clause: `thing ? stuff ? happy \? fun \? ? ?`,
|
||||
start: 1,
|
||||
expect: `thing $1 stuff $2 happy ? fun ? $3 $4`,
|
||||
},
|
||||
{clause: `?`, start: 1, expect: `$1`},
|
||||
{clause: `???`, start: 1, expect: `$1$2$3`},
|
||||
{clause: `\?`, start: 1, expect: `?`},
|
||||
{clause: `\?\?\?`, start: 1, expect: `???`},
|
||||
{clause: `\??\??\??`, start: 1, expect: `?$1?$2?$3`},
|
||||
{clause: `?\??\??\?`, start: 1, expect: `$1?$2?$3?`},
|
||||
{clause: "?", expect: "(($1,$2,$3),($4,$5,$6),($7,$8,$9))", start: 1, total: 9, group: 3},
|
||||
{clause: "?", expect: "(($2,$3),($4))", start: 2, total: 3, group: 2},
|
||||
{clause: "hello friend", start: 1, expect: "hello friend", total: 0, group: 1},
|
||||
{clause: "thing ? thing", start: 2, expect: "thing ($2,$3) thing", total: 2, group: 1},
|
||||
{clause: "thing?thing", start: 2, expect: "thing($2)thing", total: 1, group: 1},
|
||||
{clause: `thing \? stuff`, start: 2, expect: `thing ? stuff`, total: 0, group: 1},
|
||||
{clause: `thing \? stuff and happy \? fun`, start: 2, expect: `thing ? stuff and happy ? fun`, total: 0, group: 1},
|
||||
{clause: "thing ? thing ? thing", start: 1, expect: "thing ($1,$2,$3) thing ? thing", total: 3, group: 1},
|
||||
{clause: `?`, start: 1, expect: `($1)`, total: 1, group: 1},
|
||||
{clause: `???`, start: 1, expect: `($1,$2,$3)??`, total: 3, group: 1},
|
||||
{clause: `\?`, start: 1, expect: `?`, total: 0, group: 1},
|
||||
{clause: `\?\?\?`, start: 1, expect: `???`, total: 0, group: 1},
|
||||
{clause: `\??\??\??`, start: 1, expect: `?($1,$2,$3)????`, total: 3, group: 1},
|
||||
{clause: `?\??\??\?`, start: 1, expect: `($1,$2,$3)?????`, total: 3, group: 1},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
res := convertQuestionMarks(test.clause, test.start)
|
||||
res, count := convertInQuestionMarks(test.clause, test.start, test.group, test.total)
|
||||
if res != test.expect {
|
||||
t.Errorf("%d) Mismatch between expect and result:\n%s\n%s\n", i, test.expect, res)
|
||||
}
|
||||
if count != test.total {
|
||||
t.Errorf("%d) Expected %d, got %d", i, test.total, count)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -237,7 +237,7 @@ func PrefixStringSlice(str string, strs []string) []string {
|
|||
}
|
||||
|
||||
// Placeholders generates the SQL statement placeholders for in queries.
|
||||
// For example, ($1, $2, $3), ($4, $5, $6) etc.
|
||||
// For example, ($1,$2,$3),($4,$5,$6) etc.
|
||||
// It will start counting placeholders at "start".
|
||||
func Placeholders(count int, start int, group int) string {
|
||||
buf := GetBuffer()
|
||||
|
|
Loading…
Reference in a new issue