2019-03-15 22:02:59 +01:00
|
|
|
package sqlbuilder
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"github.com/dropbox/godropbox/database/sqltypes"
|
|
|
|
|
"github.com/dropbox/godropbox/errors"
|
|
|
|
|
"reflect"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
2019-03-31 09:17:28 +02:00
|
|
|
type BoolExpression interface {
|
|
|
|
|
Expression
|
|
|
|
|
|
2019-03-31 14:07:58 +02:00
|
|
|
Eq(expression BoolExpression) BoolExpression
|
|
|
|
|
NotEq(expression BoolExpression) BoolExpression
|
|
|
|
|
GtEq(rhs Expression) BoolExpression
|
|
|
|
|
LtEq(rhs Expression) BoolExpression
|
|
|
|
|
|
2019-03-31 09:17:28 +02:00
|
|
|
And(expression BoolExpression) BoolExpression
|
|
|
|
|
Or(expression BoolExpression) BoolExpression
|
|
|
|
|
IsTrue() BoolExpression
|
|
|
|
|
IsFalse() BoolExpression
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type boolInterfaceImpl struct {
|
|
|
|
|
parent BoolExpression
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-31 14:07:58 +02:00
|
|
|
func (b *boolInterfaceImpl) Eq(expression BoolExpression) BoolExpression {
|
|
|
|
|
return Eq(b.parent, expression)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (b *boolInterfaceImpl) NotEq(expression BoolExpression) BoolExpression {
|
2019-04-03 11:03:07 +02:00
|
|
|
return NotEq(b.parent, expression)
|
2019-03-31 14:07:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (b *boolInterfaceImpl) GtEq(rhs Expression) BoolExpression {
|
2019-04-03 11:03:07 +02:00
|
|
|
return GtEq(b.parent, rhs)
|
2019-03-31 14:07:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (b *boolInterfaceImpl) LtEq(rhs Expression) BoolExpression {
|
2019-04-03 11:03:07 +02:00
|
|
|
return LtEq(b.parent, rhs)
|
2019-03-31 14:07:58 +02:00
|
|
|
}
|
|
|
|
|
|
2019-03-31 09:17:28 +02:00
|
|
|
func (b *boolInterfaceImpl) And(expression BoolExpression) BoolExpression {
|
|
|
|
|
return And(b.parent, expression)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (b *boolInterfaceImpl) Or(expression BoolExpression) BoolExpression {
|
|
|
|
|
return Or(b.parent, expression)
|
|
|
|
|
}
|
|
|
|
|
func (b *boolInterfaceImpl) IsTrue() BoolExpression {
|
|
|
|
|
return IsTrue(b.parent)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (b *boolInterfaceImpl) IsFalse() BoolExpression {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------//
|
|
|
|
|
type boolLiteralExpression struct {
|
|
|
|
|
boolInterfaceImpl
|
|
|
|
|
literalExpression
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-31 14:07:58 +02:00
|
|
|
func newBoolLiteralExpression(value bool) BoolExpression {
|
2019-03-31 09:17:28 +02:00
|
|
|
boolLiteralExpression := boolLiteralExpression{}
|
|
|
|
|
|
|
|
|
|
sqlValue, err := sqltypes.BuildValue(value)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(errors.Wrap(err, "Invalid literal value"))
|
|
|
|
|
}
|
|
|
|
|
boolLiteralExpression.literalExpression = *NewLiteralExpression(sqlValue)
|
|
|
|
|
boolLiteralExpression.boolInterfaceImpl.parent = &boolLiteralExpression
|
|
|
|
|
|
|
|
|
|
return &boolLiteralExpression
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------//
|
|
|
|
|
type binaryBoolExpression struct {
|
2019-03-31 14:07:58 +02:00
|
|
|
expressionInterfaceImpl
|
2019-03-31 09:17:28 +02:00
|
|
|
boolInterfaceImpl
|
|
|
|
|
|
|
|
|
|
binaryExpression
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-31 14:07:58 +02:00
|
|
|
func newBinaryBoolExpression(lhs, rhs Expression, operator []byte) BoolExpression {
|
2019-03-31 09:17:28 +02:00
|
|
|
boolExpression := binaryBoolExpression{}
|
|
|
|
|
|
2019-03-31 14:07:58 +02:00
|
|
|
boolExpression.binaryExpression = newBinaryExpression(lhs, rhs, operator)
|
|
|
|
|
boolExpression.expressionInterfaceImpl.parent = &boolExpression
|
2019-03-31 09:17:28 +02:00
|
|
|
boolExpression.boolInterfaceImpl.parent = &boolExpression
|
|
|
|
|
|
|
|
|
|
return &boolExpression
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------//
|
|
|
|
|
type prefixBoolExpression struct {
|
2019-03-31 14:07:58 +02:00
|
|
|
expressionInterfaceImpl
|
2019-03-31 09:17:28 +02:00
|
|
|
boolInterfaceImpl
|
|
|
|
|
|
|
|
|
|
prefixExpression
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-31 14:07:58 +02:00
|
|
|
func newPrefixBoolExpression(expression Expression, operator []byte) BoolExpression {
|
2019-03-31 09:17:28 +02:00
|
|
|
boolExpression := prefixBoolExpression{}
|
2019-03-31 14:07:58 +02:00
|
|
|
boolExpression.prefixExpression = newPrefixExpression(expression, operator)
|
2019-03-31 09:17:28 +02:00
|
|
|
|
2019-03-31 14:07:58 +02:00
|
|
|
boolExpression.expressionInterfaceImpl.parent = &boolExpression
|
2019-03-31 09:17:28 +02:00
|
|
|
boolExpression.boolInterfaceImpl.parent = &boolExpression
|
|
|
|
|
|
|
|
|
|
return &boolExpression
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------//
|
|
|
|
|
type conjunctBoolExpression struct {
|
2019-03-31 14:07:58 +02:00
|
|
|
expressionInterfaceImpl
|
2019-03-31 09:17:28 +02:00
|
|
|
boolInterfaceImpl
|
|
|
|
|
|
|
|
|
|
conjunctExpression
|
|
|
|
|
name string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewConjunctBoolExpression(operator []byte, expressions ...BoolExpression) BoolExpression {
|
|
|
|
|
boolExpression := conjunctBoolExpression{
|
|
|
|
|
conjunctExpression: conjunctExpression{
|
|
|
|
|
expressions: expressions,
|
|
|
|
|
conjunction: operator,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-31 14:07:58 +02:00
|
|
|
boolExpression.expressionInterfaceImpl.parent = &boolExpression
|
|
|
|
|
boolExpression.boolInterfaceImpl.parent = &boolExpression
|
2019-03-31 09:17:28 +02:00
|
|
|
|
|
|
|
|
return &boolExpression
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------//
|
|
|
|
|
type inExpression struct {
|
|
|
|
|
expressionInterfaceImpl
|
|
|
|
|
boolInterfaceImpl
|
|
|
|
|
|
|
|
|
|
lhs Expression
|
|
|
|
|
rhs *listClause
|
|
|
|
|
|
|
|
|
|
err error
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-31 14:07:58 +02:00
|
|
|
func (c *inExpression) SerializeSql(out *bytes.Buffer, options ...serializeOption) error {
|
2019-03-31 09:17:28 +02:00
|
|
|
if c.err != nil {
|
|
|
|
|
return errors.Wrap(c.err, "Invalid IN expression")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if c.lhs == nil {
|
|
|
|
|
return errors.Newf(
|
|
|
|
|
"lhs of in expression is nil. Generated sql: %s",
|
|
|
|
|
out.String())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We'll serialize the lhs even if we don't need it to ensure no error
|
|
|
|
|
buf := &bytes.Buffer{}
|
|
|
|
|
|
|
|
|
|
err := c.lhs.SerializeSql(buf)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if c.rhs == nil {
|
|
|
|
|
_, _ = out.WriteString("FALSE")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, _ = out.WriteString(buf.String())
|
|
|
|
|
_, _ = out.WriteString(" IN ")
|
|
|
|
|
|
|
|
|
|
err = c.rhs.SerializeSql(out)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-15 22:02:59 +01:00
|
|
|
// Returns a representation of "a=b"
|
|
|
|
|
func Eq(lhs, rhs Expression) BoolExpression {
|
|
|
|
|
lit, ok := rhs.(*literalExpression)
|
|
|
|
|
if ok && sqltypes.Value(lit.value).IsNull() {
|
2019-03-31 14:07:58 +02:00
|
|
|
return newBinaryBoolExpression(lhs, rhs, []byte(" IS "))
|
2019-03-15 22:02:59 +01:00
|
|
|
}
|
2019-03-31 14:07:58 +02:00
|
|
|
return newBinaryBoolExpression(lhs, rhs, []byte(" = "))
|
2019-03-15 22:02:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns a representation of "a=b", where b is a literal
|
|
|
|
|
func EqL(lhs Expression, val interface{}) BoolExpression {
|
|
|
|
|
return Eq(lhs, Literal(val))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns a representation of "a!=b"
|
2019-04-03 11:03:07 +02:00
|
|
|
func NotEq(lhs, rhs Expression) BoolExpression {
|
2019-03-15 22:02:59 +01:00
|
|
|
lit, ok := rhs.(*literalExpression)
|
|
|
|
|
if ok && sqltypes.Value(lit.value).IsNull() {
|
2019-03-31 14:07:58 +02:00
|
|
|
return newBinaryBoolExpression(lhs, rhs, []byte(" IS NOT "))
|
2019-03-15 22:02:59 +01:00
|
|
|
}
|
2019-03-31 14:07:58 +02:00
|
|
|
return newBinaryBoolExpression(lhs, rhs, []byte("!="))
|
2019-03-15 22:02:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns a representation of "a!=b", where b is a literal
|
|
|
|
|
func NeqL(lhs Expression, val interface{}) BoolExpression {
|
2019-04-03 11:03:07 +02:00
|
|
|
return NotEq(lhs, Literal(val))
|
2019-03-15 22:02:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns a representation of "a<b"
|
|
|
|
|
func Lt(lhs Expression, rhs Expression) BoolExpression {
|
2019-03-31 14:07:58 +02:00
|
|
|
return newBinaryBoolExpression(lhs, rhs, []byte("<"))
|
2019-03-15 22:02:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns a representation of "a<b", where b is a literal
|
|
|
|
|
func LtL(lhs Expression, val interface{}) BoolExpression {
|
|
|
|
|
return Lt(lhs, Literal(val))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns a representation of "a<=b"
|
2019-04-03 11:03:07 +02:00
|
|
|
func LtEq(lhs, rhs Expression) BoolExpression {
|
2019-03-31 14:07:58 +02:00
|
|
|
return newBinaryBoolExpression(lhs, rhs, []byte("<="))
|
2019-03-15 22:02:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns a representation of "a<=b", where b is a literal
|
|
|
|
|
func LteL(lhs Expression, val interface{}) BoolExpression {
|
2019-04-03 11:03:07 +02:00
|
|
|
return LtEq(lhs, Literal(val))
|
2019-03-15 22:02:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns a representation of "a>b"
|
|
|
|
|
func Gt(lhs, rhs Expression) BoolExpression {
|
2019-03-31 14:07:58 +02:00
|
|
|
return newBinaryBoolExpression(lhs, rhs, []byte(">"))
|
2019-03-15 22:02:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns a representation of "a>b", where b is a literal
|
|
|
|
|
func GtL(lhs Expression, val interface{}) BoolExpression {
|
|
|
|
|
return Gt(lhs, Literal(val))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns a representation of "a>=b"
|
2019-04-03 11:03:07 +02:00
|
|
|
func GtEq(lhs, rhs Expression) BoolExpression {
|
2019-03-31 14:07:58 +02:00
|
|
|
return newBinaryBoolExpression(lhs, rhs, []byte(">="))
|
2019-03-15 22:02:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns a representation of "a>=b", where b is a literal
|
|
|
|
|
func GteL(lhs Expression, val interface{}) BoolExpression {
|
2019-04-03 11:03:07 +02:00
|
|
|
return GtEq(lhs, Literal(val))
|
2019-03-15 22:02:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns a representation of "not expr"
|
|
|
|
|
func Not(expr BoolExpression) BoolExpression {
|
2019-03-31 14:07:58 +02:00
|
|
|
return newPrefixBoolExpression(expr, []byte(" NOT "))
|
2019-03-31 09:17:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func IsTrue(expr BoolExpression) BoolExpression {
|
2019-03-31 14:07:58 +02:00
|
|
|
return newPrefixBoolExpression(expr, []byte(" IS TRUE "))
|
2019-03-15 22:02:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns a representation of "c[0] AND ... AND c[n-1]" for c in clauses
|
|
|
|
|
func And(expressions ...BoolExpression) BoolExpression {
|
2019-03-31 09:17:28 +02:00
|
|
|
return NewConjunctBoolExpression([]byte(" AND "), expressions...)
|
2019-03-15 22:02:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns a representation of "c[0] OR ... OR c[n-1]" for c in clauses
|
|
|
|
|
func Or(expressions ...BoolExpression) BoolExpression {
|
2019-03-31 09:17:28 +02:00
|
|
|
return NewConjunctBoolExpression([]byte(" OR "), expressions...)
|
2019-03-15 22:02:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Like(lhs, rhs Expression) BoolExpression {
|
2019-03-31 14:07:58 +02:00
|
|
|
return newBinaryBoolExpression(lhs, rhs, []byte(" LIKE "))
|
2019-03-15 22:02:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func LikeL(lhs Expression, val string) BoolExpression {
|
|
|
|
|
return Like(lhs, Literal(val))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Regexp(lhs, rhs Expression) BoolExpression {
|
2019-03-31 14:07:58 +02:00
|
|
|
return newBinaryBoolExpression(lhs, rhs, []byte(" REGEXP "))
|
2019-03-15 22:02:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func RegexpL(lhs Expression, val string) BoolExpression {
|
|
|
|
|
return Regexp(lhs, Literal(val))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns a representation of "a IN (b[0], ..., b[n-1])", where b is a list
|
|
|
|
|
// of literals valList must be a slice type
|
|
|
|
|
func In(lhs Expression, valList interface{}) BoolExpression {
|
|
|
|
|
var clauses []Clause
|
|
|
|
|
switch val := valList.(type) {
|
|
|
|
|
// This atrocious body of copy-paste code is due to the fact that if you
|
|
|
|
|
// try to merge the cases, you can't treat val as a list
|
|
|
|
|
case []int:
|
|
|
|
|
clauses = make([]Clause, 0, len(val))
|
|
|
|
|
for _, v := range val {
|
|
|
|
|
clauses = append(clauses, Literal(v))
|
|
|
|
|
}
|
|
|
|
|
case []int32:
|
|
|
|
|
clauses = make([]Clause, 0, len(val))
|
|
|
|
|
for _, v := range val {
|
|
|
|
|
clauses = append(clauses, Literal(v))
|
|
|
|
|
}
|
|
|
|
|
case []int64:
|
|
|
|
|
clauses = make([]Clause, 0, len(val))
|
|
|
|
|
for _, v := range val {
|
|
|
|
|
clauses = append(clauses, Literal(v))
|
|
|
|
|
}
|
|
|
|
|
case []uint:
|
|
|
|
|
clauses = make([]Clause, 0, len(val))
|
|
|
|
|
for _, v := range val {
|
|
|
|
|
clauses = append(clauses, Literal(v))
|
|
|
|
|
}
|
|
|
|
|
case []uint32:
|
|
|
|
|
clauses = make([]Clause, 0, len(val))
|
|
|
|
|
for _, v := range val {
|
|
|
|
|
clauses = append(clauses, Literal(v))
|
|
|
|
|
}
|
|
|
|
|
case []uint64:
|
|
|
|
|
clauses = make([]Clause, 0, len(val))
|
|
|
|
|
for _, v := range val {
|
|
|
|
|
clauses = append(clauses, Literal(v))
|
|
|
|
|
}
|
|
|
|
|
case []float64:
|
|
|
|
|
clauses = make([]Clause, 0, len(val))
|
|
|
|
|
for _, v := range val {
|
|
|
|
|
clauses = append(clauses, Literal(v))
|
|
|
|
|
}
|
|
|
|
|
case []string:
|
|
|
|
|
clauses = make([]Clause, 0, len(val))
|
|
|
|
|
for _, v := range val {
|
|
|
|
|
clauses = append(clauses, Literal(v))
|
|
|
|
|
}
|
|
|
|
|
case [][]byte:
|
|
|
|
|
clauses = make([]Clause, 0, len(val))
|
|
|
|
|
for _, v := range val {
|
|
|
|
|
clauses = append(clauses, Literal(v))
|
|
|
|
|
}
|
|
|
|
|
case []time.Time:
|
|
|
|
|
clauses = make([]Clause, 0, len(val))
|
|
|
|
|
for _, v := range val {
|
|
|
|
|
clauses = append(clauses, Literal(v))
|
|
|
|
|
}
|
|
|
|
|
case []sqltypes.Numeric:
|
|
|
|
|
clauses = make([]Clause, 0, len(val))
|
|
|
|
|
for _, v := range val {
|
|
|
|
|
clauses = append(clauses, Literal(v))
|
|
|
|
|
}
|
|
|
|
|
case []sqltypes.Fractional:
|
|
|
|
|
clauses = make([]Clause, 0, len(val))
|
|
|
|
|
for _, v := range val {
|
|
|
|
|
clauses = append(clauses, Literal(v))
|
|
|
|
|
}
|
|
|
|
|
case []sqltypes.String:
|
|
|
|
|
clauses = make([]Clause, 0, len(val))
|
|
|
|
|
for _, v := range val {
|
|
|
|
|
clauses = append(clauses, Literal(v))
|
|
|
|
|
}
|
|
|
|
|
case []sqltypes.Value:
|
|
|
|
|
clauses = make([]Clause, 0, len(val))
|
|
|
|
|
for _, v := range val {
|
|
|
|
|
clauses = append(clauses, Literal(v))
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
return &inExpression{
|
|
|
|
|
err: errors.Newf(
|
|
|
|
|
"Unknown value list type in IN clause: %s",
|
|
|
|
|
reflect.TypeOf(valList)),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
expr := &inExpression{lhs: lhs}
|
|
|
|
|
if len(clauses) > 0 {
|
|
|
|
|
expr.rhs = &listClause{clauses: clauses, includeParentheses: true}
|
|
|
|
|
}
|
|
|
|
|
return expr
|
|
|
|
|
}
|