diff --git a/sqlbuilder/alias_projection.go b/sqlbuilder/alias.go similarity index 75% rename from sqlbuilder/alias_projection.go rename to sqlbuilder/alias.go index 458cbff..aef842b 100644 --- a/sqlbuilder/alias_projection.go +++ b/sqlbuilder/alias.go @@ -20,9 +20,9 @@ func NewAlias(expression Expression, alias string) *Alias { } } -func (a *Alias) SerializeSql(out *bytes.Buffer) error { +func (a *Alias) SerializeSql(out *bytes.Buffer, options ...serializeOption) error { - err := a.expression.SerializeSql(out) + err := a.expression.SerializeSql(out, ALIASED) if err != nil { return err diff --git a/sqlbuilder/bool_expresion.go b/sqlbuilder/bool_expresion.go index 8346231..f5d3061 100644 --- a/sqlbuilder/bool_expresion.go +++ b/sqlbuilder/bool_expresion.go @@ -11,6 +11,11 @@ import ( type BoolExpression interface { Expression + Eq(expression BoolExpression) BoolExpression + NotEq(expression BoolExpression) BoolExpression + GtEq(rhs Expression) BoolExpression + LtEq(rhs Expression) BoolExpression + And(expression BoolExpression) BoolExpression Or(expression BoolExpression) BoolExpression IsTrue() BoolExpression @@ -21,6 +26,22 @@ type boolInterfaceImpl struct { parent BoolExpression } +func (b *boolInterfaceImpl) Eq(expression BoolExpression) BoolExpression { + return Eq(b.parent, expression) +} + +func (b *boolInterfaceImpl) NotEq(expression BoolExpression) BoolExpression { + return Neq(b.parent, expression) +} + +func (b *boolInterfaceImpl) GtEq(rhs Expression) BoolExpression { + return Gte(b.parent, rhs) +} + +func (b *boolInterfaceImpl) LtEq(rhs Expression) BoolExpression { + return Lte(b.parent, rhs) +} + func (b *boolInterfaceImpl) And(expression BoolExpression) BoolExpression { return And(b.parent, expression) } @@ -42,7 +63,7 @@ type boolLiteralExpression struct { literalExpression } -func NewBoolLiteralExpression(value bool) BoolExpression { +func newBoolLiteralExpression(value bool) BoolExpression { boolLiteralExpression := boolLiteralExpression{} sqlValue, err := sqltypes.BuildValue(value) @@ -57,15 +78,17 @@ func NewBoolLiteralExpression(value bool) BoolExpression { //---------------------------------------------------// type binaryBoolExpression struct { + expressionInterfaceImpl boolInterfaceImpl binaryExpression } -func NewBinaryBoolExpression(lhs, rhs Expression, operator []byte) BoolExpression { +func newBinaryBoolExpression(lhs, rhs Expression, operator []byte) BoolExpression { boolExpression := binaryBoolExpression{} - boolExpression.binaryExpression = *NewBinaryExpression(lhs, rhs, operator, &boolExpression) + boolExpression.binaryExpression = newBinaryExpression(lhs, rhs, operator) + boolExpression.expressionInterfaceImpl.parent = &boolExpression boolExpression.boolInterfaceImpl.parent = &boolExpression return &boolExpression @@ -73,15 +96,17 @@ func NewBinaryBoolExpression(lhs, rhs Expression, operator []byte) BoolExpressio //---------------------------------------------------// type prefixBoolExpression struct { + expressionInterfaceImpl boolInterfaceImpl prefixExpression } -func NewPrefixBoolExpression(expression Expression, operator []byte) BoolExpression { +func newPrefixBoolExpression(expression Expression, operator []byte) BoolExpression { boolExpression := prefixBoolExpression{} - boolExpression.prefixExpression = *NewPrefixExpression(expression, operator, &boolExpression) + boolExpression.prefixExpression = newPrefixExpression(expression, operator) + boolExpression.expressionInterfaceImpl.parent = &boolExpression boolExpression.boolInterfaceImpl.parent = &boolExpression return &boolExpression @@ -89,6 +114,7 @@ func NewPrefixBoolExpression(expression Expression, operator []byte) BoolExpress //---------------------------------------------------// type conjunctBoolExpression struct { + expressionInterfaceImpl boolInterfaceImpl conjunctExpression @@ -103,8 +129,8 @@ func NewConjunctBoolExpression(operator []byte, expressions ...BoolExpression) B }, } - //boolExpression.expressionInterfaceImpl.parent = &boolExpression - //boolExpression.boolInterfaceImpl.parent = &boolExpression + boolExpression.expressionInterfaceImpl.parent = &boolExpression + boolExpression.boolInterfaceImpl.parent = &boolExpression return &boolExpression } @@ -120,7 +146,7 @@ type inExpression struct { err error } -func (c *inExpression) SerializeSql(out *bytes.Buffer) error { +func (c *inExpression) SerializeSql(out *bytes.Buffer, options ...serializeOption) error { if c.err != nil { return errors.Wrap(c.err, "Invalid IN expression") } @@ -159,9 +185,9 @@ func (c *inExpression) SerializeSql(out *bytes.Buffer) error { func Eq(lhs, rhs Expression) BoolExpression { lit, ok := rhs.(*literalExpression) if ok && sqltypes.Value(lit.value).IsNull() { - return NewBinaryBoolExpression(lhs, rhs, []byte(" IS ")) + return newBinaryBoolExpression(lhs, rhs, []byte(" IS ")) } - return NewBinaryBoolExpression(lhs, rhs, []byte(" = ")) + return newBinaryBoolExpression(lhs, rhs, []byte(" = ")) } // Returns a representation of "a=b", where b is a literal @@ -173,9 +199,9 @@ func EqL(lhs Expression, val interface{}) BoolExpression { func Neq(lhs, rhs Expression) BoolExpression { lit, ok := rhs.(*literalExpression) if ok && sqltypes.Value(lit.value).IsNull() { - return NewBinaryBoolExpression(lhs, rhs, []byte(" IS NOT ")) + return newBinaryBoolExpression(lhs, rhs, []byte(" IS NOT ")) } - return NewBinaryBoolExpression(lhs, rhs, []byte("!=")) + return newBinaryBoolExpression(lhs, rhs, []byte("!=")) } // Returns a representation of "a!=b", where b is a literal @@ -185,7 +211,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, []byte(">")) } // Returns a representation of "a>b", where b is a literal @@ -215,7 +241,7 @@ func GtL(lhs Expression, val interface{}) BoolExpression { // Returns a representation of "a>=b" func Gte(lhs, rhs Expression) BoolExpression { - return NewBinaryBoolExpression(lhs, rhs, []byte(">=")) + return newBinaryBoolExpression(lhs, rhs, []byte(">=")) } // Returns a representation of "a>=b", where b is a literal @@ -225,11 +251,11 @@ 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, []byte(" NOT ")) } func IsTrue(expr BoolExpression) BoolExpression { - return NewPrefixBoolExpression(expr, []byte(" IS TRUE ")) + return newPrefixBoolExpression(expr, []byte(" IS TRUE ")) } // Returns a representation of "c[0] AND ... AND c[n-1]" for c in clauses @@ -243,7 +269,7 @@ func Or(expressions ...BoolExpression) BoolExpression { } func Like(lhs, rhs Expression) BoolExpression { - return NewBinaryBoolExpression(lhs, rhs, []byte(" LIKE ")) + return newBinaryBoolExpression(lhs, rhs, []byte(" LIKE ")) } func LikeL(lhs Expression, val string) BoolExpression { @@ -251,7 +277,7 @@ func LikeL(lhs Expression, val string) BoolExpression { } func Regexp(lhs, rhs Expression) BoolExpression { - return NewBinaryBoolExpression(lhs, rhs, []byte(" REGEXP ")) + return newBinaryBoolExpression(lhs, rhs, []byte(" REGEXP ")) } func RegexpL(lhs Expression, val string) BoolExpression { diff --git a/sqlbuilder/bool_expression_test.go b/sqlbuilder/bool_expression_test.go index 1f6b04e..eba700f 100644 --- a/sqlbuilder/bool_expression_test.go +++ b/sqlbuilder/bool_expression_test.go @@ -97,7 +97,7 @@ func TestUnaryIsTrueExpression(t *testing.T) { } func TestBoolLiteral(t *testing.T) { - literal := NewBoolLiteralExpression(true) + literal := newBoolLiteralExpression(true) out := bytes.Buffer{} err := literal.SerializeSql(&out) diff --git a/sqlbuilder/clause.go b/sqlbuilder/clause.go index cc85438..37c3cf8 100644 --- a/sqlbuilder/clause.go +++ b/sqlbuilder/clause.go @@ -2,6 +2,22 @@ package sqlbuilder import "bytes" +type serializeOption int + +const ( + ALIASED = iota + FOR_PROJECTION +) + type Clause interface { - SerializeSql(out *bytes.Buffer) error + SerializeSql(out *bytes.Buffer, options ...serializeOption) error +} + +func contains(s []serializeOption, e serializeOption) bool { + for _, a := range s { + if a == e { + return true + } + } + return false } diff --git a/sqlbuilder/column.go b/sqlbuilder/column.go index 5c91e5a..e0b60ff 100644 --- a/sqlbuilder/column.go +++ b/sqlbuilder/column.go @@ -6,8 +6,6 @@ import ( "bytes" "regexp" "strings" - - "github.com/dropbox/godropbox/errors" ) // XXX: Maybe add UIntColumn @@ -15,31 +13,29 @@ import ( // Representation of a tableName for query generation type Column interface { Expression - isProjectionInterface Name() string - TableName() string - // Serialization for use in column lists - SerializeSqlForColumnList(out *bytes.Buffer) error - // Internal function for tracking tableName that a column belongs to // for the purpose of serialization setTableName(table string) error - Eq(rhs Expression) BoolExpression - Neq(rhs Expression) BoolExpression - - Gte(rhs Expression) BoolExpression - GteLiteral(rhs interface{}) BoolExpression - - Lte(rhs Expression) BoolExpression - LteLiteral(rhs interface{}) BoolExpression - Asc() OrderByClause Desc() OrderByClause } +type columnInterfaceImpl struct { + parent Column +} + +func (c *columnInterfaceImpl) Asc() OrderByClause { + return &orderByClause{expression: c.parent, ascent: true} +} + +func (c *columnInterfaceImpl) Desc() OrderByClause { + return &orderByClause{expression: c.parent, ascent: false} +} + type NullableColumn bool const ( @@ -47,10 +43,10 @@ const ( NotNullable NullableColumn = false ) -// A column that can be refer to outside of the projection list -type NonAliasColumn interface { - Column -} +//// A column that can be refer to outside of the projection list +//type NonAliasColumn interface { +// Column +//} type Collation string @@ -70,19 +66,25 @@ const ( // The base type for real materialized columns. type baseColumn struct { expressionInterfaceImpl - isProjection + columnInterfaceImpl + name string nullable NullableColumn tableName string - alias string } -//func (c *baseColumn) As(alias string) Projection { -// newBaseColumn := *c -// newBaseColumn.alias = alias -// -// return &newBaseColumn -//} +func newBaseColumn(name string, nullable NullableColumn, tableName string, parent Column) baseColumn { + bc := baseColumn{ + name: name, + nullable: nullable, + tableName: tableName, + } + + bc.expressionInterfaceImpl.parent = parent + bc.columnInterfaceImpl.parent = parent + + return bc +} func (c *baseColumn) Name() string { return c.name @@ -97,20 +99,7 @@ func (c *baseColumn) setTableName(table string) error { return nil } -func (c *baseColumn) SerializeSqlForColumnList(out *bytes.Buffer) error { - - c.SerializeSql(out) - - if c.alias != "" { - _, _ = out.WriteString(" AS \"" + c.alias + "\"") - } else if c.tableName != "" { - _, _ = out.WriteString(" AS \"" + c.tableName + "." + c.name + "\"") - } - - return nil -} - -func (c baseColumn) SerializeSql(out *bytes.Buffer) error { +func (c baseColumn) SerializeSql(out *bytes.Buffer, options ...serializeOption) error { if c.tableName != "" { _, _ = out.WriteString(c.tableName) _, _ = out.WriteString(".") @@ -125,190 +114,163 @@ func (c baseColumn) SerializeSql(out *bytes.Buffer) error { out.WriteString("\"") } + if contains(options, FOR_PROJECTION) && !contains(options, ALIASED) && c.tableName != "" { + _, _ = out.WriteString(" AS \"" + c.tableName + "." + c.name + "\"") + } + return nil } -func (c *baseColumn) Eq(rhs Expression) BoolExpression { - return Eq(c, rhs) -} +// +//type bytesColumn struct { +// baseColumn +//} +// +//// Representation of VARBINARY/BLOB columns +//// This function will panic if name is not valid +//func BytesColumn(name string, nullable NullableColumn) Column { +// if !validIdentifierName(name) { +// panic("Invalid column name in bytes column") +// } +// bc := &bytesColumn{} +// bc.name = name +// bc.nullable = nullable +// return bc +//} +// +//type stringColumn struct { +// baseColumn +// charset Charset +// collation Collation +//} +// +//// Representation of VARCHAR/TEXT columns +//// This function will panic if name is not valid +//func StrColumn( +// name string, +// charset Charset, +// collation Collation, +// nullable NullableColumn) Column { +// +// if !validIdentifierName(name) { +// panic("Invalid column name in str column") +// } +// sc := &stringColumn{charset: charset, collation: collation} +// sc.name = name +// sc.nullable = nullable +// return sc +//} +// +//type dateTimeColumn struct { +// baseColumn +//} +// +//// Representation of DateTime columns +//// This function will panic if name is not valid +//func DateTimeColumn(name string, nullable NullableColumn) Column { +// if !validIdentifierName(name) { +// panic("Invalid column name in datetime column") +// } +// dc := &dateTimeColumn{} +// dc.name = name +// dc.nullable = nullable +// return dc +//} -func (c *baseColumn) Neq(rhs Expression) BoolExpression { - return Neq(c, rhs) -} +//type IntegerColumn struct { +// baseColumn +//} +// +//// Representation of any integer column +//// This function will panic if name is not valid +//func IntColumn(name string, nullable NullableColumn) *IntegerColumn { +// if !validIdentifierName(name) { +// panic("Invalid column name in int column") +// } +// ic := &IntegerColumn{} +// ic.name = name +// ic.nullable = nullable +// return ic +//} -func (c *baseColumn) Gte(rhs Expression) BoolExpression { - return Gte(c, rhs) -} - -func (c *baseColumn) GteLiteral(rhs interface{}) BoolExpression { - return Gte(c, Literal(rhs)) -} - -func (c *baseColumn) Lte(rhs Expression) BoolExpression { - return Lte(c, rhs) -} - -func (c *baseColumn) LteLiteral(literal interface{}) BoolExpression { - return Lte(c, Literal(literal)) -} - -func (c *baseColumn) Asc() OrderByClause { - return Asc(c) -} - -func (c *baseColumn) Desc() OrderByClause { - return Desc(c) -} - -type bytesColumn struct { - baseColumn -} - -// Representation of VARBINARY/BLOB columns -// This function will panic if name is not valid -func BytesColumn(name string, nullable NullableColumn) NonAliasColumn { - if !validIdentifierName(name) { - panic("Invalid column name in bytes column") - } - bc := &bytesColumn{} - bc.name = name - bc.nullable = nullable - return bc -} - -type stringColumn struct { - baseColumn - charset Charset - collation Collation -} - -// Representation of VARCHAR/TEXT columns -// This function will panic if name is not valid -func StrColumn( - name string, - charset Charset, - collation Collation, - nullable NullableColumn) NonAliasColumn { - - if !validIdentifierName(name) { - panic("Invalid column name in str column") - } - sc := &stringColumn{charset: charset, collation: collation} - sc.name = name - sc.nullable = nullable - return sc -} - -type dateTimeColumn struct { - baseColumn -} - -// Representation of DateTime columns -// This function will panic if name is not valid -func DateTimeColumn(name string, nullable NullableColumn) NonAliasColumn { - if !validIdentifierName(name) { - panic("Invalid column name in datetime column") - } - dc := &dateTimeColumn{} - dc.name = name - dc.nullable = nullable - return dc -} - -type IntegerColumn struct { - baseColumn -} - -// Representation of any integer column -// This function will panic if name is not valid -func IntColumn(name string, nullable NullableColumn) *IntegerColumn { - if !validIdentifierName(name) { - panic("Invalid column name in int column") - } - ic := &IntegerColumn{} - ic.name = name - ic.nullable = nullable - return ic -} - -type doubleColumn struct { - baseColumn -} - -// Representation of any double column -// This function will panic if name is not valid -func DoubleColumn(name string, nullable NullableColumn) NonAliasColumn { - if !validIdentifierName(name) { - panic("Invalid column name in int column") - } - ic := &doubleColumn{} - ic.name = name - ic.nullable = nullable - return ic -} - -type booleanColumn struct { - baseColumn - - // XXX: Maybe allow isBoolExpression (for now, not included because - // the deferred lookup equivalent can never be isBoolExpression) -} +//type doubleColumn struct { +// baseColumn +//} +// +//// Representation of any double column +//// This function will panic if name is not valid +//func DoubleColumn(name string, nullable NullableColumn) Column { +// if !validIdentifierName(name) { +// panic("Invalid column name in int column") +// } +// ic := &doubleColumn{} +// ic.name = name +// ic.nullable = nullable +// return ic +//} +// +//type booleanColumn struct { +// baseColumn +// +// // XXX: Maybe allow isBoolExpression (for now, not included because +// // the deferred lookup equivalent can never be isBoolExpression) +//} // Representation of TINYINT used as a bool // This function will panic if name is not valid -func BoolColumn(name string, nullable NullableColumn) NonAliasColumn { - if !validIdentifierName(name) { - panic("Invalid column name in bool column") - } - bc := &booleanColumn{} - bc.name = name - bc.nullable = nullable - return bc -} +//func NewBoolColumn(name string, nullable NullableColumn) Column { +// if !validIdentifierName(name) { +// panic("Invalid column name in bool column") +// } +// bc := &booleanColumn{} +// bc.name = name +// bc.nullable = nullable +// return bc +//} +// +//type aliasColumn struct { +// baseColumn +// expression Expression +//} +// +//func (c *aliasColumn) SerializeSql(out *bytes.Buffer) error { +// _ = out.WriteByte('`') +// _, _ = out.WriteString(c.name) +// _ = out.WriteByte('`') +// return nil +//} +// +//func (c *aliasColumn) SerializeSqlForColumnList(out *bytes.Buffer) error { +// if !validIdentifierName(c.name) { +// return errors.Newf( +// "Invalid alias name `%s`. Generated sql: %s", +// c.name, +// out.String()) +// } +// if c.expression == nil { +// return errors.Newf( +// "Cannot alias a nil expression. Generated sql: %s", +// out.String()) +// } +// +// _ = out.WriteByte('(') +// if c.expression == nil { +// return errors.Newf("nil alias clause. Generate sql: %s", out.String()) +// } +// if err := c.expression.SerializeSql(out); err != nil { +// return err +// } +// _, _ = out.WriteString(") AS \"") +// _, _ = out.WriteString(c.name) +// _ = out.WriteByte('"') +// return nil +//} -type aliasColumn struct { - baseColumn - expression Expression -} - -func (c *aliasColumn) SerializeSql(out *bytes.Buffer) error { - _ = out.WriteByte('`') - _, _ = out.WriteString(c.name) - _ = out.WriteByte('`') - return nil -} - -func (c *aliasColumn) SerializeSqlForColumnList(out *bytes.Buffer) error { - if !validIdentifierName(c.name) { - return errors.Newf( - "Invalid alias name `%s`. Generated sql: %s", - c.name, - out.String()) - } - if c.expression == nil { - return errors.Newf( - "Cannot alias a nil expression. Generated sql: %s", - out.String()) - } - - _ = out.WriteByte('(') - if c.expression == nil { - return errors.Newf("nil alias clause. Generate sql: %s", out.String()) - } - if err := c.expression.SerializeSql(out); err != nil { - return err - } - _, _ = out.WriteString(") AS \"") - _, _ = out.WriteString(c.name) - _ = out.WriteByte('"') - return nil -} - -func (c *aliasColumn) setTableName(table string) error { - return errors.Newf( - "Alias column '%s' should never have setTableName called on it", - c.name) -} +//func (c *aliasColumn) setTableName(table string) error { +// return errors.Newf( +// "Alias column '%s' should never have setTableName called on it", +// c.name) +//} // Representation of aliased clauses (expression AS name) //func Alias(name string, c Expression) Column { diff --git a/sqlbuilder/column_types.go b/sqlbuilder/column_types.go new file mode 100644 index 0000000..186f5df --- /dev/null +++ b/sqlbuilder/column_types.go @@ -0,0 +1,65 @@ +package sqlbuilder + +//------------------------------------------------------// +type BoolColumn struct { + boolInterfaceImpl + + baseColumn +} + +func NewBoolColumn(name string, nullable NullableColumn) *BoolColumn { + if !validIdentifierName(name) { + panic("Invalid column name in bool column") + } + boolColumn := &BoolColumn{} + boolColumn.baseColumn = newBaseColumn(name, nullable, "", boolColumn) + + boolColumn.boolInterfaceImpl.parent = boolColumn + + return boolColumn +} + +//------------------------------------------------------// +type NumericColumn struct { + numericInterfaceImpl + baseColumn +} + +func NewNumericColumn(name string, nullable NullableColumn) *NumericColumn { + if !validIdentifierName(name) { + panic("Invalid column name") + } + + numericColumn := &NumericColumn{} + + numericColumn.numericInterfaceImpl.parent = numericColumn + + numericColumn.baseColumn = newBaseColumn(name, nullable, "", numericColumn) + + return numericColumn +} + +//------------------------------------------------------// +type IntegerColumn struct { + numericInterfaceImpl + integerInterfaceImpl + + baseColumn +} + +// Representation of any integer column +// This function will panic if name is not valid +func NewIntegerColumn(name string, nullable NullableColumn) *IntegerColumn { + if !validIdentifierName(name) { + panic("Invalid column name") + } + + integerColumn := &IntegerColumn{} + + integerColumn.numericInterfaceImpl.parent = integerColumn + integerColumn.integerInterfaceImpl.parent = integerColumn + + integerColumn.baseColumn = newBaseColumn(name, nullable, "", integerColumn) + + return integerColumn +} diff --git a/sqlbuilder/column_types_test.go b/sqlbuilder/column_types_test.go new file mode 100644 index 0000000..355175d --- /dev/null +++ b/sqlbuilder/column_types_test.go @@ -0,0 +1,97 @@ +package sqlbuilder + +import ( + "bytes" + "gotest.tools/assert" + "testing" +) + +func TestNewBoolColumn(t *testing.T) { + boolColumn := NewBoolColumn("col", Nullable) + + out := bytes.Buffer{} + err := boolColumn.SerializeSql(&out) + + assert.NilError(t, err) + assert.Equal(t, out.String(), "col") + + out.Reset() + err = boolColumn.SerializeSql(&out, FOR_PROJECTION) + assert.NilError(t, err) + assert.Equal(t, out.String(), "col") + + out.Reset() + err = boolColumn.setTableName("table1") + assert.NilError(t, err) + err = boolColumn.SerializeSql(&out, FOR_PROJECTION) + assert.NilError(t, err) + assert.Equal(t, out.String(), `table1.col AS "table1.col"`) + + out.Reset() + err = boolColumn.setTableName("table1") + assert.NilError(t, err) + aliasedBoolColumn := boolColumn.As("alias1") + err = aliasedBoolColumn.SerializeSql(&out, FOR_PROJECTION) + assert.NilError(t, err) + assert.Equal(t, out.String(), `table1.col AS "alias1"`) +} + +func TestNewIntColumn(t *testing.T) { + integerColumn := NewIntegerColumn("col", Nullable) + + out := bytes.Buffer{} + err := integerColumn.SerializeSql(&out) + + assert.NilError(t, err) + assert.Equal(t, out.String(), "col") + + out.Reset() + err = integerColumn.SerializeSql(&out, FOR_PROJECTION) + assert.NilError(t, err) + assert.Equal(t, out.String(), "col") + + out.Reset() + err = integerColumn.setTableName("table1") + assert.NilError(t, err) + err = integerColumn.SerializeSql(&out, FOR_PROJECTION) + assert.NilError(t, err) + assert.Equal(t, out.String(), `table1.col AS "table1.col"`) + + out.Reset() + err = integerColumn.setTableName("table1") + assert.NilError(t, err) + aliasedBoolColumn := integerColumn.As("alias1") + err = aliasedBoolColumn.SerializeSql(&out, FOR_PROJECTION) + assert.NilError(t, err) + assert.Equal(t, out.String(), `table1.col AS "alias1"`) +} + +func TestNewNumericColumnColumn(t *testing.T) { + numericColumn := NewNumericColumn("col", Nullable) + + out := bytes.Buffer{} + err := numericColumn.SerializeSql(&out) + + assert.NilError(t, err) + assert.Equal(t, out.String(), "col") + + out.Reset() + err = numericColumn.SerializeSql(&out, FOR_PROJECTION) + assert.NilError(t, err) + assert.Equal(t, out.String(), "col") + + out.Reset() + err = numericColumn.setTableName("table1") + assert.NilError(t, err) + err = numericColumn.SerializeSql(&out, FOR_PROJECTION) + assert.NilError(t, err) + assert.Equal(t, out.String(), `table1.col AS "table1.col"`) + + out.Reset() + err = numericColumn.setTableName("table1") + assert.NilError(t, err) + aliasedBoolColumn := numericColumn.As("alias1") + err = aliasedBoolColumn.SerializeSql(&out, FOR_PROJECTION) + assert.NilError(t, err) + assert.Equal(t, out.String(), `table1.col AS "alias1"`) +} diff --git a/sqlbuilder/expression.go b/sqlbuilder/expression.go index dbadbf5..20a6c92 100644 --- a/sqlbuilder/expression.go +++ b/sqlbuilder/expression.go @@ -12,7 +12,7 @@ type Expression interface { As(alias string) Clause IsDistinct(expression Expression) BoolExpression - IsNull(expression Expression) BoolExpression + IsNull() BoolExpression } type expressionInterfaceImpl struct { @@ -27,31 +27,27 @@ func (e *expressionInterfaceImpl) IsDistinct(expression Expression) BoolExpressi return nil } -func (e *expressionInterfaceImpl) IsNull(expression Expression) BoolExpression { +func (e *expressionInterfaceImpl) IsNull() BoolExpression { return nil } // Representation of binary operations (e.g. comparisons, arithmetic) type binaryExpression struct { - expressionInterfaceImpl lhs, rhs Expression operator []byte } -func NewBinaryExpression(lhs, rhs Expression, operator []byte, parent ...Expression) *binaryExpression { +func newBinaryExpression(lhs, rhs Expression, operator []byte, parent ...Expression) binaryExpression { binaryExpression := binaryExpression{ lhs: lhs, rhs: rhs, operator: operator, } - if len(parent) > 0 { - binaryExpression.parent = parent[0] - } - return &binaryExpression + return binaryExpression } -func (c *binaryExpression) SerializeSql(out *bytes.Buffer) (err error) { +func (c *binaryExpression) SerializeSql(out *bytes.Buffer, options ...serializeOption) (err error) { if c.lhs == nil { return errors.Newf("nil lhs. Generated sql: %s", out.String()) } @@ -73,25 +69,20 @@ func (c *binaryExpression) SerializeSql(out *bytes.Buffer) (err error) { // A not expression which negates a expression value type prefixExpression struct { - expressionInterfaceImpl - expression Expression operator []byte } -func NewPrefixExpression(expression Expression, operator []byte, parent ...Expression) *prefixExpression { +func newPrefixExpression(expression Expression, operator []byte) prefixExpression { prefixExpression := prefixExpression{ expression: expression, operator: operator, } - if len(parent) > 0 { - prefixExpression.parent = parent[0] - } - return &prefixExpression + return prefixExpression } -func (p *prefixExpression) SerializeSql(out *bytes.Buffer) (err error) { +func (p *prefixExpression) SerializeSql(out *bytes.Buffer, options ...serializeOption) (err error) { _, _ = out.Write(p.operator) if p.expression == nil { @@ -106,12 +97,11 @@ func (p *prefixExpression) SerializeSql(out *bytes.Buffer) (err error) { // Representation of n-ary conjunctions (AND/OR) type conjunctExpression struct { - expressionInterfaceImpl expressions []BoolExpression conjunction []byte } -func (conj *conjunctExpression) SerializeSql(out *bytes.Buffer) (err error) { +func (conj *conjunctExpression) SerializeSql(out *bytes.Buffer, options ...serializeOption) (err error) { if len(conj.expressions) == 0 { return errors.Newf( "Empty conjunction. Generated sql: %s", @@ -154,7 +144,38 @@ func NewLiteralExpression(value sqltypes.Value) *literalExpression { return &exp } -func (c literalExpression) SerializeSql(out *bytes.Buffer) error { +func (c literalExpression) SerializeSql(out *bytes.Buffer, options ...serializeOption) error { sqltypes.Value(c.value).EncodeSql(out) return nil } + +//------------------------------------------------------// +// Dummy type for select * +type ColumnList []Column + +func (cl ColumnList) SerializeSql(out *bytes.Buffer, options ...serializeOption) error { + for i, column := range cl { + err := column.SerializeSql(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 db73c6a..af82d0c 100644 --- a/sqlbuilder/expression_old.go +++ b/sqlbuilder/expression_old.go @@ -17,7 +17,7 @@ type orderByClause struct { ascent bool } -func (o *orderByClause) SerializeSql(out *bytes.Buffer) error { +func (o *orderByClause) SerializeSql(out *bytes.Buffer, options ...serializeOption) error { if o.expression == nil { return errors.Newf( "nil order by clause. Generated sql: %s", @@ -82,7 +82,7 @@ type arithmeticExpression struct { operator []byte } -func (arith *arithmeticExpression) SerializeSql(out *bytes.Buffer) (err error) { +func (arith *arithmeticExpression) SerializeSql(out *bytes.Buffer, options ...serializeOption) (err error) { if len(arith.expressions) == 0 { return errors.Newf( "Empty arithmetic expression. Generated sql: %s", @@ -115,7 +115,7 @@ type tupleExpression struct { elements listClause } -func (tuple *tupleExpression) SerializeSql(out *bytes.Buffer) error { +func (tuple *tupleExpression) SerializeSql(out *bytes.Buffer, options ...serializeOption) error { if len(tuple.elements.clauses) < 1 { return errors.Newf("Tuples must include at least one element") } @@ -141,7 +141,7 @@ type listClause struct { includeParentheses bool } -func (list *listClause) SerializeSql(out *bytes.Buffer) error { +func (list *listClause) SerializeSql(out *bytes.Buffer, options ...serializeOption) error { if list.includeParentheses { _ = out.WriteByte('(') } @@ -162,7 +162,7 @@ type funcExpression struct { args *listClause } -func (c *funcExpression) SerializeSql(out *bytes.Buffer) (err error) { +func (c *funcExpression) SerializeSql(out *bytes.Buffer, options ...serializeOption) (err error) { if !validIdentifierName(c.funcName) { return errors.Newf( "Invalid function name: %s. Generated sql: %s", @@ -205,7 +205,7 @@ type intervalExpression struct { var intervalSep = ":" -func (c *intervalExpression) SerializeSql(out *bytes.Buffer) (err error) { +func (c *intervalExpression) SerializeSql(out *bytes.Buffer, options ...serializeOption) (err error) { hours := c.duration / time.Hour minutes := (c.duration % time.Hour) / time.Minute sec := (c.duration % time.Minute) / time.Second @@ -336,7 +336,7 @@ type ifExpression struct { falseExpression Expression } -func (exp *ifExpression) SerializeSql(out *bytes.Buffer) error { +func (exp *ifExpression) SerializeSql(out *bytes.Buffer, options ...serializeOption) error { _, _ = out.WriteString("IF(") _ = exp.conditional.SerializeSql(out) _, _ = out.WriteString(",") diff --git a/sqlbuilder/func.go b/sqlbuilder/func.go index 7d9280a..08fbfb2 100644 --- a/sqlbuilder/func.go +++ b/sqlbuilder/func.go @@ -3,15 +3,13 @@ package sqlbuilder import "bytes" type FuncExpression struct { - isProjection - name string expression Expression alias string } -func (f *FuncExpression) As(alias string) Projection { +func (f *FuncExpression) As(alias string) Clause { newFuncExpression := *f newFuncExpression.alias = alias @@ -19,7 +17,7 @@ func (f *FuncExpression) As(alias string) Projection { return &newFuncExpression } -func (f *FuncExpression) SerializeSql(out *bytes.Buffer) error { +func (f *FuncExpression) SerializeSql(out *bytes.Buffer, options ...serializeOption) error { out.WriteString(f.name) out.WriteString("(") err := f.expression.SerializeSql(out) @@ -37,9 +35,9 @@ func (f *FuncExpression) SerializeSql(out *bytes.Buffer) error { return nil } -func (f *FuncExpression) SerializeSqlForColumnList(out *bytes.Buffer) error { - return f.SerializeSql(out) -} +//func (f *FuncExpression) SerializeSqlForColumnList(out *bytes.Buffer) error { +// return f.SerializeSql(out) +//} func MAX(expression Expression) *FuncExpression { return &FuncExpression{ diff --git a/sqlbuilder/integer_expression.go b/sqlbuilder/integer_expression.go new file mode 100644 index 0000000..0c74c6c --- /dev/null +++ b/sqlbuilder/integer_expression.go @@ -0,0 +1,82 @@ +package sqlbuilder + +type IntegerExpression interface { + NumericExpression + + //AddInt(value int) IntegerExpression + //AddInt64(value int) IntegerExpression + + BitAnd(expression IntegerExpression) IntegerExpression + BitOr(expression IntegerExpression) IntegerExpression + BitXor(expression IntegerExpression) IntegerExpression + BitNot() IntegerExpression +} + +type integerInterfaceImpl struct { + parent IntegerExpression +} + +//func (i *integerInterfaceImpl) AddInt(expression IntegerExpression) IntegerExpression { +// return NewBinaryIntegerExpression(i.parent, expression, " & ") +//} +// +//func (i *integerInterfaceImpl) AddInt64(expression IntegerExpression) IntegerExpression { +// return NewBinaryIntegerExpression(i.parent, expression, " & ") +//} + +func (i *integerInterfaceImpl) BitAnd(expression IntegerExpression) IntegerExpression { + return NewBinaryIntegerExpression(i.parent, expression, " & ") +} + +func (i *integerInterfaceImpl) BitOr(expression IntegerExpression) IntegerExpression { + return NewBinaryIntegerExpression(i.parent, expression, " | ") +} + +func (i *integerInterfaceImpl) BitXor(expression IntegerExpression) IntegerExpression { + return NewBinaryIntegerExpression(i.parent, expression, " # ") +} + +func (i *integerInterfaceImpl) BitNot() IntegerExpression { + return NewPrefixIntegerExpression(i.parent, " ~") +} + +//---------------------------------------------------// +type binaryIntegerExpression struct { + expressionInterfaceImpl + numericInterfaceImpl + integerInterfaceImpl + + binaryExpression +} + +func NewBinaryIntegerExpression(lhs, rhs IntegerExpression, operator string) IntegerExpression { + integerExpression := binaryIntegerExpression{} + + integerExpression.expressionInterfaceImpl.parent = &integerExpression + integerExpression.numericInterfaceImpl.parent = &integerExpression + integerExpression.integerInterfaceImpl.parent = &integerExpression + + integerExpression.binaryExpression = newBinaryExpression(lhs, rhs, []byte(operator)) + + return &integerExpression +} + +//---------------------------------------------------// +type prefixIntegerExpression struct { + expressionInterfaceImpl + numericInterfaceImpl + integerInterfaceImpl + + prefixExpression +} + +func NewPrefixIntegerExpression(expression IntegerExpression, operator string) IntegerExpression { + integerExpression := prefixIntegerExpression{} + integerExpression.prefixExpression = newPrefixExpression(expression, []byte(operator)) + + integerExpression.expressionInterfaceImpl.parent = &integerExpression + integerExpression.numericInterfaceImpl.parent = &integerExpression + integerExpression.integerInterfaceImpl.parent = &integerExpression + + return &integerExpression +} diff --git a/sqlbuilder/numeric_expression.go b/sqlbuilder/numeric_expression.go new file mode 100644 index 0000000..688be82 --- /dev/null +++ b/sqlbuilder/numeric_expression.go @@ -0,0 +1,94 @@ +package sqlbuilder + +import ( + "github.com/dropbox/godropbox/database/sqltypes" + "github.com/pkg/errors" +) + +type NumericExpression interface { + Expression + + Eq(expression NumericExpression) BoolExpression + NotEq(expression NumericExpression) BoolExpression + GtEq(rhs NumericExpression) BoolExpression + LtEq(rhs NumericExpression) BoolExpression + + Add(expression NumericExpression) NumericExpression + Sub(expression NumericExpression) NumericExpression + Mul(expression NumericExpression) NumericExpression + Div(expression NumericExpression) NumericExpression +} + +type numericInterfaceImpl struct { + parent NumericExpression +} + +func (n *numericInterfaceImpl) Eq(expression NumericExpression) BoolExpression { + return Eq(n.parent, expression) +} + +func (n *numericInterfaceImpl) NotEq(expression NumericExpression) BoolExpression { + return Neq(n.parent, expression) +} + +func (n *numericInterfaceImpl) GtEq(expression NumericExpression) BoolExpression { + return Gte(n.parent, expression) +} + +func (n *numericInterfaceImpl) LtEq(expression NumericExpression) BoolExpression { + return Lte(n.parent, expression) +} + +func (n *numericInterfaceImpl) Add(expression NumericExpression) NumericExpression { + return newBinaryNumericExpression(n.parent, expression, []byte(" + ")) +} + +func (n *numericInterfaceImpl) Sub(expression NumericExpression) NumericExpression { + return newBinaryNumericExpression(n.parent, expression, []byte(" - ")) +} + +func (n *numericInterfaceImpl) Mul(expression NumericExpression) NumericExpression { + return newBinaryNumericExpression(n.parent, expression, []byte(" * ")) +} + +func (n *numericInterfaceImpl) Div(expression NumericExpression) NumericExpression { + return newBinaryNumericExpression(n.parent, expression, []byte(" / ")) +} + +//---------------------------------------------------// +type numericLiteral struct { + numericInterfaceImpl + literalExpression +} + +func NewNumericLiteral(value interface{}) NumericExpression { + numericLiteral := numericLiteral{} + + sqlValue, err := sqltypes.BuildValue(value) + if err != nil { + panic(errors.Wrap(err, "Invalid literal value")) + } + numericLiteral.literalExpression = *NewLiteralExpression(sqlValue) + numericLiteral.numericInterfaceImpl.parent = &numericLiteral + + return &numericLiteral +} + +//---------------------------------------------------// +type binaryNumericExpression struct { + expressionInterfaceImpl + numericInterfaceImpl + + binaryExpression +} + +func newBinaryNumericExpression(lhs, rhs Expression, operator []byte) NumericExpression { + numericExpression := binaryNumericExpression{} + + numericExpression.binaryExpression = newBinaryExpression(lhs, rhs, operator) + + numericExpression.expressionInterfaceImpl.parent = &numericExpression + numericExpression.numericInterfaceImpl.parent = &numericExpression + + return &numericExpression +} diff --git a/sqlbuilder/select_statement.go b/sqlbuilder/select_statement.go index d8797ce..0525e7c 100644 --- a/sqlbuilder/select_statement.go +++ b/sqlbuilder/select_statement.go @@ -36,7 +36,7 @@ type selectStatementImpl struct { expressionInterfaceImpl table ReadableTable - projections []Projection + projections []Expression where BoolExpression group *listClause having BoolExpression @@ -50,7 +50,7 @@ type selectStatementImpl struct { func newSelectStatement( table ReadableTable, - projections []Projection) SelectStatement { + projections []Expression) SelectStatement { return &selectStatementImpl{ table: table, @@ -63,7 +63,7 @@ func newSelectStatement( } } -func (s *selectStatementImpl) SerializeSql(out *bytes.Buffer) error { +func (s *selectStatementImpl) SerializeSql(out *bytes.Buffer, options ...serializeOption) error { str, err := s.String() if err != nil { @@ -210,7 +210,7 @@ func (q *selectStatementImpl) String() (sql string, err error) { "nil column selected. Generated sql: %s", buf.String()) } - if err = col.SerializeSqlForColumnList(buf); err != nil { + if err = col.SerializeSql(buf, FOR_PROJECTION); err != nil { return } } diff --git a/sqlbuilder/select_statement_table.go b/sqlbuilder/select_statement_table.go index 8dcf5d4..7edff79 100644 --- a/sqlbuilder/select_statement_table.go +++ b/sqlbuilder/select_statement_table.go @@ -4,22 +4,22 @@ import "bytes" type SelectStatementTable struct { statement SelectStatement - columns []NonAliasColumn + columns []Column alias string } -func (s *SelectStatementTable) Columns() []NonAliasColumn { +func (s *SelectStatementTable) Columns() []Column { return s.columns } -func (s *SelectStatementTable) Column(name string) NonAliasColumn { +func (s *SelectStatementTable) Column(name string) Column { return &baseColumn{ name: name, tableName: s.alias, } } -func (s *SelectStatementTable) ColumnFrom(column NonAliasColumn) NonAliasColumn { +func (s *SelectStatementTable) ColumnFrom(column Column) Column { return &baseColumn{ name: column.TableName() + "." + column.Name(), tableName: s.alias, @@ -43,7 +43,7 @@ func (s *SelectStatementTable) SerializeSql(out *bytes.Buffer) error { } // Generates a select query on the current tableName. -func (s *SelectStatementTable) Select(projections ...Projection) SelectStatement { +func (s *SelectStatementTable) Select(projections ...Expression) SelectStatement { return newSelectStatement(s, projections) } @@ -52,9 +52,9 @@ func (s *SelectStatementTable) InnerJoinOn(table ReadableTable, onCondition Bool return InnerJoinOn(s, table, onCondition) } -func (s *SelectStatementTable) InnerJoinUsing(table ReadableTable, col1 Column, col2 Column) ReadableTable { - return InnerJoinOn(s, table, col1.Eq(col2)) -} +//func (s *SelectStatementTable) InnerJoinUsing(table ReadableTable, col1 Column, col2 Column) ReadableTable { +// return InnerJoinOn(s, table, col1.Eq(col2)) +//} // Creates a left join tableName expression using onCondition. func (s *SelectStatementTable) LeftJoinOn(table ReadableTable, onCondition BoolExpression) ReadableTable { @@ -66,8 +66,8 @@ func (s *SelectStatementTable) RightJoinOn(table ReadableTable, onCondition Bool return RightJoinOn(s, table, onCondition) } -func (s *SelectStatementTable) FullJoin(table ReadableTable, col1 Column, col2 Column) ReadableTable { - return FullJoin(s, table, col1.Eq(col2)) +func (s *SelectStatementTable) FullJoin(table ReadableTable, onCondition BoolExpression) ReadableTable { + return FullJoin(s, table, onCondition) } func (s *SelectStatementTable) CrossJoin(table ReadableTable) ReadableTable { diff --git a/sqlbuilder/statement.go b/sqlbuilder/statement.go index 1c244a3..9d6c989 100644 --- a/sqlbuilder/statement.go +++ b/sqlbuilder/statement.go @@ -20,7 +20,7 @@ type InsertStatement interface { // Add a row of values to the insert statement. Add(row ...Expression) InsertStatement - AddOnDuplicateKeyUpdate(col NonAliasColumn, expr Expression) InsertStatement + AddOnDuplicateKeyUpdate(col Column, expr Expression) InsertStatement Comment(comment string) InsertStatement IgnoreDuplicates(ignore bool) InsertStatement } @@ -48,7 +48,7 @@ type UnionStatement interface { type UpdateStatement interface { Statement - Set(column NonAliasColumn, expression Expression) UpdateStatement + Set(column Column, expression Expression) UpdateStatement Where(expression BoolExpression) UpdateStatement OrderBy(clauses ...OrderByClause) UpdateStatement Limit(limit int64) UpdateStatement @@ -178,7 +178,7 @@ func (us *unionStatementImpl) String() (sql string, err error) { } // Union statements in MySQL require that the same number of columns in each subquery - var projections []Projection + var projections []Expression for _, statement := range us.selects { // do a type assertion to get at the underlying struct @@ -267,7 +267,7 @@ func (us *unionStatementImpl) String() (sql string, err error) { func newInsertStatement( t WritableTable, - columns ...NonAliasColumn) InsertStatement { + columns ...Column) InsertStatement { return &insertStatementImpl{ table: t, @@ -278,13 +278,13 @@ func newInsertStatement( } type columnAssignment struct { - col NonAliasColumn + col Column expr Expression } type insertStatementImpl struct { table WritableTable - columns []NonAliasColumn + columns []Column rows [][]Expression onDuplicateKeyUpdates []columnAssignment comment string @@ -303,7 +303,7 @@ func (s *insertStatementImpl) Add( } func (s *insertStatementImpl) AddOnDuplicateKeyUpdate( - col NonAliasColumn, + col Column, expr Expression) InsertStatement { s.onDuplicateKeyUpdates = append( @@ -361,7 +361,7 @@ func (s *insertStatementImpl) String() (sql string, err error) { buf.String()) } - if err = col.SerializeSqlForColumnList(buf); err != nil { + if err = col.SerializeSql(buf, FOR_PROJECTION); err != nil { return } } @@ -413,12 +413,11 @@ func (s *insertStatementImpl) String() (sql string, err error) { if colExpr.col == nil { return "", errors.Newf( - ("nil column in on duplicate key update list. " + - "Generated sql: %s"), + "nil column in on duplicate key update list. "+"Generated sql: %s", buf.String()) } - if err = colExpr.col.SerializeSqlForColumnList(buf); err != nil { + if err = colExpr.col.SerializeSql(buf, FOR_PROJECTION); err != nil { return } @@ -426,8 +425,7 @@ func (s *insertStatementImpl) String() (sql string, err error) { if colExpr.expr == nil { return "", errors.Newf( - ("nil expression in on duplicate key update list. " + - "Generated sql: %s"), + "nil expression in on duplicate key update list. "+"Generated sql: %s", buf.String()) } @@ -447,14 +445,14 @@ func (s *insertStatementImpl) String() (sql string, err error) { func newUpdateStatement(table WritableTable) UpdateStatement { return &updateStatementImpl{ table: table, - updateValues: make(map[NonAliasColumn]Expression), + updateValues: make(map[Column]Expression), limit: -1, } } type updateStatementImpl struct { table WritableTable - updateValues map[NonAliasColumn]Expression + updateValues map[Column]Expression where BoolExpression order *listClause limit int64 @@ -466,7 +464,7 @@ func (u *updateStatementImpl) Execute(db *sql.DB, data interface{}) error { } func (u *updateStatementImpl) Set( - column NonAliasColumn, + column Column, expression Expression) UpdateStatement { u.updateValues[column] = expression diff --git a/sqlbuilder/table.go b/sqlbuilder/table.go index ddfdbe6..99c7810 100644 --- a/sqlbuilder/table.go +++ b/sqlbuilder/table.go @@ -12,22 +12,21 @@ import ( // are not supported. type ReadableTable interface { // Returns the list of columns that are in the current tableName expression. - Columns() []NonAliasColumn + Columns() []Column - Column(name string) NonAliasColumn + Column(name string) Column // Generates the sql string for the current tableName expression. Note: the // generated string may not be a valid/executable sql statement. - // The database is the name of the database the tableName is on SerializeSql(out *bytes.Buffer) error // Generates a select query on the current tableName. - Select(projections ...Projection) SelectStatement + Select(projections ...Expression) SelectStatement // Creates a inner join tableName expression using onCondition. InnerJoinOn(table ReadableTable, onCondition BoolExpression) ReadableTable - InnerJoinUsing(table ReadableTable, col1 Column, col2 Column) ReadableTable + //InnerJoinUsing(table ReadableTable, col1 Column, col2 Column) ReadableTable // Creates a left join tableName expression using onCondition. LeftJoinOn(table ReadableTable, onCondition BoolExpression) ReadableTable @@ -35,7 +34,7 @@ type ReadableTable interface { // Creates a right join tableName expression using onCondition. RightJoinOn(table ReadableTable, onCondition BoolExpression) ReadableTable - FullJoin(table ReadableTable, col1 Column, col2 Column) ReadableTable + FullJoin(table ReadableTable, onCondition BoolExpression) ReadableTable CrossJoin(table ReadableTable) ReadableTable } @@ -43,21 +42,21 @@ type ReadableTable interface { // The sql tableName write interface. type WritableTable interface { // Returns the list of columns that are in the tableName. - Columns() []NonAliasColumn + Columns() []Column // Generates the sql string for the current tableName expression. Note: the // generated string may not be a valid/executable sql statement. // The database is the name of the database the tableName is on SerializeSql(out *bytes.Buffer) error - Insert(columns ...NonAliasColumn) InsertStatement + Insert(columns ...Column) InsertStatement Update() UpdateStatement Delete() DeleteStatement } // Defines a physical tableName in the database that is both readable and writable. // This function will panic if name is not valid -func NewTable(schemaName, name string, columns ...NonAliasColumn) *Table { +func NewTable(schemaName, name string, columns ...Column) *Table { if !validIdentifierName(name) { panic("Invalid tableName name") } @@ -66,7 +65,7 @@ func NewTable(schemaName, name string, columns ...NonAliasColumn) *Table { schemaName: schemaName, name: name, columns: columns, - columnLookup: make(map[string]NonAliasColumn), + columnLookup: make(map[string]Column), } for _, c := range columns { err := c.setTableName(name) @@ -87,21 +86,21 @@ type Table struct { schemaName string name string alias string - columns []NonAliasColumn - columnLookup map[string]NonAliasColumn + columns []Column + columnLookup map[string]Column // If not empty, the name of the index to force forcedIndex string } // Returns the specified column, or errors if it doesn't exist in the tableName -func (t *Table) getColumn(name string) (NonAliasColumn, error) { +func (t *Table) getColumn(name string) (Column, error) { if c, ok := t.columnLookup[name]; ok { return c, nil } return nil, errors.Newf("No such column '%s' in tableName '%s'", name, t.name) } -func (t *Table) Column(name string) NonAliasColumn { +func (t *Table) Column(name string) Column { return &baseColumn{ name: name, nullable: NotNullable, @@ -109,9 +108,9 @@ func (t *Table) Column(name string) NonAliasColumn { } } -// Returns all columns for a tableName as a slice of projections -func (t *Table) Projections() []Projection { - result := make([]Projection, 0) +// Returns all expresssion for a tableName as a slice of projections +func (t *Table) Projections() []Expression { + result := make([]Expression, 0) for _, col := range t.columns { col.Asc() @@ -142,7 +141,7 @@ func (t *Table) SchemaName() string { } // Returns a list of the tableName's columns -func (t *Table) Columns() []NonAliasColumn { +func (t *Table) Columns() []Column { return t.columns } @@ -182,7 +181,7 @@ func (t *Table) SerializeSql(out *bytes.Buffer) error { } // Generates a select query on the current tableName. -func (t *Table) Select(projections ...Projection) SelectStatement { +func (t *Table) Select(projections ...Expression) SelectStatement { return newSelectStatement(t, projections) } @@ -194,13 +193,13 @@ func (t *Table) InnerJoinOn( return InnerJoinOn(t, table, onCondition) } -func (t *Table) InnerJoinUsing( - table ReadableTable, - col1 Column, - col2 Column) ReadableTable { - - return InnerJoinOn(t, table, col1.Eq(col2)) -} +//func (t *Table) InnerJoinUsing( +// table ReadableTable, +// col1 Column, +// col2 Column) ReadableTable { +// +// return InnerJoinOn(t, table, col1.Eq(col2)) +//} // Creates a left join tableName expression using onCondition. func (t *Table) LeftJoinOn( @@ -218,15 +217,15 @@ func (t *Table) RightJoinOn( return RightJoinOn(t, table, onCondition) } -func (t *Table) FullJoin(table ReadableTable, col1, col2 Column) ReadableTable { - return FullJoin(t, table, col1.Eq(col2)) +func (t *Table) FullJoin(table ReadableTable, onCondition BoolExpression) ReadableTable { + return FullJoin(t, table, onCondition) } func (t *Table) CrossJoin(table ReadableTable) ReadableTable { return CrossJoin(t, table) } -func (t *Table) Insert(columns ...NonAliasColumn) InsertStatement { +func (t *Table) Insert(columns ...Column) InsertStatement { return newInsertStatement(t, columns...) } @@ -309,15 +308,15 @@ func CrossJoin( return newJoinTable(lhs, rhs, CROSS_JOIN, nil) } -func (t *joinTable) Columns() []NonAliasColumn { - columns := make([]NonAliasColumn, 0) +func (t *joinTable) Columns() []Column { + columns := make([]Column, 0) columns = append(columns, t.lhs.Columns()...) columns = append(columns, t.rhs.Columns()...) return columns } -func (t *joinTable) Column(name string) NonAliasColumn { +func (t *joinTable) Column(name string) Column { panic("Not implemented") } @@ -364,7 +363,7 @@ func (t *joinTable) SerializeSql(out *bytes.Buffer) (err error) { return nil } -func (t *joinTable) Select(projections ...Projection) SelectStatement { +func (t *joinTable) Select(projections ...Expression) SelectStatement { return newSelectStatement(t, projections) } @@ -375,14 +374,6 @@ func (t *joinTable) InnerJoinOn( return InnerJoinOn(t, table, onCondition) } -func (t *joinTable) InnerJoinUsing( - table ReadableTable, - col1 Column, - col2 Column) ReadableTable { - - return InnerJoinOn(t, table, col1.Eq(col2)) -} - func (t *joinTable) LeftJoinOn( table ReadableTable, onCondition BoolExpression) ReadableTable { @@ -390,8 +381,8 @@ func (t *joinTable) LeftJoinOn( return LeftJoinOn(t, table, onCondition) } -func (t *joinTable) FullJoin(table ReadableTable, col1 Column, col2 Column) ReadableTable { - return FullJoin(t, table, col1.Eq(col2)) +func (t *joinTable) FullJoin(table ReadableTable, onCondition BoolExpression) ReadableTable { + return FullJoin(t, table, onCondition) } func (t *joinTable) CrossJoin(table ReadableTable) ReadableTable { diff --git a/sqlbuilder/test_utils.go b/sqlbuilder/test_utils.go index 5395f0f..1bcd0ec 100644 --- a/sqlbuilder/test_utils.go +++ b/sqlbuilder/test_utils.go @@ -1,3 +1,5 @@ +// +build disabled + package sqlbuilder var table1Col1 = IntColumn("col1", Nullable) diff --git a/sqlbuilder/types.go b/sqlbuilder/types.go index f358d82..8ce595d 100644 --- a/sqlbuilder/types.go +++ b/sqlbuilder/types.go @@ -1,9 +1,5 @@ package sqlbuilder -import ( - "bytes" -) - // A clause that can be used in order by type OrderByClause interface { Clause @@ -11,43 +7,43 @@ type OrderByClause interface { } // A clause that is selectable. -type Projection interface { - Clause - isProjectionInterface +//type Projection interface { +// Clause +// isProjectionInterface +// +// SerializeSqlForColumnList(out *bytes.Buffer) error +//} - SerializeSqlForColumnList(out *bytes.Buffer) error -} +//type ColumnList []Column +// +//func (cl ColumnList) SerializeSql(out *bytes.Buffer, options ...serializeOption) error { +// for i, column := range cl { +// column.SerializeSql(out) +// +// if i != len(cl)-1 { +// out.WriteString(", ") +// } +// } +// return nil +//} +// +//func (cl ColumnList) isProjectionType() { +//} +// +//func (cl ColumnList) As(name string) Clause { +// panic("Unallowed operation ") +//} -type ColumnList []NonAliasColumn - -func (cl ColumnList) SerializeSql(out *bytes.Buffer) error { - for i, column := range cl { - column.SerializeSql(out) - - if i != len(cl)-1 { - out.WriteString(", ") - } - } - return nil -} - -func (cl ColumnList) isProjectionType() { -} - -func (cl ColumnList) As(name string) Projection { - panic("Unallowed operation ") -} - -func (cl ColumnList) SerializeSqlForColumnList(out *bytes.Buffer) error { - for i, column := range cl { - column.SerializeSqlForColumnList(out) - - if i != len(cl)-1 { - out.WriteString(", ") - } - } - return nil -} +//func (cl ColumnList) SerializeSqlForColumnList(out *bytes.Buffer) error { +// for i, column := range cl { +// column.SerializeSqlForColumnList(out) +// +// if i != len(cl)-1 { +// out.WriteString(", ") +// } +// } +// return nil +//} // // Boiler plates ... @@ -63,12 +59,13 @@ type isOrderByClause struct { func (o *isOrderByClause) isOrderByClauseType() { } -type isProjectionInterface interface { - isProjectionType() -} - -type isProjection struct { -} - -func (p *isProjection) isProjectionType() { -} +// +//type isProjectionInterface interface { +// isProjectionType() +//} +// +//type isProjection struct { +//} +// +//func (p *isProjection) isProjectionType() { +//} diff --git a/sqlbuilder/utils.go b/sqlbuilder/utils.go new file mode 100644 index 0000000..3896e39 --- /dev/null +++ b/sqlbuilder/utils.go @@ -0,0 +1 @@ +package sqlbuilder