From 3367df247c9792090933d19a9d399363c78f2b51 Mon Sep 17 00:00:00 2001 From: zer0sub Date: Sun, 5 May 2019 18:03:30 +0200 Subject: [PATCH] Add ROW constructor and IN/EXISTS operator. --- generator/templates.go | 2 +- sqlbuilder/alias.go | 2 +- sqlbuilder/bool_expresion.go | 196 +++-------------------------- sqlbuilder/bool_expression_test.go | 36 +++++- sqlbuilder/clause.go | 3 +- sqlbuilder/column.go | 88 +------------ sqlbuilder/column_types_test.go | 6 +- sqlbuilder/expression.go | 115 +++++------------ sqlbuilder/expression_old.go | 130 +++---------------- sqlbuilder/expression_old_test.go | 19 --- sqlbuilder/func_expression.go | 46 ++++--- sqlbuilder/integer_expression.go | 4 +- sqlbuilder/numeric_expression.go | 10 +- sqlbuilder/set_statement_test.go | 2 +- sqlbuilder/statement.go | 13 -- sqlbuilder/string_expression.go | 8 +- sqlbuilder/types.go | 2 +- tests/select_test.go | 66 +++++----- 18 files changed, 183 insertions(+), 565 deletions(-) diff --git a/generator/templates.go b/generator/templates.go index 089fa96..cd32a70 100644 --- a/generator/templates.go +++ b/generator/templates.go @@ -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) diff --git a/sqlbuilder/alias.go b/sqlbuilder/alias.go index f9c46c5..293d182 100644 --- a/sqlbuilder/alias.go +++ b/sqlbuilder/alias.go @@ -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 diff --git a/sqlbuilder/bool_expresion.go b/sqlbuilder/bool_expresion.go index 182dde6..9869f0e 100644 --- a/sqlbuilder/bool_expresion.go +++ b/sqlbuilder/bool_expresion.go @@ -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 "ab" 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 -} diff --git a/sqlbuilder/bool_expression_test.go b/sqlbuilder/bool_expression_test.go index a721bf2..8c60595 100644 --- a/sqlbuilder/bool_expression_test.go +++ b/sqlbuilder/bool_expression_test.go @@ -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))`) +} diff --git a/sqlbuilder/clause.go b/sqlbuilder/clause.go index fca5e26..65253dc 100644 --- a/sqlbuilder/clause.go +++ b/sqlbuilder/clause.go @@ -9,8 +9,7 @@ import ( type serializeOption int const ( - SKIP_DEFAULT_ALIASING = iota - FOR_PROJECTION + FOR_PROJECTION = iota UNION_ORDER_BY NO_TABLE_NAME ) diff --git a/sqlbuilder/column.go b/sqlbuilder/column.go index cba7933..9b267de 100644 --- a/sqlbuilder/column.go +++ b/sqlbuilder/column.go @@ -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) -//} diff --git a/sqlbuilder/column_types_test.go b/sqlbuilder/column_types_test.go index adfde82..82fb101 100644 --- a/sqlbuilder/column_types_test.go +++ b/sqlbuilder/column_types_test.go @@ -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"`) diff --git a/sqlbuilder/expression.go b/sqlbuilder/expression.go index 5db68a9..f1bc548 100644 --- a/sqlbuilder/expression.go +++ b/sqlbuilder/expression.go @@ -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") -//} diff --git a/sqlbuilder/expression_old.go b/sqlbuilder/expression_old.go index b5716f2..16edc5d 100644 --- a/sqlbuilder/expression_old.go +++ b/sqlbuilder/expression_old.go @@ -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 { diff --git a/sqlbuilder/expression_old_test.go b/sqlbuilder/expression_old_test.go index fb3de48..355a1ff 100644 --- a/sqlbuilder/expression_old_test.go +++ b/sqlbuilder/expression_old_test.go @@ -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")+"%") diff --git a/sqlbuilder/func_expression.go b/sqlbuilder/func_expression.go index b50ec8a..63a40da 100644 --- a/sqlbuilder/func_expression.go +++ b/sqlbuilder/func_expression.go @@ -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) //} diff --git a/sqlbuilder/integer_expression.go b/sqlbuilder/integer_expression.go index 0c74c6c..bac5c4a 100644 --- a/sqlbuilder/integer_expression.go +++ b/sqlbuilder/integer_expression.go @@ -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 diff --git a/sqlbuilder/numeric_expression.go b/sqlbuilder/numeric_expression.go index e169a7f..4281627 100644 --- a/sqlbuilder/numeric_expression.go +++ b/sqlbuilder/numeric_expression.go @@ -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) diff --git a/sqlbuilder/set_statement_test.go b/sqlbuilder/set_statement_test.go index 84361f6..a5c6c04 100644 --- a/sqlbuilder/set_statement_test.go +++ b/sqlbuilder/set_statement_test.go @@ -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`) diff --git a/sqlbuilder/statement.go b/sqlbuilder/statement.go index 91d2423..2fa4095 100644 --- a/sqlbuilder/statement.go +++ b/sqlbuilder/statement.go @@ -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 -} diff --git a/sqlbuilder/string_expression.go b/sqlbuilder/string_expression.go index 34bbf75..5ad82bf 100644 --- a/sqlbuilder/string_expression.go +++ b/sqlbuilder/string_expression.go @@ -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), " != ") } diff --git a/sqlbuilder/types.go b/sqlbuilder/types.go index 13db60e..247a88c 100644 --- a/sqlbuilder/types.go +++ b/sqlbuilder/types.go @@ -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 ") //} diff --git a/tests/select_test.go b/tests/select_test.go index f2455cd..cb85648 100644 --- a/tests/select_test.go +++ b/tests/select_test.go @@ -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()