Begin IN implementation
This commit is contained in:
parent
31bc69a7cc
commit
94d36d7bf7
6 changed files with 410 additions and 93 deletions
|
@ -71,24 +71,51 @@ func Or(clause string, args ...interface{}) QueryMod {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WhereIn allows you to specify a "x IN (set)" clause for your where statement
|
||||||
|
// Example clauses: "column in ?", "(column1,column2) in ?"
|
||||||
|
func WhereIn(clause string, args ...interface{}) QueryMod {
|
||||||
|
return func(q *boil.Query) {
|
||||||
|
boil.AppendIn(q, clause, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AndIn allows you to specify a "x IN (set)" clause separated by an AndIn
|
||||||
|
// for your where statement. AndIn is a duplicate of the WhereIn function, but
|
||||||
|
// allows for more natural looking query mod chains, for example:
|
||||||
|
// (WhereIn("column1 in ?"), AndIn("column2 in ?"), OrIn("column3 in ?"))
|
||||||
|
func AndIn(clause string, args ...interface{}) QueryMod {
|
||||||
|
return func(q *boil.Query) {
|
||||||
|
boil.AppendIn(q, clause, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrIn allows you to specify an IN clause separated by
|
||||||
|
// 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...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 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) {
|
||||||
boil.ApplyGroupBy(q, clause)
|
boil.AppendGroupBy(q, clause)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OrderBy allows you to specify a order by clause for your statement
|
// OrderBy allows you to specify a order by clause for your statement
|
||||||
func OrderBy(clause string) QueryMod {
|
func OrderBy(clause string) QueryMod {
|
||||||
return func(q *boil.Query) {
|
return func(q *boil.Query) {
|
||||||
boil.ApplyOrderBy(q, clause)
|
boil.AppendOrderBy(q, clause)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Having allows you to specify a having clause for your statement
|
// Having allows you to specify a having clause for your statement
|
||||||
func Having(clause string, args ...interface{}) QueryMod {
|
func Having(clause string, args ...interface{}) QueryMod {
|
||||||
return func(q *boil.Query) {
|
return func(q *boil.Query) {
|
||||||
boil.ApplyHaving(q, clause, args...)
|
boil.AppendHaving(q, clause, args...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
100
boil/query.go
100
boil/query.go
|
@ -27,6 +27,7 @@ type Query struct {
|
||||||
from []string
|
from []string
|
||||||
joins []join
|
joins []join
|
||||||
where []where
|
where []where
|
||||||
|
in []in
|
||||||
groupBy []string
|
groupBy []string
|
||||||
orderBy []string
|
orderBy []string
|
||||||
having []having
|
having []having
|
||||||
|
@ -40,6 +41,12 @@ type where struct {
|
||||||
args []interface{}
|
args []interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type in struct {
|
||||||
|
clause string
|
||||||
|
orSeparator bool
|
||||||
|
args []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
type having struct {
|
type having struct {
|
||||||
clause string
|
clause string
|
||||||
args []interface{}
|
args []interface{}
|
||||||
|
@ -96,6 +103,11 @@ func ExecQueryAll(q *Query) (*sql.Rows, error) {
|
||||||
return q.executor.Query(qs, args...)
|
return q.executor.Query(qs, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetExecutor on the query.
|
||||||
|
func SetExecutor(q *Query, exec Executor) {
|
||||||
|
q.executor = exec
|
||||||
|
}
|
||||||
|
|
||||||
// SetSQL on the query.
|
// SetSQL on the query.
|
||||||
func SetSQL(q *Query, sql string, args ...interface{}) {
|
func SetSQL(q *Query, sql string, args ...interface{}) {
|
||||||
q.plainSQL = plainSQL{sql: sql, args: args}
|
q.plainSQL = plainSQL{sql: sql, args: args}
|
||||||
|
@ -131,33 +143,26 @@ func SetDelete(q *Query) {
|
||||||
q.delete = true
|
q.delete = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetLimit on the query.
|
||||||
|
func SetLimit(q *Query, limit int) {
|
||||||
|
q.limit = limit
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOffset on the query.
|
||||||
|
func SetOffset(q *Query, offset int) {
|
||||||
|
q.offset = offset
|
||||||
|
}
|
||||||
|
|
||||||
// SetUpdate on the query.
|
// SetUpdate on the query.
|
||||||
func SetUpdate(q *Query, cols map[string]interface{}) {
|
func SetUpdate(q *Query, cols map[string]interface{}) {
|
||||||
q.update = cols
|
q.update = cols
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetExecutor on the query.
|
|
||||||
func SetExecutor(q *Query, exec Executor) {
|
|
||||||
q.executor = exec
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendSelect on the query.
|
// AppendSelect on the query.
|
||||||
func AppendSelect(q *Query, columns ...string) {
|
func AppendSelect(q *Query, columns ...string) {
|
||||||
q.selectCols = append(q.selectCols, columns...)
|
q.selectCols = append(q.selectCols, columns...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetSelect replaces the current select clause.
|
|
||||||
func SetSelect(q *Query, columns ...string) {
|
|
||||||
q.selectCols = append([]string(nil), columns...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select returns the select columns in the query.
|
|
||||||
func Select(q *Query) []string {
|
|
||||||
cols := make([]string, len(q.selectCols))
|
|
||||||
copy(cols, q.selectCols)
|
|
||||||
return cols
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendFrom on the query.
|
// AppendFrom on the query.
|
||||||
func AppendFrom(q *Query, from ...string) {
|
func AppendFrom(q *Query, from ...string) {
|
||||||
q.from = append(q.from, from...)
|
q.from = append(q.from, from...)
|
||||||
|
@ -173,9 +178,9 @@ func AppendInnerJoin(q *Query, clause string, args ...interface{}) {
|
||||||
q.joins = append(q.joins, join{clause: clause, kind: JoinInner, args: args})
|
q.joins = append(q.joins, join{clause: clause, kind: JoinInner, args: args})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetInnerJoin on the query.
|
// AppendHaving on the query.
|
||||||
func SetInnerJoin(q *Query, clause string, args ...interface{}) {
|
func AppendHaving(q *Query, clause string, args ...interface{}) {
|
||||||
q.joins = append([]join(nil), join{clause: clause, kind: JoinInner, args: args})
|
q.having = append(q.having, having{clause: clause, args: args})
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendWhere on the query.
|
// AppendWhere on the query.
|
||||||
|
@ -183,12 +188,12 @@ func AppendWhere(q *Query, clause string, args ...interface{}) {
|
||||||
q.where = append(q.where, where{clause: clause, args: args})
|
q.where = append(q.where, where{clause: clause, args: args})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetWhere on the query.
|
// AppendIn on the query.
|
||||||
func SetWhere(q *Query, clause string, args ...interface{}) {
|
func AppendIn(q *Query, clause string, args ...interface{}) {
|
||||||
q.where = append([]where(nil), where{clause: clause, args: args})
|
q.in = append(q.in, in{clause: clause, args: args})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetLastWhereAsOr sets the or separator for the tail where in the slice
|
// SetLastWhereAsOr sets the or separator for the tail "WHERE" in the slice
|
||||||
func SetLastWhereAsOr(q *Query) {
|
func SetLastWhereAsOr(q *Query) {
|
||||||
if len(q.where) == 0 {
|
if len(q.where) == 0 {
|
||||||
return
|
return
|
||||||
|
@ -197,42 +202,21 @@ func SetLastWhereAsOr(q *Query) {
|
||||||
q.where[len(q.where)-1].orSeparator = true
|
q.where[len(q.where)-1].orSeparator = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplyGroupBy on the query.
|
// SetLastInAsOr sets the or separator for the tail "IN" in the slice
|
||||||
func ApplyGroupBy(q *Query, clause string) {
|
func SetLastInAsOr(q *Query) {
|
||||||
|
if len(q.in) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
q.in[len(q.in)-1].orSeparator = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendGroupBy on the query.
|
||||||
|
func AppendGroupBy(q *Query, clause string) {
|
||||||
q.groupBy = append(q.groupBy, clause)
|
q.groupBy = append(q.groupBy, clause)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetGroupBy on the query.
|
// AppendOrderBy on the query.
|
||||||
func SetGroupBy(q *Query, clause string) {
|
func AppendOrderBy(q *Query, clause string) {
|
||||||
q.groupBy = append([]string(nil), clause)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyOrderBy on the query.
|
|
||||||
func ApplyOrderBy(q *Query, clause string) {
|
|
||||||
q.orderBy = append(q.orderBy, clause)
|
q.orderBy = append(q.orderBy, clause)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetOrderBy on the query.
|
|
||||||
func SetOrderBy(q *Query, clause string) {
|
|
||||||
q.orderBy = append([]string(nil), clause)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyHaving on the query.
|
|
||||||
func ApplyHaving(q *Query, clause string, args ...interface{}) {
|
|
||||||
q.having = append(q.having, having{clause: clause, args: args})
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetHaving on the query.
|
|
||||||
func SetHaving(q *Query, clause string, args ...interface{}) {
|
|
||||||
q.having = append([]having(nil), having{clause: clause, args: args})
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetLimit on the query.
|
|
||||||
func SetLimit(q *Query, limit int) {
|
|
||||||
q.limit = limit
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetOffset on the query.
|
|
||||||
func SetOffset(q *Query, offset int) {
|
|
||||||
q.offset = offset
|
|
||||||
}
|
|
||||||
|
|
|
@ -83,8 +83,16 @@ func buildSelectQuery(q *Query) (*bytes.Buffer, []interface{}) {
|
||||||
|
|
||||||
where, whereArgs := whereClause(q, len(args)+1)
|
where, whereArgs := whereClause(q, len(args)+1)
|
||||||
buf.WriteString(where)
|
buf.WriteString(where)
|
||||||
|
if len(whereArgs) != 0 {
|
||||||
args = append(args, whereArgs...)
|
args = append(args, whereArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
in, inArgs := inClause(q, len(args)+1)
|
||||||
|
buf.WriteString(in)
|
||||||
|
if len(inArgs) != 0 {
|
||||||
|
args = append(args, inArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
writeModifiers(q, buf, &args)
|
writeModifiers(q, buf, &args)
|
||||||
|
|
||||||
buf.WriteByte(';')
|
buf.WriteByte(';')
|
||||||
|
@ -92,14 +100,24 @@ func buildSelectQuery(q *Query) (*bytes.Buffer, []interface{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildDeleteQuery(q *Query) (*bytes.Buffer, []interface{}) {
|
func buildDeleteQuery(q *Query) (*bytes.Buffer, []interface{}) {
|
||||||
|
var args []interface{}
|
||||||
buf := strmangle.GetBuffer()
|
buf := strmangle.GetBuffer()
|
||||||
|
|
||||||
buf.WriteString("DELETE FROM ")
|
buf.WriteString("DELETE FROM ")
|
||||||
buf.WriteString(strings.Join(strmangle.IdentQuoteSlice(q.from), ", "))
|
buf.WriteString(strings.Join(strmangle.IdentQuoteSlice(q.from), ", "))
|
||||||
|
|
||||||
where, args := whereClause(q, 1)
|
where, whereArgs := whereClause(q, 1)
|
||||||
|
if len(whereArgs) != 0 {
|
||||||
|
args = append(args, whereArgs)
|
||||||
|
}
|
||||||
buf.WriteString(where)
|
buf.WriteString(where)
|
||||||
|
|
||||||
|
in, inArgs := inClause(q, len(args)+1)
|
||||||
|
if len(inArgs) != 0 {
|
||||||
|
args = append(args, inArgs...)
|
||||||
|
}
|
||||||
|
buf.WriteString(in)
|
||||||
|
|
||||||
writeModifiers(q, buf, &args)
|
writeModifiers(q, buf, &args)
|
||||||
|
|
||||||
buf.WriteByte(';')
|
buf.WriteByte(';')
|
||||||
|
@ -136,9 +154,17 @@ func buildUpdateQuery(q *Query) (*bytes.Buffer, []interface{}) {
|
||||||
)
|
)
|
||||||
|
|
||||||
where, whereArgs := whereClause(q, len(args)+1)
|
where, whereArgs := whereClause(q, len(args)+1)
|
||||||
|
if len(whereArgs) != 0 {
|
||||||
|
args = append(args, whereArgs...)
|
||||||
|
}
|
||||||
buf.WriteString(where)
|
buf.WriteString(where)
|
||||||
|
|
||||||
args = append(args, whereArgs...)
|
in, inArgs := inClause(q, len(args)+1)
|
||||||
|
if len(inArgs) != 0 {
|
||||||
|
args = append(args, inArgs...)
|
||||||
|
}
|
||||||
|
buf.WriteString(in)
|
||||||
|
|
||||||
writeModifiers(q, buf, &args)
|
writeModifiers(q, buf, &args)
|
||||||
|
|
||||||
buf.WriteByte(';')
|
buf.WriteByte(';')
|
||||||
|
@ -260,6 +286,39 @@ func whereClause(q *Query, startAt int) (string, []interface{}) {
|
||||||
return convertQuestionMarks(buf.String(), startAt), args
|
return convertQuestionMarks(buf.String(), startAt), args
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func inClause(q *Query, startAt int) (string, []interface{}) {
|
||||||
|
if len(q.in) == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := strmangle.GetBuffer()
|
||||||
|
defer strmangle.PutBuffer(buf)
|
||||||
|
var args []interface{}
|
||||||
|
|
||||||
|
if len(q.where) == 0 {
|
||||||
|
buf.WriteString(" WHERE ")
|
||||||
|
}
|
||||||
|
for i := 0; i < len(q.in); i++ {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertInQuestionMarks(clause string, total, groupAt, startAt int) string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
if startAt == 0 {
|
if startAt == 0 {
|
||||||
panic("Not a valid start number.")
|
panic("Not a valid start number.")
|
||||||
|
|
|
@ -334,6 +334,138 @@ func TestWhereClause(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInClause(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
q Query
|
||||||
|
expect string
|
||||||
|
}{
|
||||||
|
// Or("a=?")
|
||||||
|
{
|
||||||
|
q: Query{
|
||||||
|
in: []in{{clause: "a in ?", args: []interface{}{1}, orSeparator: true}},
|
||||||
|
},
|
||||||
|
expect: " WHERE a IN ($1)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
q: Query{
|
||||||
|
in: []in{{clause: "a in ?", args: []interface{}{1, 2, 3}}},
|
||||||
|
},
|
||||||
|
expect: " WHERE a IN ($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)
|
||||||
|
if result != test.expect {
|
||||||
|
t.Errorf("%d) Mismatch between expect and result:\n%s\n%s\n", i, test.expect, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestConvertQuestionMarks(t *testing.T) {
|
func TestConvertQuestionMarks(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
@ -373,6 +505,45 @@ func TestConvertQuestionMarks(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConvertInQuestionMarks(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
clause string
|
||||||
|
start 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?`},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
res := 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestWriteAsStatements(t *testing.T) {
|
func TestWriteAsStatements(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ func TestSetSQL(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWhere(t *testing.T) {
|
func TestAppendWhere(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
q := &Query{}
|
q := &Query{}
|
||||||
|
@ -69,7 +69,7 @@ func TestWhere(t *testing.T) {
|
||||||
t.Errorf("args wrong: %#v", q.where)
|
t.Errorf("args wrong: %#v", q.where)
|
||||||
}
|
}
|
||||||
|
|
||||||
SetWhere(q, expect, 5, 3)
|
q.where = []where{{clause: expect, args: []interface{}{5, 3}}}
|
||||||
if q.where[0].clause != expect {
|
if q.where[0].clause != expect {
|
||||||
t.Errorf("Expected %s, got %v", expect, q.where)
|
t.Errorf("Expected %s, got %v", expect, q.where)
|
||||||
}
|
}
|
||||||
|
@ -120,49 +120,124 @@ func TestSetLastWhereAsOr(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGroupBy(t *testing.T) {
|
func TestAppendIn(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
q := &Query{}
|
||||||
|
expect := "col IN ?"
|
||||||
|
AppendIn(q, expect, 5, 3)
|
||||||
|
AppendIn(q, expect, 5, 3)
|
||||||
|
|
||||||
|
if len(q.in) != 2 {
|
||||||
|
t.Errorf("%#v", q.in)
|
||||||
|
}
|
||||||
|
|
||||||
|
if q.in[0].clause != expect || q.in[1].clause != expect {
|
||||||
|
t.Errorf("Expected %s, got %#v", expect, q.in)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(q.in[0].args) != 2 || len(q.in[0].args) != 2 {
|
||||||
|
t.Errorf("arg length wrong: %#v", q.in)
|
||||||
|
}
|
||||||
|
|
||||||
|
if q.in[0].args[0].(int) != 5 || q.in[0].args[1].(int) != 3 {
|
||||||
|
t.Errorf("args wrong: %#v", q.in)
|
||||||
|
}
|
||||||
|
|
||||||
|
q.in = []in{{clause: expect, args: []interface{}{5, 3}}}
|
||||||
|
if q.in[0].clause != expect {
|
||||||
|
t.Errorf("Expected %s, got %v", expect, q.in)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(q.in[0].args) != 2 {
|
||||||
|
t.Errorf("Expected %d args, got %d", 2, len(q.in[0].args))
|
||||||
|
}
|
||||||
|
|
||||||
|
if q.in[0].args[0].(int) != 5 || q.in[0].args[1].(int) != 3 {
|
||||||
|
t.Errorf("Args not set correctly, expected 5 & 3, got: %#v", q.in[0].args)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(q.in) != 1 {
|
||||||
|
t.Errorf("%#v", q.in)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetLastInAsOr(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
q := &Query{}
|
||||||
|
|
||||||
|
AppendIn(q, "")
|
||||||
|
|
||||||
|
if q.in[0].orSeparator {
|
||||||
|
t.Errorf("Do not want or separator")
|
||||||
|
}
|
||||||
|
|
||||||
|
SetLastInAsOr(q)
|
||||||
|
|
||||||
|
if len(q.in) != 1 {
|
||||||
|
t.Errorf("Want len 1")
|
||||||
|
}
|
||||||
|
if !q.in[0].orSeparator {
|
||||||
|
t.Errorf("Want or separator")
|
||||||
|
}
|
||||||
|
|
||||||
|
AppendIn(q, "")
|
||||||
|
SetLastInAsOr(q)
|
||||||
|
|
||||||
|
if len(q.in) != 2 {
|
||||||
|
t.Errorf("Want len 2")
|
||||||
|
}
|
||||||
|
if q.in[0].orSeparator != true {
|
||||||
|
t.Errorf("Expected true")
|
||||||
|
}
|
||||||
|
if q.in[1].orSeparator != true {
|
||||||
|
t.Errorf("Expected true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAppendGroupBy(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
q := &Query{}
|
q := &Query{}
|
||||||
expect := "col1, col2"
|
expect := "col1, col2"
|
||||||
ApplyGroupBy(q, expect)
|
AppendGroupBy(q, expect)
|
||||||
ApplyGroupBy(q, expect)
|
AppendGroupBy(q, expect)
|
||||||
|
|
||||||
if len(q.groupBy) != 2 && (q.groupBy[0] != expect || q.groupBy[1] != expect) {
|
if len(q.groupBy) != 2 && (q.groupBy[0] != expect || q.groupBy[1] != expect) {
|
||||||
t.Errorf("Expected %s, got %s %s", expect, q.groupBy[0], q.groupBy[1])
|
t.Errorf("Expected %s, got %s %s", expect, q.groupBy[0], q.groupBy[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
SetGroupBy(q, expect)
|
q.groupBy = []string{expect}
|
||||||
if len(q.groupBy) != 1 && q.groupBy[0] != expect {
|
if len(q.groupBy) != 1 && q.groupBy[0] != expect {
|
||||||
t.Errorf("Expected %s, got %s", expect, q.groupBy[0])
|
t.Errorf("Expected %s, got %s", expect, q.groupBy[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOrderBy(t *testing.T) {
|
func TestAppendOrderBy(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
q := &Query{}
|
q := &Query{}
|
||||||
expect := "col1 desc, col2 asc"
|
expect := "col1 desc, col2 asc"
|
||||||
ApplyOrderBy(q, expect)
|
AppendOrderBy(q, expect)
|
||||||
ApplyOrderBy(q, expect)
|
AppendOrderBy(q, expect)
|
||||||
|
|
||||||
if len(q.orderBy) != 2 && (q.orderBy[0] != expect || q.orderBy[1] != expect) {
|
if len(q.orderBy) != 2 && (q.orderBy[0] != expect || q.orderBy[1] != expect) {
|
||||||
t.Errorf("Expected %s, got %s %s", expect, q.orderBy[0], q.orderBy[1])
|
t.Errorf("Expected %s, got %s %s", expect, q.orderBy[0], q.orderBy[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
SetOrderBy(q, "col1 desc, col2 asc")
|
q.orderBy = []string{"col1 desc, col2 asc"}
|
||||||
if len(q.orderBy) != 1 && q.orderBy[0] != expect {
|
if len(q.orderBy) != 1 && q.orderBy[0] != expect {
|
||||||
t.Errorf("Expected %s, got %s", expect, q.orderBy[0])
|
t.Errorf("Expected %s, got %s", expect, q.orderBy[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHaving(t *testing.T) {
|
func TestAppendHaving(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
q := &Query{}
|
q := &Query{}
|
||||||
expect := "count(orders.order_id) > ?"
|
expect := "count(orders.order_id) > ?"
|
||||||
ApplyHaving(q, expect, 10)
|
AppendHaving(q, expect, 10)
|
||||||
ApplyHaving(q, expect, 10)
|
AppendHaving(q, expect, 10)
|
||||||
|
|
||||||
if len(q.having) != 2 {
|
if len(q.having) != 2 {
|
||||||
t.Errorf("Expected 2, got %d", len(q.having))
|
t.Errorf("Expected 2, got %d", len(q.having))
|
||||||
|
@ -176,7 +251,7 @@ func TestHaving(t *testing.T) {
|
||||||
t.Errorf("Expected %v, got %v %v", 10, q.having[0].args[0], q.having[1].args[0])
|
t.Errorf("Expected %v, got %v %v", 10, q.having[0].args[0], q.having[1].args[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
SetHaving(q, expect, 10)
|
q.having = []having{{clause: expect, args: []interface{}{10}}}
|
||||||
if len(q.having) != 1 && (q.having[0].clause != expect || q.having[0].args[0] != 10) {
|
if len(q.having) != 1 && (q.having[0].clause != expect || q.having[0].args[0] != 10) {
|
||||||
t.Errorf("Expected %s, got %s %v", expect, q.having[0], q.having[0].args[0])
|
t.Errorf("Expected %s, got %s %v", expect, q.having[0], q.having[0].args[0])
|
||||||
}
|
}
|
||||||
|
@ -307,24 +382,12 @@ func TestAppendSelect(t *testing.T) {
|
||||||
t.Errorf("select cols value mismatch: %#v", q.selectCols)
|
t.Errorf("select cols value mismatch: %#v", q.selectCols)
|
||||||
}
|
}
|
||||||
|
|
||||||
SetSelect(q, "col1", "col2")
|
q.selectCols = []string{"col1", "col2"}
|
||||||
if q.selectCols[0] != `col1` && q.selectCols[1] != `col2` {
|
if q.selectCols[0] != `col1` && q.selectCols[1] != `col2` {
|
||||||
t.Errorf("select cols value mismatch: %#v", q.selectCols)
|
t.Errorf("select cols value mismatch: %#v", q.selectCols)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSelect(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
q := &Query{}
|
|
||||||
q.selectCols = []string{"one"}
|
|
||||||
|
|
||||||
ret := Select(q)
|
|
||||||
if ret[0] != "one" {
|
|
||||||
t.Errorf("Expected %q, got %s", "one", ret[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSQL(t *testing.T) {
|
func TestSQL(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
@ -337,7 +400,7 @@ func TestSQL(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInnerJoin(t *testing.T) {
|
func TestAppendInnerJoin(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
q := &Query{}
|
q := &Query{}
|
||||||
|
@ -366,7 +429,11 @@ func TestInnerJoin(t *testing.T) {
|
||||||
t.Errorf("Invalid args values, got %#v", q.joins[0].args)
|
t.Errorf("Invalid args values, got %#v", q.joins[0].args)
|
||||||
}
|
}
|
||||||
|
|
||||||
SetInnerJoin(q, "thing=$1 AND stuff=$2", 2, 5)
|
q.joins = []join{{kind: JoinInner,
|
||||||
|
clause: "thing=$1 AND stuff=$2",
|
||||||
|
args: []interface{}{2, 5},
|
||||||
|
}}
|
||||||
|
|
||||||
if len(q.joins) != 1 {
|
if len(q.joins) != 1 {
|
||||||
t.Errorf("Expected len 1, got %d", len(q.joins))
|
t.Errorf("Expected len 1, got %d", len(q.joins))
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,15 @@ var (
|
||||||
smartQuoteRgx = regexp.MustCompile(`^(?i)"?[a-z_][_a-z0-9]*"?(\."?[_a-z][_a-z0-9]*"?)*(\.\*)?$`)
|
smartQuoteRgx = regexp.MustCompile(`^(?i)"?[a-z_][_a-z0-9]*"?(\."?[_a-z][_a-z0-9]*"?)*(\.\*)?$`)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Override the uncountable inflections with an empty set.
|
||||||
|
// This way, people using words like Sheep will not have
|
||||||
|
// collisions with their model name (Sheep) and their
|
||||||
|
// function name (Sheep()). Instead, it will
|
||||||
|
// use the regular inflection rules: Sheep, Sheeps().
|
||||||
|
inflection.SetUncountable([]string{})
|
||||||
|
}
|
||||||
|
|
||||||
// IdentQuote attempts to quote simple identifiers in SQL tatements
|
// IdentQuote attempts to quote simple identifiers in SQL tatements
|
||||||
func IdentQuote(s string) string {
|
func IdentQuote(s string) string {
|
||||||
if strings.ToLower(s) == "null" {
|
if strings.ToLower(s) == "null" {
|
||||||
|
|
Loading…
Reference in a new issue