Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ func (e expr) ToSql() (sql string, args []interface{}, err error) {
for _, arg := range e.args {
if _, ok := arg.(Sqlizer); ok {
simple = false
break
}
if isListType(arg) {
// Slice/array args need placeholder expansion (e.g. "id IN ?" + []int{...}).
simple = false
break
}
}
if simple {
Expand Down Expand Up @@ -65,6 +71,24 @@ func (e expr) ToSql() (sql string, args []interface{}, err error) {
buf.WriteString(sp[:i])
buf.WriteString(isql)
args = append(args, iargs...)
} else if isListType(ap[0]) {
// list argument; expand "?" into "(?,?,...)" like Eq/NotEq helpers
valVal := reflect.ValueOf(ap[0])
n := valVal.Len()
buf.WriteString(sp[:i])
if n == 0 {
// empty IN lists are falsey; empty NOT IN lists are truey, but
// Expr cannot know the surrounding operator. Expand to no
// placeholders: "()" matches historical Placeholders(0).
buf.WriteString("()")
} else {
buf.WriteByte('(')
buf.WriteString(Placeholders(n))
buf.WriteByte(')')
for j := 0; j < n; j++ {
args = append(args, valVal.Index(j).Interface())
}
}
} else {
// normal argument; append it and the placeholder
buf.WriteString(sp[:i+1])
Expand Down
20 changes: 20 additions & 0 deletions expr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -462,3 +462,23 @@ func ExampleEq() {
"company": 20,
})
}


func TestExprListArgExpansion(t *testing.T) {
sql, args, err := Expr("id NOT IN ?", []int{1, 2, 3}).ToSql()
assert.NoError(t, err)
assert.Equal(t, "id NOT IN (?,?,?)", sql)
assert.Equal(t, []interface{}{1, 2, 3}, args)

sql, args, err = Expr("id IN ?", []string{}).ToSql()
assert.NoError(t, err)
assert.Equal(t, "id IN ()", sql)
assert.Empty(t, args)
}

func TestWhereRawSQLListArgExpansion(t *testing.T) {
sql, args, err := Select("something FROM somewhere").Where("id NOT IN ?", []int{1, 2, 3}).ToSql()
assert.NoError(t, err)
assert.Equal(t, "SELECT something FROM somewhere WHERE id NOT IN (?,?,?)", sql)
assert.Equal(t, []interface{}{1, 2, 3}, args)
}
4 changes: 2 additions & 2 deletions part.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ func (p part) ToSql() (sql string, args []interface{}, err error) {
case Sqlizer:
sql, args, err = nestedToSql(pred)
case string:
sql = pred
args = p.args
// Route through Expr so slice/array args expand placeholders.
return Expr(pred, p.args...).ToSql()
default:
err = fmt.Errorf("expected string or Sqlizer, not %T", pred)
}
Expand Down
4 changes: 2 additions & 2 deletions where.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ func (p wherePart) ToSql() (sql string, args []interface{}, err error) {
case map[string]interface{}:
return Eq(pred).ToSql()
case string:
sql = pred
args = p.args
// Route through Expr so slice/array args expand placeholders (e.g. IN ?).
return Expr(pred, p.args...).ToSql()
default:
err = fmt.Errorf("expected string-keyed map or string, not %T", pred)
}
Expand Down