Add ROW constructor and IN/EXISTS operator.

This commit is contained in:
zer0sub 2019-05-05 18:03:30 +02:00
parent 08e4392278
commit 3367df247c
18 changed files with 183 additions and 565 deletions

View file

@ -1,13 +1,5 @@
package sqlbuilder
import (
"bytes"
"github.com/dropbox/godropbox/database/sqltypes"
"github.com/dropbox/godropbox/errors"
"reflect"
"time"
)
type BoolExpression interface {
Expression
@ -80,7 +72,7 @@ type binaryBoolExpression struct {
binaryExpression
}
func newBinaryBoolExpression(lhs, rhs Expression, operator []byte) BoolExpression {
func newBinaryBoolExpression(lhs, rhs Expression, operator string) BoolExpression {
boolExpression := binaryBoolExpression{}
boolExpression.binaryExpression = newBinaryExpression(lhs, rhs, operator)
@ -98,7 +90,7 @@ type prefixBoolExpression struct {
prefixExpression
}
func newPrefixBoolExpression(expression Expression, operator []byte) BoolExpression {
func newPrefixBoolExpression(expression Expression, operator string) BoolExpression {
boolExpression := prefixBoolExpression{}
boolExpression.prefixExpression = newPrefixExpression(expression, operator)
@ -108,77 +100,13 @@ func newPrefixBoolExpression(expression Expression, operator []byte) BoolExpress
return &boolExpression
}
//---------------------------------------------------//
//type conjunctBoolExpression struct {
// expressionInterfaceImpl
// boolInterfaceImpl
//
// conjunctExpression
// name string
//}
//
//func NewConjunctBoolExpression(operator []byte, expressions ...BoolExpression) BoolExpression {
// boolExpression := conjunctBoolExpression{
// conjunctExpression: conjunctExpression{
// expressions: expressions,
// conjunction: operator,
// },
// }
//
// boolExpression.expressionInterfaceImpl.parent = &boolExpression
// boolExpression.boolInterfaceImpl.parent = &boolExpression
//
// return &boolExpression
//}
//---------------------------------------------------//
type inExpression struct {
expressionInterfaceImpl
boolInterfaceImpl
lhs Expression
rhs *listClause
err error
}
func (c *inExpression) Serialize(out *queryData, options ...serializeOption) error {
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.")
}
// We'll serialize the lhs even if we don't need it to ensure no error
buf := &bytes.Buffer{}
err := c.lhs.Serialize(out, options...)
if err != nil {
return err
}
if c.rhs == nil {
out.WriteString("FALSE")
return nil
}
out.WriteString(buf.String())
out.WriteString(" IN ")
err = c.rhs.Serialize(out)
if err != nil {
return err
}
return nil
func EXISTS(subQuery SelectStatement) BoolExpression {
return newPrefixBoolExpression(subQuery, "EXISTS")
}
// Returns a representation of "a=b"
func Eq(lhs, rhs Expression) BoolExpression {
return newBinaryBoolExpression(lhs, rhs, []byte(" = "))
return newBinaryBoolExpression(lhs, rhs, " = ")
}
// Returns a representation of "a=b", where b is a literal
@ -188,7 +116,7 @@ func EqL(lhs Expression, val interface{}) BoolExpression {
// Returns a representation of "a!=b"
func NotEq(lhs, rhs Expression) BoolExpression {
return newBinaryBoolExpression(lhs, rhs, []byte("!="))
return newBinaryBoolExpression(lhs, rhs, "!=")
}
// Returns a representation of "a!=b", where b is a literal
@ -198,7 +126,7 @@ func NeqL(lhs Expression, val interface{}) BoolExpression {
// Returns a representation of "a<b"
func Lt(lhs Expression, rhs Expression) BoolExpression {
return newBinaryBoolExpression(lhs, rhs, []byte("<"))
return newBinaryBoolExpression(lhs, rhs, "<")
}
// Returns a representation of "a<b", where b is a literal
@ -208,7 +136,7 @@ func LtL(lhs Expression, val interface{}) BoolExpression {
// Returns a representation of "a<=b"
func LtEq(lhs, rhs Expression) BoolExpression {
return newBinaryBoolExpression(lhs, rhs, []byte("<="))
return newBinaryBoolExpression(lhs, rhs, "<=")
}
// Returns a representation of "a<=b", where b is a literal
@ -218,7 +146,7 @@ func LteL(lhs Expression, val interface{}) BoolExpression {
// Returns a representation of "a>b"
func Gt(lhs, rhs Expression) BoolExpression {
return newBinaryBoolExpression(lhs, rhs, []byte(">"))
return newBinaryBoolExpression(lhs, rhs, ">")
}
// Returns a representation of "a>b", where b is a literal
@ -228,7 +156,7 @@ func GtL(lhs Expression, val interface{}) BoolExpression {
// Returns a representation of "a>=b"
func GtEq(lhs, rhs Expression) BoolExpression {
return newBinaryBoolExpression(lhs, rhs, []byte(">="))
return newBinaryBoolExpression(lhs, rhs, ">=")
}
// Returns a representation of "a>=b", where b is a literal
@ -238,24 +166,24 @@ func GteL(lhs Expression, val interface{}) BoolExpression {
// Returns a representation of "not expr"
func Not(expr BoolExpression) BoolExpression {
return newPrefixBoolExpression(expr, []byte(" NOT "))
return newPrefixBoolExpression(expr, " NOT")
}
func IsTrue(expr BoolExpression) BoolExpression {
return newPrefixBoolExpression(expr, []byte(" IS TRUE "))
return newPrefixBoolExpression(expr, " IS TRUE")
}
func And(lhs, rhs Expression) BoolExpression {
return newBinaryBoolExpression(lhs, rhs, []byte(" AND "))
return newBinaryBoolExpression(lhs, rhs, " AND ")
}
// Returns a representation of "c[0] OR ... OR c[n-1]" for c in clauses
func Or(lhs, rhs Expression) BoolExpression {
return newBinaryBoolExpression(lhs, rhs, []byte(" OR "))
return newBinaryBoolExpression(lhs, rhs, " OR ")
}
func Like(lhs, rhs Expression) BoolExpression {
return newBinaryBoolExpression(lhs, rhs, []byte(" LIKE "))
return newBinaryBoolExpression(lhs, rhs, " LIKE ")
}
func LikeL(lhs Expression, val string) BoolExpression {
@ -263,101 +191,9 @@ func LikeL(lhs Expression, val string) BoolExpression {
}
func Regexp(lhs, rhs Expression) BoolExpression {
return newBinaryBoolExpression(lhs, rhs, []byte(" REGEXP "))
return newBinaryBoolExpression(lhs, rhs, " REGEXP ")
}
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
}