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

@ -39,7 +39,7 @@ func new{{.ToGoStructName}}() *{{.ToGoStructName}} {
}
func (a *{{.ToGoStructName}}) As(alias string) *{{.ToGoStructName}} {
func (a *{{.ToGoStructName}}) AS(alias string) *{{.ToGoStructName}} {
aliasTable := new{{.ToGoStructName}}()
aliasTable.Table.SetAlias(alias)

View file

@ -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

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
}

View file

@ -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))`)
}

View file

@ -9,8 +9,7 @@ import (
type serializeOption int
const (
SKIP_DEFAULT_ALIASING = iota
FOR_PROJECTION
FOR_PROJECTION = iota
UNION_ORDER_BY
NO_TABLE_NAME
)

View file

@ -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)
//}

View file

@ -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"`)

View file

@ -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")
//}

View file

@ -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 {

View file

@ -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")+"%")

View file

@ -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
return numericFunc
if parent != nil {
funcExp.expressionInterfaceImpl.parent = parent
} else {
funcExp.expressionInterfaceImpl.parent = funcExp
}
func (f *numericFunc) Serialize(out *queryData, options ...serializeOption) error {
return funcExp
}
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)
//}

View file

@ -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

View file

@ -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)

View file

@ -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`)

View file

@ -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
}

View file

@ -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), " != ")
}

View file

@ -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 ")
//}

View file

@ -13,7 +13,7 @@ import (
func TestSelect_ScanToStruct(t *testing.T) {
actor := model.Actor{}
query := Actor.SELECT(Actor.AllColumns).ORDER_BY(Actor.ActorID.Asc())
query := Actor.SELECT(Actor.AllColumns).ORDER_BY(Actor.ActorID.ASC())
queryStr, args, err := query.Sql()
@ -39,7 +39,7 @@ func TestSelect_ScanToStruct(t *testing.T) {
func TestClassicSelect(t *testing.T) {
query := sqlbuilder.SELECT(Payment.AllColumns, Customer.AllColumns).
FROM(Payment.INNER_JOIN(Customer, Payment.CustomerID.Eq(Customer.CustomerID))).
ORDER_BY(Payment.PaymentID.Asc()).
ORDER_BY(Payment.PaymentID.ASC()).
LIMIT(30)
queryStr, args, err := query.Sql()
@ -58,7 +58,7 @@ func TestClassicSelect(t *testing.T) {
func TestSelect_ScanToSlice(t *testing.T) {
customers := []model.Customer{}
query := Customer.SELECT(Customer.AllColumns).ORDER_BY(Customer.CustomerID.Asc())
query := Customer.SELECT(Customer.AllColumns).ORDER_BY(Customer.CustomerID.ASC())
queryStr, args, err := query.Sql()
assert.NilError(t, err)
@ -210,7 +210,7 @@ func TestSelectOrderByAscDesc(t *testing.T) {
customersAsc := []model.Customer{}
err := Customer.SELECT(Customer.CustomerID, Customer.FirstName, Customer.LastName).
ORDER_BY(Customer.FirstName.Asc()).
ORDER_BY(Customer.FirstName.ASC()).
Query(db, &customersAsc)
assert.NilError(t, err)
@ -220,7 +220,7 @@ func TestSelectOrderByAscDesc(t *testing.T) {
customersDesc := []model.Customer{}
err = Customer.SELECT(Customer.CustomerID, Customer.FirstName, Customer.LastName).
ORDER_BY(Customer.FirstName.Desc()).
ORDER_BY(Customer.FirstName.DESC()).
Query(db, &customersDesc)
assert.NilError(t, err)
@ -233,7 +233,7 @@ func TestSelectOrderByAscDesc(t *testing.T) {
customersAscDesc := []model.Customer{}
err = Customer.SELECT(Customer.CustomerID, Customer.FirstName, Customer.LastName).
ORDER_BY(Customer.FirstName.Asc(), Customer.LastName.Desc()).
ORDER_BY(Customer.FirstName.ASC(), Customer.LastName.DESC()).
Query(db, &customersAscDesc)
assert.NilError(t, err)
@ -258,7 +258,7 @@ func TestSelectFullJoin(t *testing.T) {
query := Customer.
FULL_JOIN(Address, Customer.AddressID.Eq(Address.AddressID)).
SELECT(Customer.AllColumns, Address.AllColumns).
ORDER_BY(Customer.CustomerID.Asc())
ORDER_BY(Customer.CustomerID.ASC())
queryStr, args, err := query.Sql()
@ -291,7 +291,7 @@ func TestSelectFullCrossJoin(t *testing.T) {
query := Customer.
CROSS_JOIN(Address).
SELECT(Customer.AllColumns, Address.AllColumns).
ORDER_BY(Customer.CustomerID.Asc()).
ORDER_BY(Customer.CustomerID.ASC()).
LIMIT(1000)
queryStr, args, err := query.Sql()
@ -311,15 +311,15 @@ func TestSelectFullCrossJoin(t *testing.T) {
func TestSelectSelfJoin(t *testing.T) {
f1 := Film.As("f1")
f1 := Film.AS("f1")
//spew.Dump(f1)
f2 := Film.As("f2")
f2 := Film.AS("f2")
query := f1.
INNER_JOIN(f2, f1.FilmID.NotEq(f2.FilmID).And(f1.Length.Eq(f2.Length))).
SELECT(f1.AllColumns, f2.AllColumns).
ORDER_BY(f1.FilmID.Asc())
ORDER_BY(f1.FilmID.ASC())
queryStr, args, err := query.Sql()
assert.Equal(t, len(args), 0)
@ -346,8 +346,8 @@ func TestSelectSelfJoin(t *testing.T) {
}
func TestSelectAliasColumn(t *testing.T) {
f1 := Film.As("f1")
f2 := Film.As("f2")
f1 := Film.AS("f1")
f2 := Film.AS("f2")
type thesameLengthFilms struct {
Title1 string
@ -357,10 +357,10 @@ func TestSelectAliasColumn(t *testing.T) {
query := f1.
INNER_JOIN(f2, f1.FilmID.NotEq(f2.FilmID).And(f1.Length.Eq(f2.Length))).
SELECT(f1.Title.As("thesame_length_films.title1"),
f2.Title.As("thesame_length_films.title2"),
f1.Length.As("thesame_length_films.length")).
ORDER_BY(f1.Length.Asc(), f1.Title.Asc(), f2.Title.Asc()).
SELECT(f1.Title.AS("thesame_length_films.title1"),
f2.Title.AS("thesame_length_films.title2"),
f1.Length.AS("thesame_length_films.length")).
ORDER_BY(f1.Length.ASC(), f1.Title.ASC(), f2.Title.ASC()).
LIMIT(1000)
queryStr, args, err := query.Sql()
@ -399,7 +399,7 @@ type staff struct {
func TestSelectSelfReferenceType(t *testing.T) {
manager := Staff.As("manager")
manager := Staff.AS("manager")
query := Staff.
INNER_JOIN(Address, Staff.AddressID.Eq(Address.AddressID)).
@ -425,8 +425,8 @@ func TestSubQuery(t *testing.T) {
//selectStmtTable := Actor.SELECT(Actor.FirstName, Actor.LastName).AsTable("table_expression")
//
//query := selectStmtTable.SELECT(
// selectStmtTable.RefStringColumn(Actor.FirstName).As("nesto"),
// selectStmtTable.RefIntColumnName("actor.last_name").As("nesto2"),
// selectStmtTable.RefStringColumn(Actor.FirstName).AS("nesto"),
// selectStmtTable.RefIntColumnName("actor.last_name").AS("nesto2"),
// )
//
//queryStr, args, err := query.Sql()
@ -451,8 +451,8 @@ func TestSubQuery(t *testing.T) {
SELECT(
Actor.AllColumns,
FilmActor.AllColumns,
rFilmsOnly.RefStringColumn(Film.Title).As("film.title"),
rFilmsOnly.RefStringColumn(Film.Rating).As("film.rating"),
rFilmsOnly.RefStringColumn(Film.Title).AS("film.title"),
rFilmsOnly.RefStringColumn(Film.Rating).AS("film.rating"),
)
queryStr, args, err := query.Sql()
@ -464,7 +464,7 @@ func TestSubQuery(t *testing.T) {
}
func TestSelectFunctions(t *testing.T) {
query := Film.SELECT(sqlbuilder.MAX(Film.RentalRate).As("max_film_rate"))
query := Film.SELECT(sqlbuilder.MAX(Film.RentalRate).AS("max_film_rate"))
str, args, err := query.Sql()
@ -481,7 +481,7 @@ func TestSelectQueryScalar(t *testing.T) {
query := Film.SELECT(Film.AllColumns).
WHERE(Film.RentalRate.Eq(maxFilmRentalRate)).
ORDER_BY(Film.FilmID.Asc())
ORDER_BY(Film.FilmID.ASC())
queryStr, args, err := query.Sql()
@ -520,11 +520,11 @@ func TestSelectQueryScalar(t *testing.T) {
func TestSelectGroupByHaving(t *testing.T) {
customersPaymentQuery := Payment.
SELECT(
Payment.CustomerID.As("customer_payment_sum.customer_id"),
sqlbuilder.SUM(Payment.Amount).As("customer_payment_sum.amount_sum"),
Payment.CustomerID.AS("customer_payment_sum.customer_id"),
sqlbuilder.SUM(Payment.Amount).AS("customer_payment_sum.amount_sum"),
).
GROUP_BY(Payment.CustomerID).
ORDER_BY(sqlbuilder.SUM(Payment.Amount).Asc()).
ORDER_BY(sqlbuilder.SUM(Payment.Amount).ASC()).
HAVING(sqlbuilder.SUM(Payment.Amount).Gt(sqlbuilder.NewNumericLiteral(100)))
queryStr, args, err := customersPaymentQuery.Sql()
@ -562,7 +562,7 @@ func TestSelectGroupBy2(t *testing.T) {
customersPaymentSubQuery := Payment.
SELECT(
Payment.CustomerID,
sqlbuilder.SUM(Payment.Amount).As("amount_sum"),
sqlbuilder.SUM(Payment.Amount).AS("amount_sum"),
).
GROUP_BY(Payment.CustomerID)
@ -571,8 +571,8 @@ func TestSelectGroupBy2(t *testing.T) {
query := Customer.
INNER_JOIN(customersPaymentTable, Customer.CustomerID.Eq(customersPaymentTable.RefIntColumn(Payment.CustomerID))).
SELECT(Customer.AllColumns, amountSumColumn.As("customer_with_amounts.amount_sum")).
ORDER_BY(amountSumColumn.Asc())
SELECT(Customer.AllColumns, amountSumColumn.AS("customer_with_amounts.amount_sum")).
ORDER_BY(amountSumColumn.ASC())
queryStr, args, err := query.Sql()
assert.NilError(t, err)
@ -603,7 +603,7 @@ func TestSelectGroupBy2(t *testing.T) {
func TestSelectTimeColumns(t *testing.T) {
query := Payment.SELECT(Payment.AllColumns).
WHERE(Payment.PaymentDate.LtEqL("2007-02-14 22:16:01")).
ORDER_BY(Payment.PaymentDate.Asc())
ORDER_BY(Payment.PaymentDate.ASC())
queryStr, args, err := query.Sql()
@ -630,13 +630,13 @@ func TestSelectTimeColumns(t *testing.T) {
func TestUnion(t *testing.T) {
query := sqlbuilder.UNION(
Payment.
SELECT(Payment.PaymentID.As("payment.payment_id"), Payment.Amount).
SELECT(Payment.PaymentID.AS("payment.payment_id"), Payment.Amount).
WHERE(Payment.Amount.LtEqL(100)),
Payment.
SELECT(Payment.PaymentID, Payment.Amount).
WHERE(Payment.Amount.GtEqL(200)),
).
ORDER_BY(sqlbuilder.RefColumn("payment.payment_id").Asc(), Payment.Amount.Desc()).
ORDER_BY(sqlbuilder.RefColumn("payment.payment_id").ASC(), Payment.Amount.DESC()).
LIMIT(10).OFFSET(20)
queryStr, args, err := query.Sql()