diff --git a/generator/postgres-metadata/column_info.go b/generator/postgres-metadata/column_info.go index 97e950f..d23a745 100644 --- a/generator/postgres-metadata/column_info.go +++ b/generator/postgres-metadata/column_info.go @@ -17,27 +17,27 @@ type ColumnInfo struct { func (c ColumnInfo) SqlBuilderColumnType() string { switch c.DataType { case "boolean": - return "BoolColumn" + return "Bool" case "smallint", "integer", "bigint": - return "IntegerColumn" + return "Integer" case "date": - return "DateColumn" + return "Date" case "timestamp without time zone": - return "TimestampColumn" + return "Timestamp" case "timestamp with time zone": - return "TimestampzColumn" + return "Timestampz" case "time without time zone": - return "TimeColumn" + return "Time" case "time with time zone": - return "TimezColumn" + return "Timez" case "USER-DEFINED", "text", "character", "character varying", "bytea", "uuid", "tsvector", "bit", "bit varying", "money", "json", "jsonb", "xml", "point", "interval", "line", "ARRAY": - return "StringColumn" + return "String" case "real", "numeric", "decimal", "double precision": - return "FloatColumn" + return "Float" default: fmt.Println("Unknown sql type: " + c.DataType + ", using string column instead for sql builder.") - return "StringColumn" + return "String" } } diff --git a/generator/templates.go b/generator/templates.go index ca15139..8b181be 100644 --- a/generator/templates.go +++ b/generator/templates.go @@ -31,7 +31,7 @@ type {{.GoStructName}} struct { //Columns {{- range .Columns}} - {{camelize .Name}} *sqlbuilder.{{.SqlBuilderColumnType}} + {{camelize .Name}} sqlbuilder.Column{{.SqlBuilderColumnType}} {{- end}} AllColumns sqlbuilder.ColumnList @@ -42,7 +42,7 @@ var {{camelize .Name}} = new{{.GoStructName}}() func new{{.GoStructName}}() *{{.GoStructName}} { var ( {{- range .Columns}} - {{camelize .Name}}Column = sqlbuilder.New{{.SqlBuilderColumnType}}("{{.Name}}", {{.IsNullable}}) + {{camelize .Name}}Column = sqlbuilder.{{.SqlBuilderColumnType}}Column("{{.Name}}") {{- end}} ) diff --git a/sqlbuilder/column.go b/sqlbuilder/column.go index 642f8cd..bd5b14a 100644 --- a/sqlbuilder/column.go +++ b/sqlbuilder/column.go @@ -6,32 +6,34 @@ import ( "strings" ) -type Column interface { - Expression - +type column interface { Name() string TableName() string - IsNullable() bool - DefaultAlias() projection + // Internal function for tracking tableName that a column belongs to // for the purpose of serialization setTableName(table string) + defaultAlias() string + defaultAliasProjection() projection +} + +type Column interface { + Expression + column } // The base type for real materialized columns. -type baseColumn struct { +type columnImpl struct { expressionInterfaceImpl - name string - isNullable bool - tableName string + name string + tableName string } -func newBaseColumn(name string, isNullable bool, tableName string, parent Column) baseColumn { - bc := baseColumn{ - name: name, - isNullable: isNullable, - tableName: tableName, +func newColumn(name string, tableName string, parent Column) columnImpl { + bc := columnImpl{ + name: name, + tableName: tableName, } bc.expressionInterfaceImpl.parent = parent @@ -39,27 +41,31 @@ func newBaseColumn(name string, isNullable bool, tableName string, parent Column return bc } -func (c *baseColumn) Name() string { +func (c *columnImpl) Name() string { return c.name } -func (c *baseColumn) TableName() string { +func (c *columnImpl) TableName() string { return c.tableName } -func (c *baseColumn) setTableName(table string) { +func (c *columnImpl) setTableName(table string) { c.tableName = table } -func (c *baseColumn) IsNullable() bool { - return c.isNullable +func (c *columnImpl) defaultAlias() string { + if c.tableName != "" { + return c.tableName + "." + c.name + } + + return c.name } -func (c *baseColumn) DefaultAlias() projection { - return c.AS(c.tableName + "." + c.name) +func (c *columnImpl) defaultAliasProjection() projection { + return c.AS(c.defaultAlias()) } -func (c *baseColumn) serializeAsOrderBy(statement statementType, out *queryData) error { +func (c *columnImpl) serializeAsOrderBy(statement statementType, out *queryData) error { if statement == set_statement { // set Statement (UNION, EXCEPT ...) can reference only select projections in order by clause columnRef := "" @@ -78,7 +84,7 @@ func (c *baseColumn) serializeAsOrderBy(statement statementType, out *queryData) return c.serialize(statement, out) } -func (c baseColumn) serialize(statement statementType, out *queryData, options ...serializeOption) error { +func (c columnImpl) serialize(statement statementType, out *queryData, options ...serializeOption) error { columnRef := "" diff --git a/sqlbuilder/column_test.go b/sqlbuilder/column_test.go index f3bb2eb..971919d 100644 --- a/sqlbuilder/column_test.go +++ b/sqlbuilder/column_test.go @@ -1,210 +1,14 @@ -// +build disabled - package sqlbuilder -import ( - "bytes" - "testing" +import "testing" - gc "gopkg.in/check.v1" -) +func TestColumn(t *testing.T) { + column := newColumn("col", "", nil) + column.expressionInterfaceImpl.parent = &column -func Test(t *testing.T) { - gc.TestingT(t) -} - -type ColumnSuite struct { -} - -var _ = gc.Suite(&ColumnSuite{}) - -// -// tests for baseColumn and columns that extends baseColumn -// - -func (s *ColumnSuite) TestRealColumnName(c *gc.C) { - col := IntColumn("col", Nullable) - - c.Assert(col.Name(), gc.Equals, "col") -} - -func (s *ColumnSuite) TestRealColumnSerializeSqlForColumnList(c *gc.C) { - col := IntColumn("col", Nullable) - - // Without tableName name - buf := &bytes.Buffer{} - - err := col.SerializeSqlForColumnList(buf) - c.Assert(err, gc.IsNil) - - sql := buf.String() - c.Assert(sql, gc.Equals, "col") - - // With tableName name - err = col.setTableName("foo") - c.Assert(err, gc.IsNil) - - buf = &bytes.Buffer{} - - err = col.SerializeSqlForColumnList(buf) - c.Assert(err, gc.IsNil) - - sql = buf.String() - c.Assert(sql, gc.Equals, "foo.col") -} - -func (s *ColumnSuite) TestRealColumnSerializeSql(c *gc.C) { - col := IntColumn("col", Nullable) - - // Without tableName name - buf := &bytes.Buffer{} - - err := col.SerializeSql(buf) - c.Assert(err, gc.IsNil) - - sql := buf.String() - c.Assert(sql, gc.Equals, "col") - - // With tableName name - err = col.setTableName("foo") - c.Assert(err, gc.IsNil) - - buf = &bytes.Buffer{} - - err = col.SerializeSql(buf) - c.Assert(err, gc.IsNil) - - sql = buf.String() - c.Assert(sql, gc.Equals, "foo.col") -} - -// -// tests for AliasCoulmns -// - -func (s *ColumnSuite) TestAliasColumnName(c *gc.C) { - col := Alias("foo", SqlFunc("max", table1Col1)) - - c.Assert(col.Name(), gc.Equals, "foo") -} - -func (s *ColumnSuite) TestAliasColumnSerializeSqlForColumnList(c *gc.C) { - col := Alias("foo", SqlFunc("max", table1Col1)) - - buf := &bytes.Buffer{} - err := col.SerializeSqlForColumnList(buf) - c.Assert(err, gc.IsNil) - - sql := buf.String() - c.Assert(err, gc.IsNil) - - c.Assert(sql, gc.Equals, "(max(table1.col1)) AS \"foo\"") -} - -func (s *ColumnSuite) TestAliasColumnSerializeSqlForColumnListNilExpr(c *gc.C) { - col := Alias("foo", nil) - - buf := &bytes.Buffer{} - err := col.SerializeSqlForColumnList(buf) - c.Assert(err, gc.NotNil) -} - -func (s *ColumnSuite) TestAliasColumnSerializeSqlForColumnListInvalidAlias( - c *gc.C) { - - col := Alias("1234", SqlFunc("max", table1Col1)) - - buf := &bytes.Buffer{} - err := col.SerializeSqlForColumnList(buf) - c.Assert(err, gc.NotNil) -} - -func (s *ColumnSuite) TestAliasColumnSerializeSql(c *gc.C) { - col := Alias("foo", SqlFunc("max", table1Col1)) - - buf := &bytes.Buffer{} - err := col.SerializeSql(buf) - c.Assert(err, gc.IsNil) - - sql := buf.String() - c.Assert(sql, gc.Equals, "`foo`") -} - -func (s *ColumnSuite) TestAliasColumnSetTableName(c *gc.C) { - col := Alias("foo", SqlFunc("max", table1Col1)) - - // should always error - err := col.setTableName("test") - c.Assert(err, gc.NotNil) -} - -// -// tests for deferredLookkupColumnName -// - -func (s *ColumnSuite) TestDeferredLookupColumnName(c *gc.C) { - col := table1.C("foo") - - c.Assert(col.Name(), gc.Equals, "foo") -} - -func (s *ColumnSuite) TestDeferredLookupColumnSerializeSqlForColumnList( - c *gc.C) { - - col := table1.C("col1") - - buf := &bytes.Buffer{} - - err := col.SerializeSql(buf) - c.Assert(err, gc.IsNil) - - sql := buf.String() - c.Assert(sql, gc.Equals, "table1.col1") - - // check cached lookup - buf = &bytes.Buffer{} - - err = col.SerializeSql(buf) - c.Assert(err, gc.IsNil) - - sql = buf.String() - c.Assert(sql, gc.Equals, "table1.col1") -} - -func (s *ColumnSuite) TestDeferredLookupColumnSerializeSqlForColumnListInvalidName( - c *gc.C) { - col := table1.C("foo") - - buf := &bytes.Buffer{} - - err := col.SerializeSql(buf) - c.Assert(err, gc.NotNil) -} - -func (s *ColumnSuite) TestDeferredLookupColumnSerializeSql(c *gc.C) { - col := table1.C("col1") - - buf := &bytes.Buffer{} - - err := col.SerializeSql(buf) - c.Assert(err, gc.IsNil) - - sql := buf.String() - c.Assert(sql, gc.Equals, "table1.col1") -} - -func (s *ColumnSuite) TestDeferredLookupColumnSerializeSqlInvalidName(c *gc.C) { - col := table1.C("foo") - - buf := &bytes.Buffer{} - - err := col.SerializeSql(buf) - c.Assert(err, gc.NotNil) -} - -func (s *ColumnSuite) TestDeferredLookupColumnSetTableName(c *gc.C) { - col := table1.C("col1") - - err := col.setTableName("foo") - c.Assert(err, gc.NotNil) + assertClauseSerialize(t, column, "col") + column.setTableName("table1") + assertClauseSerialize(t, column, "table1.col") + assertProjectionSerialize(t, column.defaultAliasProjection(), `table1.col AS "table1.col"`) + assertProjectionSerialize(t, column.AS("alias1"), `table1.col AS "alias1"`) } diff --git a/sqlbuilder/column_test_old.go b/sqlbuilder/column_test_old.go new file mode 100644 index 0000000..5f4e47b --- /dev/null +++ b/sqlbuilder/column_test_old.go @@ -0,0 +1,210 @@ +// +build disabled + +package sqlbuilder + +import ( + "bytes" + "testing" + + gc "gopkg.in/check.v1" +) + +func Test(t *testing.T) { + gc.TestingT(t) +} + +type ColumnSuite struct { +} + +var _ = gc.Suite(&ColumnSuite{}) + +// +// tests for columnImpl and columns that extends columnImpl +// + +func (s *ColumnSuite) TestRealColumnName(c *gc.C) { + col := IntColumn("col", Nullable) + + c.Assert(col.Name(), gc.Equals, "col") +} + +func (s *ColumnSuite) TestRealColumnSerializeSqlForColumnList(c *gc.C) { + col := IntColumn("col", Nullable) + + // Without tableName name + buf := &bytes.Buffer{} + + err := col.SerializeSqlForColumnList(buf) + c.Assert(err, gc.IsNil) + + sql := buf.String() + c.Assert(sql, gc.Equals, "col") + + // With tableName name + err = col.setTableName("foo") + c.Assert(err, gc.IsNil) + + buf = &bytes.Buffer{} + + err = col.SerializeSqlForColumnList(buf) + c.Assert(err, gc.IsNil) + + sql = buf.String() + c.Assert(sql, gc.Equals, "foo.col") +} + +func (s *ColumnSuite) TestRealColumnSerializeSql(c *gc.C) { + col := IntColumn("col", Nullable) + + // Without tableName name + buf := &bytes.Buffer{} + + err := col.SerializeSql(buf) + c.Assert(err, gc.IsNil) + + sql := buf.String() + c.Assert(sql, gc.Equals, "col") + + // With tableName name + err = col.setTableName("foo") + c.Assert(err, gc.IsNil) + + buf = &bytes.Buffer{} + + err = col.SerializeSql(buf) + c.Assert(err, gc.IsNil) + + sql = buf.String() + c.Assert(sql, gc.Equals, "foo.col") +} + +// +// tests for AliasCoulmns +// + +func (s *ColumnSuite) TestAliasColumnName(c *gc.C) { + col := Alias("foo", SqlFunc("max", table1Col1)) + + c.Assert(col.Name(), gc.Equals, "foo") +} + +func (s *ColumnSuite) TestAliasColumnSerializeSqlForColumnList(c *gc.C) { + col := Alias("foo", SqlFunc("max", table1Col1)) + + buf := &bytes.Buffer{} + err := col.SerializeSqlForColumnList(buf) + c.Assert(err, gc.IsNil) + + sql := buf.String() + c.Assert(err, gc.IsNil) + + c.Assert(sql, gc.Equals, "(max(table1.col1)) AS \"foo\"") +} + +func (s *ColumnSuite) TestAliasColumnSerializeSqlForColumnListNilExpr(c *gc.C) { + col := Alias("foo", nil) + + buf := &bytes.Buffer{} + err := col.SerializeSqlForColumnList(buf) + c.Assert(err, gc.NotNil) +} + +func (s *ColumnSuite) TestAliasColumnSerializeSqlForColumnListInvalidAlias( + c *gc.C) { + + col := Alias("1234", SqlFunc("max", table1Col1)) + + buf := &bytes.Buffer{} + err := col.SerializeSqlForColumnList(buf) + c.Assert(err, gc.NotNil) +} + +func (s *ColumnSuite) TestAliasColumnSerializeSql(c *gc.C) { + col := Alias("foo", SqlFunc("max", table1Col1)) + + buf := &bytes.Buffer{} + err := col.SerializeSql(buf) + c.Assert(err, gc.IsNil) + + sql := buf.String() + c.Assert(sql, gc.Equals, "`foo`") +} + +func (s *ColumnSuite) TestAliasColumnSetTableName(c *gc.C) { + col := Alias("foo", SqlFunc("max", table1Col1)) + + // should always error + err := col.setTableName("test") + c.Assert(err, gc.NotNil) +} + +// +// tests for deferredLookkupColumnName +// + +func (s *ColumnSuite) TestDeferredLookupColumnName(c *gc.C) { + col := table1.C("foo") + + c.Assert(col.Name(), gc.Equals, "foo") +} + +func (s *ColumnSuite) TestDeferredLookupColumnSerializeSqlForColumnList( + c *gc.C) { + + col := table1.C("col1") + + buf := &bytes.Buffer{} + + err := col.SerializeSql(buf) + c.Assert(err, gc.IsNil) + + sql := buf.String() + c.Assert(sql, gc.Equals, "table1.col1") + + // check cached lookup + buf = &bytes.Buffer{} + + err = col.SerializeSql(buf) + c.Assert(err, gc.IsNil) + + sql = buf.String() + c.Assert(sql, gc.Equals, "table1.col1") +} + +func (s *ColumnSuite) TestDeferredLookupColumnSerializeSqlForColumnListInvalidName( + c *gc.C) { + col := table1.C("foo") + + buf := &bytes.Buffer{} + + err := col.SerializeSql(buf) + c.Assert(err, gc.NotNil) +} + +func (s *ColumnSuite) TestDeferredLookupColumnSerializeSql(c *gc.C) { + col := table1.C("col1") + + buf := &bytes.Buffer{} + + err := col.SerializeSql(buf) + c.Assert(err, gc.IsNil) + + sql := buf.String() + c.Assert(sql, gc.Equals, "table1.col1") +} + +func (s *ColumnSuite) TestDeferredLookupColumnSerializeSqlInvalidName(c *gc.C) { + col := table1.C("foo") + + buf := &bytes.Buffer{} + + err := col.SerializeSql(buf) + c.Assert(err, gc.NotNil) +} + +func (s *ColumnSuite) TestDeferredLookupColumnSetTableName(c *gc.C) { + col := table1.C("col1") + + err := col.setTableName("foo") + c.Assert(err, gc.NotNil) +} diff --git a/sqlbuilder/column_types.go b/sqlbuilder/column_types.go index 4a27da1..f3cb25f 100644 --- a/sqlbuilder/column_types.go +++ b/sqlbuilder/column_types.go @@ -1,16 +1,29 @@ package sqlbuilder //------------------------------------------------------// -type BoolColumn struct { - boolInterfaceImpl +type ColumnBool interface { + BoolExpression + column - baseColumn + From(table ExpressionTable) ColumnBool } -func NewBoolColumn(name string, isNullable bool) *BoolColumn { +type boolColumnImpl struct { + boolInterfaceImpl - boolColumn := &BoolColumn{} - boolColumn.baseColumn = newBaseColumn(name, isNullable, "", boolColumn) + columnImpl +} + +func (i *boolColumnImpl) From(table ExpressionTable) ColumnBool { + newBoolColumn := BoolColumn(i.defaultAlias()) + newBoolColumn.setTableName(table.Alias()) + return newBoolColumn +} + +func BoolColumn(name string) ColumnBool { + + boolColumn := &boolColumnImpl{} + boolColumn.columnImpl = newColumn(name, "", boolColumn) boolColumn.boolInterfaceImpl.parent = boolColumn @@ -18,164 +31,241 @@ func NewBoolColumn(name string, isNullable bool) *BoolColumn { } //------------------------------------------------------// -type FloatColumn struct { - floatInterfaceImpl - baseColumn +type ColumnFloat interface { + FloatExpression + column + + From(table ExpressionTable) ColumnFloat } -func NewFloatColumn(name string, isNullable bool) *FloatColumn { +type floatColumnImpl struct { + floatInterfaceImpl + columnImpl +} - floatColumn := &FloatColumn{} +func (i *floatColumnImpl) From(table ExpressionTable) ColumnFloat { + newFloatColumn := FloatColumn(i.defaultAlias()) + newFloatColumn.setTableName(table.Alias()) + return newFloatColumn +} + +func FloatColumn(name string) ColumnFloat { + + floatColumn := &floatColumnImpl{} floatColumn.floatInterfaceImpl.parent = floatColumn - - floatColumn.baseColumn = newBaseColumn(name, isNullable, "", floatColumn) + floatColumn.columnImpl = newColumn(name, "", floatColumn) return floatColumn } //------------------------------------------------------// -type IntegerColumn struct { - integerInterfaceImpl +type ColumnInteger interface { + IntegerExpression + column - baseColumn + From(table ExpressionTable) ColumnInteger } -// Representation of any integer column -// This function will panic if name is not valid -func NewIntegerColumn(name string, isNullable bool) *IntegerColumn { - integerColumn := &IntegerColumn{} +type integerColumnImpl struct { + integerInterfaceImpl + + columnImpl +} + +func (i *integerColumnImpl) From(table ExpressionTable) ColumnInteger { + newIntColumn := IntegerColumn(i.defaultAlias()) + newIntColumn.setTableName(table.Alias()) + return newIntColumn +} + +func IntegerColumn(name string) ColumnInteger { + integerColumn := &integerColumnImpl{} integerColumn.integerInterfaceImpl.parent = integerColumn - - integerColumn.baseColumn = newBaseColumn(name, isNullable, "", integerColumn) + integerColumn.columnImpl = newColumn(name, "", integerColumn) return integerColumn } //------------------------------------------------------// -type StringColumn struct { - stringInterfaceImpl +type ColumnString interface { + StringExpression + column - baseColumn + From(table ExpressionTable) ColumnString } -// Representation of any integer column -// This function will panic if name is not valid -func NewStringColumn(name string, isNullable bool) *StringColumn { +type stringColumnImpl struct { + stringInterfaceImpl - stringColumn := &StringColumn{} + columnImpl +} + +func (i *stringColumnImpl) From(table ExpressionTable) ColumnString { + newStrColumn := StringColumn(i.defaultAlias()) + newStrColumn.setTableName(table.Alias()) + return newStrColumn +} + +func StringColumn(name string) ColumnString { + + stringColumn := &stringColumnImpl{} stringColumn.stringInterfaceImpl.parent = stringColumn - stringColumn.baseColumn = newBaseColumn(name, isNullable, "", stringColumn) + stringColumn.columnImpl = newColumn(name, "", stringColumn) return stringColumn } //------------------------------------------------------// -type TimeColumn struct { - timeInterfaceImpl +type ColumnTime interface { + TimeExpression + column - baseColumn + From(table ExpressionTable) ColumnTime } -// Representation of any integer column -// This function will panic if name is not valid -func NewTimeColumn(name string, isNullable bool) *TimeColumn { - timeColumn := &TimeColumn{} +type timeColumnImpl struct { + timeInterfaceImpl + + columnImpl +} + +func (i *timeColumnImpl) From(table ExpressionTable) ColumnTime { + newTimeColumn := TimeColumn(i.defaultAlias()) + newTimeColumn.setTableName(table.Alias()) + return newTimeColumn +} + +func TimeColumn(name string) ColumnTime { + timeColumn := &timeColumnImpl{} timeColumn.timeInterfaceImpl.parent = timeColumn - timeColumn.baseColumn = newBaseColumn(name, isNullable, "", timeColumn) + timeColumn.columnImpl = newColumn(name, "", timeColumn) return timeColumn } //------------------------------------------------------// -type TimezColumn struct { - timezInterfaceImpl - baseColumn +type ColumnTimez interface { + TimezExpression + column + + From(table ExpressionTable) ColumnTimez } -// Representation of any integer column -// This function will panic if name is not valid -func NewTimezColumn(name string, isNullable bool) *TimezColumn { - timezColumn := &TimezColumn{} +type timezColumnImpl struct { + timezInterfaceImpl + + columnImpl +} + +func (i *timezColumnImpl) From(table ExpressionTable) ColumnTimez { + newTimezColumn := TimezColumn(i.defaultAlias()) + newTimezColumn.setTableName(table.Alias()) + return newTimezColumn +} + +func TimezColumn(name string) ColumnTimez { + timezColumn := &timezColumnImpl{} timezColumn.timezInterfaceImpl.parent = timezColumn - timezColumn.baseColumn = newBaseColumn(name, isNullable, "", timezColumn) + timezColumn.columnImpl = newColumn(name, "", timezColumn) return timezColumn } //------------------------------------------------------// -type TimestampColumn struct { - timestampInterfaceImpl +type ColumnTimestamp interface { + TimestampExpression + column - baseColumn + From(table ExpressionTable) ColumnTimestamp } -// Representation of any integer column -// This function will panic if name is not valid -func NewTimestampColumn(name string, isNullable bool) *TimestampColumn { - timestampColumn := &TimestampColumn{} +type timestampColumnImpl struct { + timestampInterfaceImpl + + columnImpl +} + +func (i *timestampColumnImpl) From(table ExpressionTable) ColumnTimestamp { + newTimestampColumn := TimestampColumn(i.defaultAlias()) + newTimestampColumn.setTableName(table.Alias()) + return newTimestampColumn +} + +func TimestampColumn(name string) ColumnTimestamp { + timestampColumn := ×tampColumnImpl{} timestampColumn.timestampInterfaceImpl.parent = timestampColumn - timestampColumn.baseColumn = newBaseColumn(name, isNullable, "", timestampColumn) + timestampColumn.columnImpl = newColumn(name, "", timestampColumn) return timestampColumn } //------------------------------------------------------// -type TimestampzColumn struct { - timestampzInterfaceImpl +type ColumnTimestampz interface { + TimestampzExpression + column - baseColumn + From(table ExpressionTable) ColumnTimestampz } -// Representation of any integer column -// This function will panic if name is not valid -func NewTimestampzColumn(name string, isNullable bool) *TimestampzColumn { - timestampzColumn := &TimestampzColumn{} +type timestampzColumnImpl struct { + timestampzInterfaceImpl + + columnImpl +} + +func (i *timestampzColumnImpl) From(table ExpressionTable) ColumnTimestampz { + newTimestampzColumn := TimestampzColumn(i.defaultAlias()) + newTimestampzColumn.setTableName(table.Alias()) + return newTimestampzColumn +} + +func TimestampzColumn(name string) ColumnTimestampz { + timestampzColumn := ×tampzColumnImpl{} timestampzColumn.timestampzInterfaceImpl.parent = timestampzColumn - timestampzColumn.baseColumn = newBaseColumn(name, isNullable, "", timestampzColumn) + timestampzColumn.columnImpl = newColumn(name, "", timestampzColumn) return timestampzColumn } //------------------------------------------------------// -type DateColumn struct { - dateInterfaceImpl +type ColumnDate interface { + DateExpression + column - baseColumn + From(table ExpressionTable) ColumnDate } -// Representation of any integer column -// This function will panic if name is not valid -func NewDateColumn(name string, isNullable bool) *DateColumn { - dateColumn := &DateColumn{} +type dateColumnImpl struct { + dateInterfaceImpl + + columnImpl +} + +func (i *dateColumnImpl) From(table ExpressionTable) ColumnDate { + newDateColumn := DateColumn(i.defaultAlias()) + newDateColumn.setTableName(table.Alias()) + return newDateColumn +} + +func DateColumn(name string) ColumnDate { + dateColumn := &dateColumnImpl{} dateColumn.dateInterfaceImpl.parent = dateColumn - dateColumn.baseColumn = newBaseColumn(name, isNullable, "", dateColumn) + dateColumn.columnImpl = newColumn(name, "", dateColumn) return dateColumn } - -// ------------------------------------------------------// -type refColumn struct { - baseColumn -} - -func RefColumn(name string) *refColumn { - refColumn := &refColumn{} - refColumn.baseColumn = newBaseColumn(name, false, "", refColumn) - - return refColumn -} diff --git a/sqlbuilder/column_types_test.go b/sqlbuilder/column_types_test.go index cc9fa9a..f6affb6 100644 --- a/sqlbuilder/column_types_test.go +++ b/sqlbuilder/column_types_test.go @@ -1,90 +1,45 @@ package sqlbuilder import ( - "gotest.tools/assert" "testing" ) +var subQuery = table1.SELECT(table1ColFloat, table1ColInt).AsTable("sub_query") + func TestNewBoolColumn(t *testing.T) { - boolColumn := NewBoolColumn("col", false) + boolColumn := BoolColumn("colBool").From(subQuery) + assertClauseSerialize(t, boolColumn, "sub_query.colBool") + assertClauseSerialize(t, boolColumn.EQ(Bool(true)), "(sub_query.colBool = $1)", true) + assertProjectionSerialize(t, boolColumn.defaultAliasProjection(), `sub_query.colBool AS "sub_query.colBool"`) - out := queryData{} - err := boolColumn.serialize(select_statement, &out) - - assert.NilError(t, err) - assert.Equal(t, out.buff.String(), "col") - - out.reset() - err = boolColumn.serialize(select_statement, &out) - assert.NilError(t, err) - assert.Equal(t, out.buff.String(), "col") - - out.reset() - boolColumn.setTableName("table1") - err = boolColumn.DefaultAlias().serializeForProjection(select_statement, &out) - assert.NilError(t, err) - assert.Equal(t, out.buff.String(), `table1.col AS "table1.col"`) - - out.reset() - boolColumn.setTableName("table1") - aliasedBoolColumn := boolColumn.AS("alias1") - err = aliasedBoolColumn.serializeForProjection(select_statement, &out) - assert.NilError(t, err) - assert.Equal(t, out.buff.String(), `table1.col AS "alias1"`) + boolColumn2 := table1ColBool.From(subQuery) + assertClauseSerialize(t, boolColumn2, `sub_query."table1.colBool"`) + assertClauseSerialize(t, boolColumn2.EQ(Bool(true)), `(sub_query."table1.colBool" = $1)`, true) + assertProjectionSerialize(t, boolColumn2.defaultAliasProjection(), `sub_query."table1.colBool" AS "sub_query.table1.colBool"`) } func TestNewIntColumn(t *testing.T) { - integerColumn := NewIntegerColumn("col", false) + intColumn := IntegerColumn("colInt").From(subQuery) + assertClauseSerialize(t, intColumn, "sub_query.colInt") + assertClauseSerialize(t, intColumn.EQ(Int(12)), "(sub_query.colInt = $1)", int64(12)) + assertProjectionSerialize(t, intColumn.defaultAliasProjection(), `sub_query.colInt AS "sub_query.colInt"`) - out := queryData{} - err := integerColumn.serialize(select_statement, &out) + intColumn2 := table1ColInt.From(subQuery) + assertClauseSerialize(t, intColumn2, `sub_query."table1.colInt"`) + assertClauseSerialize(t, intColumn2.EQ(Int(14)), `(sub_query."table1.colInt" = $1)`, int64(14)) + assertProjectionSerialize(t, intColumn2.defaultAliasProjection(), `sub_query."table1.colInt" AS "sub_query.table1.colInt"`) - assert.NilError(t, err) - assert.Equal(t, out.buff.String(), "col") - - out.reset() - err = integerColumn.serialize(select_statement, &out) - assert.NilError(t, err) - assert.Equal(t, out.buff.String(), "col") - - out.reset() - integerColumn.setTableName("table1") - err = integerColumn.DefaultAlias().serializeForProjection(select_statement, &out) - assert.NilError(t, err) - assert.Equal(t, out.buff.String(), `table1.col AS "table1.col"`) - - out.reset() - integerColumn.setTableName("table1") - aliasedBoolColumn := integerColumn.AS("alias1") - err = aliasedBoolColumn.serializeForProjection(select_statement, &out) - assert.NilError(t, err) - assert.Equal(t, out.buff.String(), `table1.col AS "alias1"`) } -func TestNewNumericColumnColumn(t *testing.T) { - numericColumn := NewFloatColumn("col", false) +func TestNewFloatColumnColumn(t *testing.T) { + floatColumn := FloatColumn("colFloat").From(subQuery) + assertClauseSerialize(t, floatColumn, "sub_query.colFloat") + assertClauseSerialize(t, floatColumn.EQ(Float(1.11)), "(sub_query.colFloat = $1)", float64(1.11)) + assertProjectionSerialize(t, floatColumn.defaultAliasProjection(), `sub_query.colFloat AS "sub_query.colFloat"`) - out := queryData{} - err := numericColumn.serialize(select_statement, &out) + floatColumn2 := table1ColFloat.From(subQuery) + assertClauseSerialize(t, floatColumn2, `sub_query."table1.colFloat"`) + assertClauseSerialize(t, floatColumn2.EQ(Float(2.22)), `(sub_query."table1.colFloat" = $1)`, float64(2.22)) + assertProjectionSerialize(t, floatColumn2.defaultAliasProjection(), `sub_query."table1.colFloat" AS "sub_query.table1.colFloat"`) - assert.NilError(t, err) - assert.Equal(t, out.buff.String(), "col") - - out.reset() - err = numericColumn.serialize(select_statement, &out) - assert.NilError(t, err) - assert.Equal(t, out.buff.String(), "col") - - out.reset() - numericColumn.setTableName("table1") - err = numericColumn.DefaultAlias().serializeForProjection(select_statement, &out) - assert.NilError(t, err) - assert.Equal(t, out.buff.String(), `table1.col AS "table1.col"`) - - out.reset() - numericColumn.setTableName("table1") - aliasedBoolColumn := numericColumn.AS("alias1") - err = aliasedBoolColumn.serializeForProjection(select_statement, &out) - assert.NilError(t, err) - assert.Equal(t, out.buff.String(), `table1.col AS "alias1"`) } diff --git a/sqlbuilder/expression_table.go b/sqlbuilder/expression_table.go index 0e190e1..c5af486 100644 --- a/sqlbuilder/expression_table.go +++ b/sqlbuilder/expression_table.go @@ -2,12 +2,10 @@ package sqlbuilder import "errors" -type expressionTable interface { +type ExpressionTable interface { ReadableTable - RefIntColumnName(name string) *IntegerColumn - RefIntColumn(column Column) *IntegerColumn - RefStringColumn(column Column) *StringColumn + Alias() string } type expressionTableImpl struct { @@ -16,7 +14,7 @@ type expressionTableImpl struct { alias string } -func newExpressionTable(expression Expression, alias string) expressionTable { +func newExpressionTable(expression Expression, alias string) ExpressionTable { expTable := &expressionTableImpl{expression: expression, alias: alias} expTable.readableTableInterfaceImpl.parent = expTable @@ -24,40 +22,15 @@ func newExpressionTable(expression Expression, alias string) expressionTable { return expTable } -// Returns the tableName's name in the database -func (e *expressionTableImpl) SchemaName() string { - return "" -} - -func (e *expressionTableImpl) TableName() string { +func (e *expressionTableImpl) Alias() string { return e.alias } -func (e *expressionTableImpl) RefIntColumnName(name string) *IntegerColumn { - intColumn := NewIntegerColumn(name, false) - intColumn.setTableName(e.alias) - - return intColumn -} - -func (e *expressionTableImpl) RefIntColumn(column Column) *IntegerColumn { - intColumn := NewIntegerColumn(column.TableName()+"."+column.Name(), false) - intColumn.setTableName(e.alias) - - return intColumn -} - -func (e *expressionTableImpl) RefStringColumn(column Column) *StringColumn { - strColumn := NewStringColumn(column.TableName()+"."+column.Name(), false) - strColumn.setTableName(e.alias) - return strColumn -} - func (e *expressionTableImpl) serialize(statement statementType, out *queryData, options ...serializeOption) error { if e == nil { return errors.New("Expression table is nil. ") } - //out.writeString("( ") + err := e.expression.serialize(statement, out) if err != nil { diff --git a/sqlbuilder/projection.go b/sqlbuilder/projection.go index 6e5e189..d111c9f 100644 --- a/sqlbuilder/projection.go +++ b/sqlbuilder/projection.go @@ -29,7 +29,7 @@ func (cl ColumnList) DefaultAlias() []projection { newColumnList := []projection{} for _, column := range cl { - newColumn := column.DefaultAlias() + newColumn := column.defaultAliasProjection() newColumnList = append(newColumnList, newColumn) } diff --git a/sqlbuilder/select_statement.go b/sqlbuilder/select_statement.go index 54ee06f..d236bf8 100644 --- a/sqlbuilder/select_statement.go +++ b/sqlbuilder/select_statement.go @@ -23,7 +23,7 @@ type SelectStatement interface { FOR_UPDATE() SelectStatement - AsTable(alias string) expressionTable + AsTable(alias string) ExpressionTable } func SELECT(projection ...projection) SelectStatement { @@ -54,7 +54,7 @@ func defaultProjectionAliasing(projections []projection) []projection { for _, projection := range projections { if column, ok := projection.(Column); ok { - aliasedProjections = append(aliasedProjections, column.DefaultAlias()) + aliasedProjections = append(aliasedProjections, column.defaultAliasProjection()) } else if columnList, ok := projection.(ColumnList); ok { aliasedProjections = append(aliasedProjections, columnList.DefaultAlias()...) } else { @@ -204,7 +204,7 @@ func (s *selectStatementImpl) DebugSql() (query string, err error) { return DebugSql(s) } -func (s *selectStatementImpl) AsTable(alias string) expressionTable { +func (s *selectStatementImpl) AsTable(alias string) ExpressionTable { return newExpressionTable(s.parent, alias) } diff --git a/sqlbuilder/set_statement.go b/sqlbuilder/set_statement.go index 5f69177..07d8456 100644 --- a/sqlbuilder/set_statement.go +++ b/sqlbuilder/set_statement.go @@ -15,7 +15,7 @@ type SetStatement interface { LIMIT(limit int64) SetStatement OFFSET(offset int64) SetStatement - AsTable(alias string) expressionTable + AsTable(alias string) ExpressionTable } const ( @@ -91,7 +91,7 @@ func (us *setStatementImpl) OFFSET(offset int64) SetStatement { return us } -func (us *setStatementImpl) AsTable(alias string) expressionTable { +func (us *setStatementImpl) AsTable(alias string) ExpressionTable { return newExpressionTable(us.parent, alias) } diff --git a/sqlbuilder/test_utils.go b/sqlbuilder/test_utils.go index 75ed415..5de7458 100644 --- a/sqlbuilder/test_utils.go +++ b/sqlbuilder/test_utils.go @@ -6,12 +6,12 @@ import ( "testing" ) -var table1Col1 = NewIntegerColumn("col1", true) -var table1ColInt = NewIntegerColumn("colInt", true) -var table1ColFloat = NewFloatColumn("colFloat", true) -var table1Col3 = NewIntegerColumn("col3", true) -var table1ColTime = NewTimeColumn("colTime", true) -var table1ColBool = NewBoolColumn("colBool", true) +var table1Col1 = IntegerColumn("col1") +var table1ColInt = IntegerColumn("colInt") +var table1ColFloat = FloatColumn("colFloat") +var table1Col3 = IntegerColumn("col3") +var table1ColTime = TimeColumn("colTime") +var table1ColBool = BoolColumn("colBool") var table1 = NewTable( "db", @@ -23,13 +23,13 @@ var table1 = NewTable( table1ColTime, table1ColBool) -var table2Col3 = NewIntegerColumn("col3", true) -var table2Col4 = NewIntegerColumn("col4", true) -var table2ColInt = NewIntegerColumn("colInt", true) -var table2ColFloat = NewFloatColumn("colFloat", true) -var table2ColStr = NewStringColumn("colStr", true) -var table2ColBool = NewBoolColumn("colBool", true) -var table2ColTime = NewTimeColumn("colTime", true) +var table2Col3 = IntegerColumn("col3") +var table2Col4 = IntegerColumn("col4") +var table2ColInt = IntegerColumn("colInt") +var table2ColFloat = FloatColumn("colFloat") +var table2ColStr = StringColumn("colStr") +var table2ColBool = BoolColumn("colBool") +var table2ColTime = TimeColumn("colTime") var table2 = NewTable( "db", @@ -42,9 +42,9 @@ var table2 = NewTable( table2ColBool, table2ColTime) -var table3Col1 = NewIntegerColumn("col1", true) -var table3ColInt = NewIntegerColumn("colInt", true) -var table3StrCol = NewStringColumn("col2", true) +var table3Col1 = IntegerColumn("col1") +var table3ColInt = IntegerColumn("colInt") +var table3StrCol = StringColumn("col2") var table3 = NewTable( "db", "table3", diff --git a/tests/select_test.go b/tests/select_test.go index f6e1bc9..f5d4a9d 100644 --- a/tests/select_test.go +++ b/tests/select_test.go @@ -203,7 +203,7 @@ FROM dvds.film_actor INNER JOIN dvds.inventory ON (inventory.film_id = film.film_id) INNER JOIN dvds.rental ON (rental.inventory_id = inventory.inventory_id) ORDER BY film.film_id ASC -LIMIT 50; +LIMIT 500; ` for i := 0; i < 1; i++ { query := FilmActor. @@ -222,9 +222,9 @@ LIMIT 50; ). //WHERE(FilmActor.ActorID.GtEqL(1).AND(FilmActor.ActorID.LtEqL(2))). ORDER_BY(Film.FilmID.ASC()). - LIMIT(50) + LIMIT(500) - assertQuery(t, query, expectedSql, int64(50)) + assertQuery(t, query, expectedSql, int64(500)) var languageActorFilm []struct { model.Language @@ -247,7 +247,7 @@ LIMIT 50; assert.NilError(t, err) assert.Equal(t, len(languageActorFilm), 1) - assert.Equal(t, len(languageActorFilm[0].Films), 1) + assert.Equal(t, len(languageActorFilm[0].Films), 6) assert.Equal(t, len(languageActorFilm[0].Films[0].Actors), 10) } @@ -708,8 +708,7 @@ SELECT actor.actor_id AS "actor.actor_id", film_actor.actor_id AS "film_actor.actor_id", film_actor.film_id AS "film_actor.film_id", film_actor.last_update AS "film_actor.last_update", - films."film.title" AS "film.title", - films."film.rating" AS "film.rating" + rfilms."film.title" AS "film.title" FROM dvds.actor INNER JOIN dvds.film_actor ON (actor.actor_id = film_actor.film_id) INNER JOIN ( @@ -718,28 +717,28 @@ FROM dvds.actor film.rating AS "film.rating" FROM dvds.film WHERE film.rating = 'R' - ) AS films ON (film_actor.film_id = films."film.film_id"); + ) AS rfilms ON (film_actor.film_id = rfilms."film.film_id"); ` - rFilmsOnly := Film. + rRatingFilms := Film. SELECT( Film.FilmID, Film.Title, Film.Rating, ). WHERE(Film.Rating.EQ(enum.MpaaRating.R)). - AsTable("films") + AsTable("rfilms") - rFilmId := rFilmsOnly.RefIntColumn(Film.FilmID) + rFilmId := Film.FilmID.From(rRatingFilms) + rTitle := Film.Title.From(rRatingFilms) query := Actor. INNER_JOIN(FilmActor, Actor.ActorID.EQ(FilmActor.FilmID)). - INNER_JOIN(rFilmsOnly, FilmActor.FilmID.EQ(rFilmId)). + INNER_JOIN(rRatingFilms, FilmActor.FilmID.EQ(rFilmId)). SELECT( Actor.AllColumns, FilmActor.AllColumns, - rFilmsOnly.RefStringColumn(Film.Title).AS("film.title"), - rFilmsOnly.RefStringColumn(Film.Rating).AS("film.rating"), + rTitle.AS("film.title"), ) fmt.Println(query.Sql()) @@ -914,24 +913,24 @@ FROM dvds.customer ORDER BY customer_payment_sum.amount_sum ASC; ` - customersPaymentSubQuery := Payment. + customersPayments := Payment. SELECT( Payment.CustomerID, SUMf(Payment.Amount).AS("amount_sum"), ). - GROUP_BY(Payment.CustomerID) + GROUP_BY(Payment.CustomerID). + AsTable("customer_payment_sum") - customersPaymentTable := customersPaymentSubQuery.AsTable("customer_payment_sum") - amountSumColumn := customersPaymentTable.RefIntColumnName("amount_sum") - customerId := customersPaymentTable.RefIntColumn(Payment.CustomerID) + customerId := Payment.CustomerID.From(customersPayments) + amountSum := FloatColumn("amount_sum").From(customersPayments) query := Customer. - INNER_JOIN(customersPaymentTable, Customer.CustomerID.EQ(customerId)). + INNER_JOIN(customersPayments, Customer.CustomerID.EQ(customerId)). SELECT( Customer.AllColumns, - amountSumColumn.AS("customer_with_amounts.amount_sum"), + amountSum.AS("customer_with_amounts.amount_sum"), ). - ORDER_BY(amountSumColumn.ASC()) + ORDER_BY(amountSum.ASC()) assertQuery(t, query, expectedSql) @@ -1039,7 +1038,7 @@ OFFSET 20; SELECT(Payment.PaymentID, Payment.Amount). WHERE(Payment.Amount.GT_EQ(Float(200))), ). - ORDER_BY(RefColumn("payment.payment_id").ASC(), Payment.Amount.DESC()). + ORDER_BY(IntegerColumn("payment.payment_id").ASC(), Payment.Amount.DESC()). LIMIT(10). OFFSET(20)