Add ROW constructor and IN/EXISTS operator.
This commit is contained in:
parent
08e4392278
commit
3367df247c
18 changed files with 183 additions and 565 deletions
|
|
@ -14,7 +14,7 @@ func NewAlias(expression Expression, alias string) *Alias {
|
|||
|
||||
func (a *Alias) SerializeForProjection(out *queryData) error {
|
||||
|
||||
err := a.expression.Serialize(out, SKIP_DEFAULT_ALIASING)
|
||||
err := a.expression.Serialize(out)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ func TestBinaryExpression(t *testing.T) {
|
|||
assert.Equal(t, len(out.args), 2)
|
||||
|
||||
t.Run("alias", func(t *testing.T) {
|
||||
alias := boolExpression.As("alias_eq_expression")
|
||||
alias := boolExpression.AS("alias_eq_expression")
|
||||
|
||||
out := queryData{}
|
||||
err := alias.SerializeForProjection(&out)
|
||||
|
|
@ -57,7 +57,7 @@ func TestUnaryExpression(t *testing.T) {
|
|||
assert.Equal(t, out.buff.String(), " NOT $1 = $2")
|
||||
|
||||
t.Run("alias", func(t *testing.T) {
|
||||
alias := notExpression.As("alias_not_expression")
|
||||
alias := notExpression.AS("alias_not_expression")
|
||||
|
||||
out := queryData{}
|
||||
err := alias.SerializeForProjection(&out)
|
||||
|
|
@ -107,3 +107,35 @@ func TestBoolLiteral(t *testing.T) {
|
|||
|
||||
assert.Equal(t, out.buff.String(), "$1")
|
||||
}
|
||||
|
||||
func TestExists(t *testing.T) {
|
||||
query := EXISTS(
|
||||
table2.
|
||||
SELECT(Literal(1)).
|
||||
WHERE(table1Col1.Eq(table2Col3)),
|
||||
)
|
||||
|
||||
out := queryData{}
|
||||
err := query.Serialize(&out)
|
||||
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, out.buff.String(), "EXISTS (SELECT $1 FROM db.table2 WHERE table1.col1 = table2.col3)")
|
||||
}
|
||||
|
||||
func TestIn(t *testing.T) {
|
||||
query := Literal(1.11).IN(table1.SELECT(table1Col1))
|
||||
|
||||
out := queryData{}
|
||||
err := query.Serialize(&out)
|
||||
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, out.buff.String(), `$1 IN (SELECT table1.col1 AS "table1.col1" FROM db.table1)`)
|
||||
|
||||
query2 := ROW(Literal(12), table1Col1).IN(table2.SELECT(table2Col3, table3Col1))
|
||||
|
||||
out = queryData{}
|
||||
err = query2.Serialize(&out)
|
||||
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, out.buff.String(), `(ROW($1, table1.col1) IN (SELECT table2.col3 AS "table2.col3", table3.col1 AS "table3.col1" FROM db.table2))`)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ import (
|
|||
type serializeOption int
|
||||
|
||||
const (
|
||||
SKIP_DEFAULT_ALIASING = iota
|
||||
FOR_PROJECTION
|
||||
FOR_PROJECTION = iota
|
||||
UNION_ORDER_BY
|
||||
NO_TABLE_NAME
|
||||
)
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ func (c *baseColumn) setTableName(table string) {
|
|||
}
|
||||
|
||||
func (c *baseColumn) DefaultAlias() Projection {
|
||||
return c.As(c.tableName + "." + c.name)
|
||||
return c.AS(c.tableName + "." + c.name)
|
||||
}
|
||||
|
||||
func (c baseColumn) Serialize(out *queryData, options ...serializeOption) error {
|
||||
|
|
@ -102,95 +102,9 @@ func (c baseColumn) Serialize(out *queryData, options ...serializeOption) error
|
|||
out.WriteString(`"`)
|
||||
}
|
||||
|
||||
//if contains(options, FOR_PROJECTION) && !contains(options, SKIP_DEFAULT_ALIASING) && c.tableName != "" {
|
||||
// out.WriteString(" AS \"" + c.tableName + "." + c.name + `"`)
|
||||
//}
|
||||
|
||||
if setOrderBy {
|
||||
out.WriteString(`"`)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//
|
||||
//// This is a strict subset of the actual allowed identifiers
|
||||
//var validIdentifierRegexp = regexp.MustCompile("^[a-zA-Z_]\\w*$")
|
||||
//
|
||||
//// Returns true if the given string is suitable as an identifier.
|
||||
//func validIdentifierName(name string) bool {
|
||||
// return validIdentifierRegexp.MatchString(name)
|
||||
//}
|
||||
|
||||
//
|
||||
//// Pseudo Column type returned by tableName.C(name)
|
||||
//type deferredLookupColumn struct {
|
||||
// isProjection
|
||||
// isExpression
|
||||
// tableName *Table
|
||||
// colName string
|
||||
//
|
||||
// cachedColumn NonAliasColumn
|
||||
//}
|
||||
//
|
||||
//func (c *deferredLookupColumn) Name() string {
|
||||
// return c.colName
|
||||
//}
|
||||
//
|
||||
//func (c *deferredLookupColumn) SerializeSqlForColumnList(
|
||||
// out *bytes.Buffer) error {
|
||||
//
|
||||
// return c.Serialize(out)
|
||||
//}
|
||||
//
|
||||
//func (c *deferredLookupColumn) Serialize(out *bytes.Buffer) error {
|
||||
// if c.cachedColumn != nil {
|
||||
// return c.cachedColumn.Serialize(out)
|
||||
// }
|
||||
//
|
||||
// col, err := c.tableName.getColumn(c.colName)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// c.cachedColumn = col
|
||||
// return col.Serialize(out)
|
||||
//}
|
||||
//
|
||||
//func (c *deferredLookupColumn) setTableName(tableName string) error {
|
||||
// return errors.Newf(
|
||||
// "Lookup column '%s' should never have setTableName called on it",
|
||||
// c.colName)
|
||||
//}
|
||||
//
|
||||
//func (c *deferredLookupColumn) Eq(rhs Expression) BoolExpression {
|
||||
// lit, ok := rhs.(*literalExpression)
|
||||
// if ok && sqltypes.Value(lit.value).IsNull() {
|
||||
// return newBoolExpression(c, rhs, []byte(" IS "))
|
||||
// }
|
||||
// return newBoolExpression(c, rhs, []byte(" = "))
|
||||
//}
|
||||
//
|
||||
//func (c *deferredLookupColumn) Gte(rhs Expression) BoolExpression {
|
||||
// return Gte(c, rhs)
|
||||
//}
|
||||
//
|
||||
//func (c *deferredLookupColumn) GteLiteral(rhs interface{}) BoolExpression {
|
||||
// return Gte(c, Literal(rhs))
|
||||
//}
|
||||
//
|
||||
//func (c *deferredLookupColumn) Lte(rhs Expression) BoolExpression {
|
||||
// return Lte(c, rhs)
|
||||
//}
|
||||
//
|
||||
//func (c *deferredLookupColumn) LteLiteral(literal interface{}) BoolExpression {
|
||||
// return Lte(c, Literal(literal))
|
||||
//}
|
||||
//
|
||||
//func (c *deferredLookupColumn) Asc() OrderByClause {
|
||||
// return sqlbuilder.Asc(c)
|
||||
//}
|
||||
//
|
||||
//func (c *deferredLookupColumn) Desc() OrderByClause {
|
||||
// return sqlbuilder.Desc(c)
|
||||
//}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ func TestNewBoolColumn(t *testing.T) {
|
|||
|
||||
out.Reset()
|
||||
boolColumn.setTableName("table1")
|
||||
aliasedBoolColumn := boolColumn.As("alias1")
|
||||
aliasedBoolColumn := boolColumn.AS("alias1")
|
||||
err = aliasedBoolColumn.SerializeForProjection(&out)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, out.buff.String(), `table1.col AS "alias1"`)
|
||||
|
|
@ -55,7 +55,7 @@ func TestNewIntColumn(t *testing.T) {
|
|||
|
||||
out.Reset()
|
||||
integerColumn.setTableName("table1")
|
||||
aliasedBoolColumn := integerColumn.As("alias1")
|
||||
aliasedBoolColumn := integerColumn.AS("alias1")
|
||||
err = aliasedBoolColumn.SerializeForProjection(&out)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, out.buff.String(), `table1.col AS "alias1"`)
|
||||
|
|
@ -83,7 +83,7 @@ func TestNewNumericColumnColumn(t *testing.T) {
|
|||
|
||||
out.Reset()
|
||||
numericColumn.setTableName("table1")
|
||||
aliasedBoolColumn := numericColumn.As("alias1")
|
||||
aliasedBoolColumn := numericColumn.AS("alias1")
|
||||
err = aliasedBoolColumn.SerializeForProjection(&out)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, out.buff.String(), `table1.col AS "alias1"`)
|
||||
|
|
|
|||
|
|
@ -9,34 +9,45 @@ type Expression interface {
|
|||
Clause
|
||||
Projection
|
||||
|
||||
As(alias string) Projection
|
||||
IsDistinct(expression Expression) BoolExpression
|
||||
IsNull() BoolExpression
|
||||
Asc() OrderByClause
|
||||
Desc() OrderByClause
|
||||
IN(subQuery SelectStatement) BoolExpression
|
||||
NOT_IN(subQuery SelectStatement) BoolExpression
|
||||
|
||||
AS(alias string) Projection
|
||||
IS_DISTINCT_FROM(expression Expression) BoolExpression
|
||||
IS_NULL() BoolExpression
|
||||
ASC() OrderByClause
|
||||
DESC() OrderByClause
|
||||
}
|
||||
|
||||
type expressionInterfaceImpl struct {
|
||||
parent Expression
|
||||
}
|
||||
|
||||
func (e *expressionInterfaceImpl) As(alias string) Projection {
|
||||
func (e *expressionInterfaceImpl) IN(subQuery SelectStatement) BoolExpression {
|
||||
return newBinaryBoolExpression(e.parent, subQuery, " IN ")
|
||||
}
|
||||
|
||||
func (e *expressionInterfaceImpl) NOT_IN(subQuery SelectStatement) BoolExpression {
|
||||
return newBinaryBoolExpression(e.parent, subQuery, " NOT_IN ")
|
||||
}
|
||||
|
||||
func (e *expressionInterfaceImpl) AS(alias string) Projection {
|
||||
return NewAlias(e.parent, alias)
|
||||
}
|
||||
|
||||
func (e *expressionInterfaceImpl) IsDistinct(expression Expression) BoolExpression {
|
||||
func (e *expressionInterfaceImpl) IS_DISTINCT_FROM(expression Expression) BoolExpression {
|
||||
return newBinaryBoolExpression(e.parent, expression, "IS DISTINCT FROM")
|
||||
}
|
||||
|
||||
func (e *expressionInterfaceImpl) IS_NULL() BoolExpression {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *expressionInterfaceImpl) IsNull() BoolExpression {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *expressionInterfaceImpl) Asc() OrderByClause {
|
||||
func (e *expressionInterfaceImpl) ASC() OrderByClause {
|
||||
return &orderByClause{expression: e.parent, ascent: true}
|
||||
}
|
||||
|
||||
func (e *expressionInterfaceImpl) Desc() OrderByClause {
|
||||
func (e *expressionInterfaceImpl) DESC() OrderByClause {
|
||||
return &orderByClause{expression: e.parent, ascent: false}
|
||||
}
|
||||
|
||||
|
|
@ -47,10 +58,10 @@ func (e *expressionInterfaceImpl) SerializeForProjection(out *queryData) error {
|
|||
// Representation of binary operations (e.g. comparisons, arithmetic)
|
||||
type binaryExpression struct {
|
||||
lhs, rhs Expression
|
||||
operator []byte
|
||||
operator string
|
||||
}
|
||||
|
||||
func newBinaryExpression(lhs, rhs Expression, operator []byte, parent ...Expression) binaryExpression {
|
||||
func newBinaryExpression(lhs, rhs Expression, operator string, parent ...Expression) binaryExpression {
|
||||
binaryExpression := binaryExpression{
|
||||
lhs: lhs,
|
||||
rhs: rhs,
|
||||
|
|
@ -92,7 +103,7 @@ func (c *binaryExpression) Serialize(out *queryData, options ...serializeOption)
|
|||
return err
|
||||
}
|
||||
|
||||
out.Write(c.operator)
|
||||
out.WriteString(c.operator)
|
||||
|
||||
if err := c.rhs.Serialize(out); err != nil {
|
||||
return err
|
||||
|
|
@ -108,10 +119,10 @@ func (c *binaryExpression) Serialize(out *queryData, options ...serializeOption)
|
|||
// A not expression which negates a expression value
|
||||
type prefixExpression struct {
|
||||
expression Expression
|
||||
operator []byte
|
||||
operator string
|
||||
}
|
||||
|
||||
func newPrefixExpression(expression Expression, operator []byte) prefixExpression {
|
||||
func newPrefixExpression(expression Expression, operator string) prefixExpression {
|
||||
prefixExpression := prefixExpression{
|
||||
expression: expression,
|
||||
operator: operator,
|
||||
|
|
@ -121,7 +132,7 @@ func newPrefixExpression(expression Expression, operator []byte) prefixExpressio
|
|||
}
|
||||
|
||||
func (p *prefixExpression) Serialize(out *queryData, options ...serializeOption) error {
|
||||
out.Write(p.operator)
|
||||
out.WriteString(p.operator + " ")
|
||||
|
||||
if p.expression == nil {
|
||||
return errors.Newf("nil prefix expression.")
|
||||
|
|
@ -132,69 +143,3 @@ func (p *prefixExpression) Serialize(out *queryData, options ...serializeOption)
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
//
|
||||
//// Representation of n-ary conjunctions (AND/OR)
|
||||
//type conjunctExpression struct {
|
||||
// expressions []Expression
|
||||
// conjunction []byte
|
||||
//}
|
||||
//
|
||||
//func (conj *conjunctExpression) Serialize(out *queryData, options ...serializeOption) error {
|
||||
// if len(conj.expressions) == 0 {
|
||||
// return errors.New("Empty conjunction.")
|
||||
// }
|
||||
//
|
||||
// //clauses := make([]Clause, len(conj.expressions), len(conj.expressions))
|
||||
// //for i, expr := range conj.expressions {
|
||||
// // clauses[i] = expr
|
||||
// //}
|
||||
//
|
||||
// useParentheses := len(conj.expressions) > 1
|
||||
// if useParentheses {
|
||||
// out.WriteByte('(')
|
||||
// }
|
||||
//
|
||||
// if err := serializeExpressionList(conj.expressions, string(conj.conjunction), out); err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// if useParentheses {
|
||||
// out.WriteByte(')')
|
||||
// }
|
||||
//
|
||||
// return nil
|
||||
//}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------//
|
||||
//// Dummy type for select *
|
||||
//type ColumnList []Column
|
||||
//
|
||||
//func (cl ColumnList) Serialize(out *bytes.Buffer, options ...serializeOption) error {
|
||||
// for i, column := range cl {
|
||||
// err := column.Serialize(out)
|
||||
//
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// if i != len(cl)-1 {
|
||||
// out.WriteString(", ")
|
||||
// }
|
||||
// }
|
||||
// return nil
|
||||
//}
|
||||
//
|
||||
//func (e ColumnList) As(alias string) Clause {
|
||||
// panic("Invalid usage")
|
||||
//}
|
||||
//
|
||||
//func (e ColumnList) IsDistinct(expression Expression) BoolExpression {
|
||||
// panic("Invalid usage")
|
||||
//}
|
||||
//
|
||||
//func (e ColumnList) IsNull(expression Expression) BoolExpression {
|
||||
// panic("Invalid usage")
|
||||
//}
|
||||
|
|
|
|||
|
|
@ -5,120 +5,28 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dropbox/godropbox/errors"
|
||||
)
|
||||
|
||||
//func serializeClauses(
|
||||
// clauses []Clause,
|
||||
// separator []byte,
|
||||
// out *bytes.Buffer) (err error) {
|
||||
//
|
||||
// if clauses == nil || len(clauses) == 0 {
|
||||
// return errors.Newf("Empty clauses.")
|
||||
// }
|
||||
//
|
||||
// if clauses[0] == nil {
|
||||
// return errors.Newf("nil clause.")
|
||||
// }
|
||||
// if err = clauses[0].Serialize(out); err != nil {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// for _, c := range clauses[1:] {
|
||||
// _, _ = out.Write(separator)
|
||||
//
|
||||
// if c == nil {
|
||||
// return errors.Newf("nil clause.")
|
||||
// }
|
||||
// if err = c.Serialize(out); err != nil {
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return nil
|
||||
//}
|
||||
//
|
||||
//// Representation of n-ary arithmetic (+ - * /)
|
||||
//type arithmeticExpression struct {
|
||||
// expressionInterfaceImpl
|
||||
// expressions []Expression
|
||||
// operator []byte
|
||||
//}
|
||||
//
|
||||
//func (arith *arithmeticExpression) Serialize(out *queryData, options ...serializeOption) error {
|
||||
// if len(arith.expressions) == 0 {
|
||||
// return errors.Newf(
|
||||
// "Empty arithmetic expression.")
|
||||
// }
|
||||
//
|
||||
// clauses := make([]Clause, len(arith.expressions), len(arith.expressions))
|
||||
// for i, expr := range arith.expressions {
|
||||
// clauses[i] = expr
|
||||
// }
|
||||
//
|
||||
// useParentheses := len(clauses) > 1
|
||||
// if useParentheses {
|
||||
// _ = out.WriteByte('(')
|
||||
// }
|
||||
//
|
||||
// if err = serializeClauses(clauses, arith.operator, out); err != nil {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// if useParentheses {
|
||||
// _ = out.WriteByte(')')
|
||||
// }
|
||||
//
|
||||
// return nil
|
||||
//}
|
||||
//
|
||||
|
||||
type tupleExpression struct {
|
||||
expressionInterfaceImpl
|
||||
elements listClause
|
||||
}
|
||||
|
||||
func (tuple *tupleExpression) Serialize(out *queryData, options ...serializeOption) error {
|
||||
if len(tuple.elements.clauses) == 0 {
|
||||
return errors.Newf("Tuples must include at least one element")
|
||||
}
|
||||
return tuple.elements.Serialize(out)
|
||||
}
|
||||
|
||||
func Tuple(exprs ...Expression) Expression {
|
||||
clauses := make([]Clause, 0, len(exprs))
|
||||
for _, expr := range exprs {
|
||||
clauses = append(clauses, expr)
|
||||
}
|
||||
return &tupleExpression{
|
||||
elements: listClause{
|
||||
clauses: clauses,
|
||||
includeParentheses: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Representation of a tuple enclosed, comma separated list of clauses
|
||||
type listClause struct {
|
||||
clauses []Clause
|
||||
includeParentheses bool
|
||||
}
|
||||
|
||||
func (list *listClause) Serialize(out *queryData, options ...serializeOption) error {
|
||||
if list.includeParentheses {
|
||||
out.WriteByte('(')
|
||||
}
|
||||
|
||||
if err := serializeClauseList(list.clauses, out); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if list.includeParentheses {
|
||||
out.WriteByte(')')
|
||||
}
|
||||
return nil
|
||||
}
|
||||
//type listClause struct {
|
||||
// clauses []Clause
|
||||
// includeParentheses bool
|
||||
//}
|
||||
//
|
||||
//func (list *listClause) Serialize(out *queryData, options ...serializeOption) error {
|
||||
// if list.includeParentheses {
|
||||
// out.WriteByte('(')
|
||||
// }
|
||||
//
|
||||
// if err := serializeClauseList(list.clauses, out); err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// if list.includeParentheses {
|
||||
// out.WriteByte(')')
|
||||
// }
|
||||
// return nil
|
||||
//}
|
||||
|
||||
//
|
||||
//type funcExpression struct {
|
||||
|
|
|
|||
|
|
@ -44,25 +44,6 @@ func (s *ExprSuite) TestConjunctExprSingleElement(c *gc.C) {
|
|||
c.Assert(sql, gc.Equals, "table1.col1=1")
|
||||
}
|
||||
|
||||
func (s *ExprSuite) TestTupleExpr(c *gc.C) {
|
||||
|
||||
expr := Tuple()
|
||||
buf := &bytes.Buffer{}
|
||||
err := expr.Serialize(buf)
|
||||
c.Assert(err, gc.NotNil)
|
||||
|
||||
expr = Tuple(table1Col1, Literal(1), Literal("five"))
|
||||
err = expr.Serialize(buf)
|
||||
c.Assert(err, gc.IsNil)
|
||||
|
||||
sql := buf.String()
|
||||
c.Assert(
|
||||
sql,
|
||||
gc.Equals,
|
||||
"(table1.col1,1,'five')")
|
||||
|
||||
}
|
||||
|
||||
func (s *ExprSuite) TestLikeExpr(c *gc.C) {
|
||||
expr := LikeL(table1Col1, EscapeForLike("%my_prefix")+"%")
|
||||
|
||||
|
|
|
|||
|
|
@ -1,33 +1,35 @@
|
|||
package sqlbuilder
|
||||
|
||||
//type FuncExpression interface {
|
||||
// Expression
|
||||
//}
|
||||
|
||||
type numericFunc struct {
|
||||
type funcExpressionImpl struct {
|
||||
expressionInterfaceImpl
|
||||
numericInterfaceImpl
|
||||
|
||||
name string
|
||||
expression Expression
|
||||
expression []Expression
|
||||
}
|
||||
|
||||
func NewNumericFunc(name string, expression Expression) NumericExpression {
|
||||
numericFunc := &numericFunc{
|
||||
func ROW(expressions ...Expression) Expression {
|
||||
return newFunc("ROW", expressions, nil)
|
||||
}
|
||||
|
||||
func newFunc(name string, expressions []Expression, parent Expression) *funcExpressionImpl {
|
||||
funcExp := &funcExpressionImpl{
|
||||
name: name,
|
||||
expression: expression,
|
||||
expression: expressions,
|
||||
}
|
||||
|
||||
numericFunc.expressionInterfaceImpl.parent = numericFunc
|
||||
numericFunc.numericInterfaceImpl.parent = numericFunc
|
||||
if parent != nil {
|
||||
funcExp.expressionInterfaceImpl.parent = parent
|
||||
} else {
|
||||
funcExp.expressionInterfaceImpl.parent = funcExp
|
||||
}
|
||||
|
||||
return numericFunc
|
||||
return funcExp
|
||||
}
|
||||
|
||||
func (f *numericFunc) Serialize(out *queryData, options ...serializeOption) error {
|
||||
func (f *funcExpressionImpl) Serialize(out *queryData, options ...serializeOption) error {
|
||||
out.WriteString(f.name)
|
||||
out.WriteString("(")
|
||||
err := f.expression.Serialize(out)
|
||||
err := serializeExpressionList(f.expression, ", ", out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -36,6 +38,20 @@ func (f *numericFunc) Serialize(out *queryData, options ...serializeOption) erro
|
|||
return nil
|
||||
}
|
||||
|
||||
type numericFunc struct {
|
||||
funcExpressionImpl
|
||||
numericInterfaceImpl
|
||||
}
|
||||
|
||||
func NewNumericFunc(name string, expressions ...Expression) NumericExpression {
|
||||
numericFunc := &numericFunc{}
|
||||
|
||||
numericFunc.funcExpressionImpl = *newFunc(name, expressions, numericFunc)
|
||||
numericFunc.numericInterfaceImpl.parent = numericFunc
|
||||
|
||||
return numericFunc
|
||||
}
|
||||
|
||||
//func (f *FuncExpression) SerializeSqlForColumnList(out *bytes.Buffer) error {
|
||||
// return f.Serialize(out)
|
||||
//}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ func NewBinaryIntegerExpression(lhs, rhs IntegerExpression, operator string) Int
|
|||
integerExpression.numericInterfaceImpl.parent = &integerExpression
|
||||
integerExpression.integerInterfaceImpl.parent = &integerExpression
|
||||
|
||||
integerExpression.binaryExpression = newBinaryExpression(lhs, rhs, []byte(operator))
|
||||
integerExpression.binaryExpression = newBinaryExpression(lhs, rhs, operator)
|
||||
|
||||
return &integerExpression
|
||||
}
|
||||
|
|
@ -72,7 +72,7 @@ type prefixIntegerExpression struct {
|
|||
|
||||
func NewPrefixIntegerExpression(expression IntegerExpression, operator string) IntegerExpression {
|
||||
integerExpression := prefixIntegerExpression{}
|
||||
integerExpression.prefixExpression = newPrefixExpression(expression, []byte(operator))
|
||||
integerExpression.prefixExpression = newPrefixExpression(expression, operator)
|
||||
|
||||
integerExpression.expressionInterfaceImpl.parent = &integerExpression
|
||||
integerExpression.numericInterfaceImpl.parent = &integerExpression
|
||||
|
|
|
|||
|
|
@ -62,19 +62,19 @@ func (n *numericInterfaceImpl) LtEqL(literal interface{}) BoolExpression {
|
|||
}
|
||||
|
||||
func (n *numericInterfaceImpl) Add(expression NumericExpression) NumericExpression {
|
||||
return newBinaryNumericExpression(n.parent, expression, []byte(" + "))
|
||||
return newBinaryNumericExpression(n.parent, expression, " + ")
|
||||
}
|
||||
|
||||
func (n *numericInterfaceImpl) Sub(expression NumericExpression) NumericExpression {
|
||||
return newBinaryNumericExpression(n.parent, expression, []byte(" - "))
|
||||
return newBinaryNumericExpression(n.parent, expression, " - ")
|
||||
}
|
||||
|
||||
func (n *numericInterfaceImpl) Mul(expression NumericExpression) NumericExpression {
|
||||
return newBinaryNumericExpression(n.parent, expression, []byte(" * "))
|
||||
return newBinaryNumericExpression(n.parent, expression, " * ")
|
||||
}
|
||||
|
||||
func (n *numericInterfaceImpl) Div(expression NumericExpression) NumericExpression {
|
||||
return newBinaryNumericExpression(n.parent, expression, []byte(" / "))
|
||||
return newBinaryNumericExpression(n.parent, expression, " / ")
|
||||
}
|
||||
|
||||
//---------------------------------------------------//
|
||||
|
|
@ -100,7 +100,7 @@ type binaryNumericExpression struct {
|
|||
binaryExpression
|
||||
}
|
||||
|
||||
func newBinaryNumericExpression(lhs, rhs Expression, operator []byte) NumericExpression {
|
||||
func newBinaryNumericExpression(lhs, rhs Expression, operator string) NumericExpression {
|
||||
numericExpression := binaryNumericExpression{}
|
||||
|
||||
numericExpression.binaryExpression = newBinaryExpression(lhs, rhs, operator)
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ func TestUnionWithOrderBy(t *testing.T) {
|
|||
query, args, err := UNION(
|
||||
table1.SELECT(table1Col1),
|
||||
table2.SELECT(table2Col3),
|
||||
).ORDER_BY(table1Col1.Asc()).Sql()
|
||||
).ORDER_BY(table1Col1.ASC()).Sql()
|
||||
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, query, `((SELECT table1.col1 AS "table1.col1" FROM db.table1) UNION (SELECT table2.col3 AS "table2.col3" FROM db.table2)) ORDER BY "table1.col1" ASC`)
|
||||
|
|
|
|||
|
|
@ -175,16 +175,3 @@ type Statement interface {
|
|||
// }
|
||||
// return nil
|
||||
//}
|
||||
|
||||
func newOrderByListClause(clauses ...OrderByClause) *listClause {
|
||||
ret := &listClause{
|
||||
clauses: make([]Clause, len(clauses), len(clauses)),
|
||||
includeParentheses: false,
|
||||
}
|
||||
|
||||
for i, c := range clauses {
|
||||
ret.clauses[i] = c
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,17 +14,17 @@ type stringInterfaceImpl struct {
|
|||
}
|
||||
|
||||
func (b *stringInterfaceImpl) Eq(expression StringExpression) BoolExpression {
|
||||
return newBinaryBoolExpression(b.parent, expression, []byte(" = "))
|
||||
return newBinaryBoolExpression(b.parent, expression, " = ")
|
||||
}
|
||||
|
||||
func (b *stringInterfaceImpl) EqL(value string) BoolExpression {
|
||||
return newBinaryBoolExpression(b.parent, Literal(value), []byte(" = "))
|
||||
return newBinaryBoolExpression(b.parent, Literal(value), " = ")
|
||||
}
|
||||
|
||||
func (b *stringInterfaceImpl) NotEq(expression StringExpression) BoolExpression {
|
||||
return newBinaryBoolExpression(b.parent, expression, []byte(" != "))
|
||||
return newBinaryBoolExpression(b.parent, expression, " != ")
|
||||
}
|
||||
|
||||
func (b *stringInterfaceImpl) NotEqL(value string) BoolExpression {
|
||||
return newBinaryBoolExpression(b.parent, Literal(value), []byte(" != "))
|
||||
return newBinaryBoolExpression(b.parent, Literal(value), " != ")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ package sqlbuilder
|
|||
//func (cl ColumnList) isProjectionType() {
|
||||
//}
|
||||
//
|
||||
//func (cl ColumnList) As(name string) Clause {
|
||||
//func (cl ColumnList) AS(name string) Clause {
|
||||
// panic("Unallowed operation ")
|
||||
//}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue