dbr/where.go

105 lines
2.6 KiB
Go

package dbr
import (
"bytes"
"reflect"
)
// Eq is a map column -> value pairs which must be matched in a query
type Eq map[string]interface{}
type whereFragment struct {
Condition string
Values []interface{}
EqualityMap map[string]interface{}
}
func newWhereFragment(whereSqlOrMap interface{}, args []interface{}) *whereFragment {
switch pred := whereSqlOrMap.(type) {
case string:
return &whereFragment{Condition: pred, Values: args}
case map[string]interface{}:
return &whereFragment{EqualityMap: pred}
case Eq:
return &whereFragment{EqualityMap: map[string]interface{}(pred)}
default:
panic("Invalid argument passed to Where. Pass a string or an Eq map.")
}
return nil
}
// Invariant: only called when len(fragments) > 0
func writeWhereFragmentsToSql(fragments []*whereFragment, sql *bytes.Buffer, args *[]interface{}) {
anyConditions := false
for _, f := range fragments {
if f.Condition != "" {
if anyConditions {
sql.WriteString(" AND (")
} else {
sql.WriteRune('(')
anyConditions = true
}
sql.WriteString(f.Condition)
sql.WriteRune(')')
if len(f.Values) > 0 {
*args = append(*args, f.Values...)
}
} else if f.EqualityMap != nil {
anyConditions = writeEqualityMapToSql(f.EqualityMap, sql, args, anyConditions)
} else {
panic("invalid equality map")
}
}
}
func writeEqualityMapToSql(eq map[string]interface{}, sql *bytes.Buffer, args *[]interface{}, anyConditions bool) bool {
for k, v := range eq {
if v == nil {
anyConditions = writeWhereCondition(sql, k, " IS NULL", anyConditions)
} else {
vVal := reflect.ValueOf(v)
if vVal.Kind() == reflect.Array || vVal.Kind() == reflect.Slice {
vValLen := vVal.Len()
if vValLen == 0 {
if vVal.IsNil() {
anyConditions = writeWhereCondition(sql, k, " IS NULL", anyConditions)
} else {
if anyConditions {
sql.WriteString(" AND (1=0)")
} else {
sql.WriteString("(1=0)")
}
}
} else if vValLen == 1 {
anyConditions = writeWhereCondition(sql, k, " = ?", anyConditions)
*args = append(*args, vVal.Index(0).Interface())
} else {
anyConditions = writeWhereCondition(sql, k, " IN ?", anyConditions)
*args = append(*args, v)
}
} else {
anyConditions = writeWhereCondition(sql, k, " = ?", anyConditions)
*args = append(*args, v)
}
}
}
return anyConditions
}
func writeWhereCondition(sql *bytes.Buffer, k string, pred string, anyConditions bool) bool {
if anyConditions {
sql.WriteString(" AND (")
} else {
sql.WriteRune('(')
anyConditions = true
}
Quoter.writeQuotedColumn(k, sql)
sql.WriteString(pred)
sql.WriteRune(')')
return anyConditions
}