Column reference from sub queries.

This commit is contained in:
go-jet 2019-06-08 16:34:15 +02:00
parent e727fc3d4f
commit e772768180
13 changed files with 507 additions and 470 deletions

View file

@ -17,27 +17,27 @@ type ColumnInfo struct {
func (c ColumnInfo) SqlBuilderColumnType() string { func (c ColumnInfo) SqlBuilderColumnType() string {
switch c.DataType { switch c.DataType {
case "boolean": case "boolean":
return "BoolColumn" return "Bool"
case "smallint", "integer", "bigint": case "smallint", "integer", "bigint":
return "IntegerColumn" return "Integer"
case "date": case "date":
return "DateColumn" return "Date"
case "timestamp without time zone": case "timestamp without time zone":
return "TimestampColumn" return "Timestamp"
case "timestamp with time zone": case "timestamp with time zone":
return "TimestampzColumn" return "Timestampz"
case "time without time zone": case "time without time zone":
return "TimeColumn" return "Time"
case "time with time zone": case "time with time zone":
return "TimezColumn" return "Timez"
case "USER-DEFINED", "text", "character", "character varying", "bytea", "uuid", case "USER-DEFINED", "text", "character", "character varying", "bytea", "uuid",
"tsvector", "bit", "bit varying", "money", "json", "jsonb", "xml", "point", "interval", "line", "ARRAY": "tsvector", "bit", "bit varying", "money", "json", "jsonb", "xml", "point", "interval", "line", "ARRAY":
return "StringColumn" return "String"
case "real", "numeric", "decimal", "double precision": case "real", "numeric", "decimal", "double precision":
return "FloatColumn" return "Float"
default: default:
fmt.Println("Unknown sql type: " + c.DataType + ", using string column instead for sql builder.") fmt.Println("Unknown sql type: " + c.DataType + ", using string column instead for sql builder.")
return "StringColumn" return "String"
} }
} }

View file

@ -31,7 +31,7 @@ type {{.GoStructName}} struct {
//Columns //Columns
{{- range .Columns}} {{- range .Columns}}
{{camelize .Name}} *sqlbuilder.{{.SqlBuilderColumnType}} {{camelize .Name}} sqlbuilder.Column{{.SqlBuilderColumnType}}
{{- end}} {{- end}}
AllColumns sqlbuilder.ColumnList AllColumns sqlbuilder.ColumnList
@ -42,7 +42,7 @@ var {{camelize .Name}} = new{{.GoStructName}}()
func new{{.GoStructName}}() *{{.GoStructName}} { func new{{.GoStructName}}() *{{.GoStructName}} {
var ( var (
{{- range .Columns}} {{- range .Columns}}
{{camelize .Name}}Column = sqlbuilder.New{{.SqlBuilderColumnType}}("{{.Name}}", {{.IsNullable}}) {{camelize .Name}}Column = sqlbuilder.{{.SqlBuilderColumnType}}Column("{{.Name}}")
{{- end}} {{- end}}
) )

View file

@ -6,31 +6,33 @@ import (
"strings" "strings"
) )
type Column interface { type column interface {
Expression
Name() string Name() string
TableName() string TableName() string
IsNullable() bool
DefaultAlias() projection
// Internal function for tracking tableName that a column belongs to // Internal function for tracking tableName that a column belongs to
// for the purpose of serialization // for the purpose of serialization
setTableName(table string) setTableName(table string)
defaultAlias() string
defaultAliasProjection() projection
}
type Column interface {
Expression
column
} }
// The base type for real materialized columns. // The base type for real materialized columns.
type baseColumn struct { type columnImpl struct {
expressionInterfaceImpl expressionInterfaceImpl
name string name string
isNullable bool
tableName string tableName string
} }
func newBaseColumn(name string, isNullable bool, tableName string, parent Column) baseColumn { func newColumn(name string, tableName string, parent Column) columnImpl {
bc := baseColumn{ bc := columnImpl{
name: name, name: name,
isNullable: isNullable,
tableName: tableName, tableName: tableName,
} }
@ -39,27 +41,31 @@ func newBaseColumn(name string, isNullable bool, tableName string, parent Column
return bc return bc
} }
func (c *baseColumn) Name() string { func (c *columnImpl) Name() string {
return c.name return c.name
} }
func (c *baseColumn) TableName() string { func (c *columnImpl) TableName() string {
return c.tableName return c.tableName
} }
func (c *baseColumn) setTableName(table string) { func (c *columnImpl) setTableName(table string) {
c.tableName = table c.tableName = table
} }
func (c *baseColumn) IsNullable() bool { func (c *columnImpl) defaultAlias() string {
return c.isNullable if c.tableName != "" {
return c.tableName + "." + c.name
}
return c.name
} }
func (c *baseColumn) DefaultAlias() projection { func (c *columnImpl) defaultAliasProjection() projection {
return c.AS(c.tableName + "." + c.name) 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 { if statement == set_statement {
// set Statement (UNION, EXCEPT ...) can reference only select projections in order by clause // set Statement (UNION, EXCEPT ...) can reference only select projections in order by clause
columnRef := "" columnRef := ""
@ -78,7 +84,7 @@ func (c *baseColumn) serializeAsOrderBy(statement statementType, out *queryData)
return c.serialize(statement, out) 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 := "" columnRef := ""

View file

@ -1,210 +1,14 @@
// +build disabled
package sqlbuilder package sqlbuilder
import ( import "testing"
"bytes"
"testing"
gc "gopkg.in/check.v1" func TestColumn(t *testing.T) {
) column := newColumn("col", "", nil)
column.expressionInterfaceImpl.parent = &column
func Test(t *testing.T) { assertClauseSerialize(t, column, "col")
gc.TestingT(t) column.setTableName("table1")
} assertClauseSerialize(t, column, "table1.col")
assertProjectionSerialize(t, column.defaultAliasProjection(), `table1.col AS "table1.col"`)
type ColumnSuite struct { assertProjectionSerialize(t, column.AS("alias1"), `table1.col AS "alias1"`)
}
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)
} }

View file

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

View file

@ -1,16 +1,29 @@
package sqlbuilder package sqlbuilder
//------------------------------------------------------// //------------------------------------------------------//
type BoolColumn struct { type ColumnBool interface {
boolInterfaceImpl BoolExpression
column
baseColumn From(table ExpressionTable) ColumnBool
} }
func NewBoolColumn(name string, isNullable bool) *BoolColumn { type boolColumnImpl struct {
boolInterfaceImpl
boolColumn := &BoolColumn{} columnImpl
boolColumn.baseColumn = newBaseColumn(name, isNullable, "", boolColumn) }
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 boolColumn.boolInterfaceImpl.parent = boolColumn
@ -18,164 +31,241 @@ func NewBoolColumn(name string, isNullable bool) *BoolColumn {
} }
//------------------------------------------------------// //------------------------------------------------------//
type FloatColumn struct { type ColumnFloat interface {
floatInterfaceImpl FloatExpression
baseColumn 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.floatInterfaceImpl.parent = floatColumn
floatColumn.columnImpl = newColumn(name, "", floatColumn)
floatColumn.baseColumn = newBaseColumn(name, isNullable, "", floatColumn)
return floatColumn return floatColumn
} }
//------------------------------------------------------// //------------------------------------------------------//
type IntegerColumn struct { type ColumnInteger interface {
integerInterfaceImpl IntegerExpression
column
baseColumn From(table ExpressionTable) ColumnInteger
} }
// Representation of any integer column type integerColumnImpl struct {
// This function will panic if name is not valid integerInterfaceImpl
func NewIntegerColumn(name string, isNullable bool) *IntegerColumn {
integerColumn := &IntegerColumn{} 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.integerInterfaceImpl.parent = integerColumn
integerColumn.columnImpl = newColumn(name, "", integerColumn)
integerColumn.baseColumn = newBaseColumn(name, isNullable, "", integerColumn)
return integerColumn return integerColumn
} }
//------------------------------------------------------// //------------------------------------------------------//
type StringColumn struct { type ColumnString interface {
stringInterfaceImpl StringExpression
column
baseColumn From(table ExpressionTable) ColumnString
} }
// Representation of any integer column type stringColumnImpl struct {
// This function will panic if name is not valid stringInterfaceImpl
func NewStringColumn(name string, isNullable bool) *StringColumn {
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.stringInterfaceImpl.parent = stringColumn
stringColumn.baseColumn = newBaseColumn(name, isNullable, "", stringColumn) stringColumn.columnImpl = newColumn(name, "", stringColumn)
return stringColumn return stringColumn
} }
//------------------------------------------------------// //------------------------------------------------------//
type TimeColumn struct { type ColumnTime interface {
timeInterfaceImpl TimeExpression
column
baseColumn From(table ExpressionTable) ColumnTime
} }
// Representation of any integer column type timeColumnImpl struct {
// This function will panic if name is not valid timeInterfaceImpl
func NewTimeColumn(name string, isNullable bool) *TimeColumn {
timeColumn := &TimeColumn{} 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.timeInterfaceImpl.parent = timeColumn
timeColumn.baseColumn = newBaseColumn(name, isNullable, "", timeColumn) timeColumn.columnImpl = newColumn(name, "", timeColumn)
return timeColumn return timeColumn
} }
//------------------------------------------------------// //------------------------------------------------------//
type TimezColumn struct {
timezInterfaceImpl
baseColumn type ColumnTimez interface {
TimezExpression
column
From(table ExpressionTable) ColumnTimez
} }
// Representation of any integer column type timezColumnImpl struct {
// This function will panic if name is not valid timezInterfaceImpl
func NewTimezColumn(name string, isNullable bool) *TimezColumn {
timezColumn := &TimezColumn{} 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.timezInterfaceImpl.parent = timezColumn
timezColumn.baseColumn = newBaseColumn(name, isNullable, "", timezColumn) timezColumn.columnImpl = newColumn(name, "", timezColumn)
return timezColumn return timezColumn
} }
//------------------------------------------------------// //------------------------------------------------------//
type TimestampColumn struct { type ColumnTimestamp interface {
timestampInterfaceImpl TimestampExpression
column
baseColumn From(table ExpressionTable) ColumnTimestamp
} }
// Representation of any integer column type timestampColumnImpl struct {
// This function will panic if name is not valid timestampInterfaceImpl
func NewTimestampColumn(name string, isNullable bool) *TimestampColumn {
timestampColumn := &TimestampColumn{} columnImpl
}
func (i *timestampColumnImpl) From(table ExpressionTable) ColumnTimestamp {
newTimestampColumn := TimestampColumn(i.defaultAlias())
newTimestampColumn.setTableName(table.Alias())
return newTimestampColumn
}
func TimestampColumn(name string) ColumnTimestamp {
timestampColumn := &timestampColumnImpl{}
timestampColumn.timestampInterfaceImpl.parent = timestampColumn timestampColumn.timestampInterfaceImpl.parent = timestampColumn
timestampColumn.baseColumn = newBaseColumn(name, isNullable, "", timestampColumn) timestampColumn.columnImpl = newColumn(name, "", timestampColumn)
return timestampColumn return timestampColumn
} }
//------------------------------------------------------// //------------------------------------------------------//
type TimestampzColumn struct { type ColumnTimestampz interface {
timestampzInterfaceImpl TimestampzExpression
column
baseColumn From(table ExpressionTable) ColumnTimestampz
} }
// Representation of any integer column type timestampzColumnImpl struct {
// This function will panic if name is not valid timestampzInterfaceImpl
func NewTimestampzColumn(name string, isNullable bool) *TimestampzColumn {
timestampzColumn := &TimestampzColumn{} columnImpl
}
func (i *timestampzColumnImpl) From(table ExpressionTable) ColumnTimestampz {
newTimestampzColumn := TimestampzColumn(i.defaultAlias())
newTimestampzColumn.setTableName(table.Alias())
return newTimestampzColumn
}
func TimestampzColumn(name string) ColumnTimestampz {
timestampzColumn := &timestampzColumnImpl{}
timestampzColumn.timestampzInterfaceImpl.parent = timestampzColumn timestampzColumn.timestampzInterfaceImpl.parent = timestampzColumn
timestampzColumn.baseColumn = newBaseColumn(name, isNullable, "", timestampzColumn) timestampzColumn.columnImpl = newColumn(name, "", timestampzColumn)
return timestampzColumn return timestampzColumn
} }
//------------------------------------------------------// //------------------------------------------------------//
type DateColumn struct { type ColumnDate interface {
dateInterfaceImpl DateExpression
column
baseColumn From(table ExpressionTable) ColumnDate
} }
// Representation of any integer column type dateColumnImpl struct {
// This function will panic if name is not valid dateInterfaceImpl
func NewDateColumn(name string, isNullable bool) *DateColumn {
dateColumn := &DateColumn{} 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.dateInterfaceImpl.parent = dateColumn
dateColumn.baseColumn = newBaseColumn(name, isNullable, "", dateColumn) dateColumn.columnImpl = newColumn(name, "", dateColumn)
return dateColumn return dateColumn
} }
// ------------------------------------------------------//
type refColumn struct {
baseColumn
}
func RefColumn(name string) *refColumn {
refColumn := &refColumn{}
refColumn.baseColumn = newBaseColumn(name, false, "", refColumn)
return refColumn
}

View file

@ -1,90 +1,45 @@
package sqlbuilder package sqlbuilder
import ( import (
"gotest.tools/assert"
"testing" "testing"
) )
var subQuery = table1.SELECT(table1ColFloat, table1ColInt).AsTable("sub_query")
func TestNewBoolColumn(t *testing.T) { 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{} boolColumn2 := table1ColBool.From(subQuery)
err := boolColumn.serialize(select_statement, &out) assertClauseSerialize(t, boolColumn2, `sub_query."table1.colBool"`)
assertClauseSerialize(t, boolColumn2.EQ(Bool(true)), `(sub_query."table1.colBool" = $1)`, true)
assert.NilError(t, err) assertProjectionSerialize(t, boolColumn2.defaultAliasProjection(), `sub_query."table1.colBool" AS "sub_query.table1.colBool"`)
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"`)
} }
func TestNewIntColumn(t *testing.T) { 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{} intColumn2 := table1ColInt.From(subQuery)
err := integerColumn.serialize(select_statement, &out) 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) { func TestNewFloatColumnColumn(t *testing.T) {
numericColumn := NewFloatColumn("col", false) 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{} floatColumn2 := table1ColFloat.From(subQuery)
err := numericColumn.serialize(select_statement, &out) 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"`)
} }

View file

@ -2,12 +2,10 @@ package sqlbuilder
import "errors" import "errors"
type expressionTable interface { type ExpressionTable interface {
ReadableTable ReadableTable
RefIntColumnName(name string) *IntegerColumn Alias() string
RefIntColumn(column Column) *IntegerColumn
RefStringColumn(column Column) *StringColumn
} }
type expressionTableImpl struct { type expressionTableImpl struct {
@ -16,7 +14,7 @@ type expressionTableImpl struct {
alias string alias string
} }
func newExpressionTable(expression Expression, alias string) expressionTable { func newExpressionTable(expression Expression, alias string) ExpressionTable {
expTable := &expressionTableImpl{expression: expression, alias: alias} expTable := &expressionTableImpl{expression: expression, alias: alias}
expTable.readableTableInterfaceImpl.parent = expTable expTable.readableTableInterfaceImpl.parent = expTable
@ -24,40 +22,15 @@ func newExpressionTable(expression Expression, alias string) expressionTable {
return expTable return expTable
} }
// Returns the tableName's name in the database func (e *expressionTableImpl) Alias() string {
func (e *expressionTableImpl) SchemaName() string {
return ""
}
func (e *expressionTableImpl) TableName() string {
return e.alias 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 { func (e *expressionTableImpl) serialize(statement statementType, out *queryData, options ...serializeOption) error {
if e == nil { if e == nil {
return errors.New("Expression table is nil. ") return errors.New("Expression table is nil. ")
} }
//out.writeString("( ")
err := e.expression.serialize(statement, out) err := e.expression.serialize(statement, out)
if err != nil { if err != nil {

View file

@ -29,7 +29,7 @@ func (cl ColumnList) DefaultAlias() []projection {
newColumnList := []projection{} newColumnList := []projection{}
for _, column := range cl { for _, column := range cl {
newColumn := column.DefaultAlias() newColumn := column.defaultAliasProjection()
newColumnList = append(newColumnList, newColumn) newColumnList = append(newColumnList, newColumn)
} }

View file

@ -23,7 +23,7 @@ type SelectStatement interface {
FOR_UPDATE() SelectStatement FOR_UPDATE() SelectStatement
AsTable(alias string) expressionTable AsTable(alias string) ExpressionTable
} }
func SELECT(projection ...projection) SelectStatement { func SELECT(projection ...projection) SelectStatement {
@ -54,7 +54,7 @@ func defaultProjectionAliasing(projections []projection) []projection {
for _, projection := range projections { for _, projection := range projections {
if column, ok := projection.(Column); ok { if column, ok := projection.(Column); ok {
aliasedProjections = append(aliasedProjections, column.DefaultAlias()) aliasedProjections = append(aliasedProjections, column.defaultAliasProjection())
} else if columnList, ok := projection.(ColumnList); ok { } else if columnList, ok := projection.(ColumnList); ok {
aliasedProjections = append(aliasedProjections, columnList.DefaultAlias()...) aliasedProjections = append(aliasedProjections, columnList.DefaultAlias()...)
} else { } else {
@ -204,7 +204,7 @@ func (s *selectStatementImpl) DebugSql() (query string, err error) {
return DebugSql(s) return DebugSql(s)
} }
func (s *selectStatementImpl) AsTable(alias string) expressionTable { func (s *selectStatementImpl) AsTable(alias string) ExpressionTable {
return newExpressionTable(s.parent, alias) return newExpressionTable(s.parent, alias)
} }

View file

@ -15,7 +15,7 @@ type SetStatement interface {
LIMIT(limit int64) SetStatement LIMIT(limit int64) SetStatement
OFFSET(offset int64) SetStatement OFFSET(offset int64) SetStatement
AsTable(alias string) expressionTable AsTable(alias string) ExpressionTable
} }
const ( const (
@ -91,7 +91,7 @@ func (us *setStatementImpl) OFFSET(offset int64) SetStatement {
return us return us
} }
func (us *setStatementImpl) AsTable(alias string) expressionTable { func (us *setStatementImpl) AsTable(alias string) ExpressionTable {
return newExpressionTable(us.parent, alias) return newExpressionTable(us.parent, alias)
} }

View file

@ -6,12 +6,12 @@ import (
"testing" "testing"
) )
var table1Col1 = NewIntegerColumn("col1", true) var table1Col1 = IntegerColumn("col1")
var table1ColInt = NewIntegerColumn("colInt", true) var table1ColInt = IntegerColumn("colInt")
var table1ColFloat = NewFloatColumn("colFloat", true) var table1ColFloat = FloatColumn("colFloat")
var table1Col3 = NewIntegerColumn("col3", true) var table1Col3 = IntegerColumn("col3")
var table1ColTime = NewTimeColumn("colTime", true) var table1ColTime = TimeColumn("colTime")
var table1ColBool = NewBoolColumn("colBool", true) var table1ColBool = BoolColumn("colBool")
var table1 = NewTable( var table1 = NewTable(
"db", "db",
@ -23,13 +23,13 @@ var table1 = NewTable(
table1ColTime, table1ColTime,
table1ColBool) table1ColBool)
var table2Col3 = NewIntegerColumn("col3", true) var table2Col3 = IntegerColumn("col3")
var table2Col4 = NewIntegerColumn("col4", true) var table2Col4 = IntegerColumn("col4")
var table2ColInt = NewIntegerColumn("colInt", true) var table2ColInt = IntegerColumn("colInt")
var table2ColFloat = NewFloatColumn("colFloat", true) var table2ColFloat = FloatColumn("colFloat")
var table2ColStr = NewStringColumn("colStr", true) var table2ColStr = StringColumn("colStr")
var table2ColBool = NewBoolColumn("colBool", true) var table2ColBool = BoolColumn("colBool")
var table2ColTime = NewTimeColumn("colTime", true) var table2ColTime = TimeColumn("colTime")
var table2 = NewTable( var table2 = NewTable(
"db", "db",
@ -42,9 +42,9 @@ var table2 = NewTable(
table2ColBool, table2ColBool,
table2ColTime) table2ColTime)
var table3Col1 = NewIntegerColumn("col1", true) var table3Col1 = IntegerColumn("col1")
var table3ColInt = NewIntegerColumn("colInt", true) var table3ColInt = IntegerColumn("colInt")
var table3StrCol = NewStringColumn("col2", true) var table3StrCol = StringColumn("col2")
var table3 = NewTable( var table3 = NewTable(
"db", "db",
"table3", "table3",

View file

@ -203,7 +203,7 @@ FROM dvds.film_actor
INNER JOIN dvds.inventory ON (inventory.film_id = film.film_id) INNER JOIN dvds.inventory ON (inventory.film_id = film.film_id)
INNER JOIN dvds.rental ON (rental.inventory_id = inventory.inventory_id) INNER JOIN dvds.rental ON (rental.inventory_id = inventory.inventory_id)
ORDER BY film.film_id ASC ORDER BY film.film_id ASC
LIMIT 50; LIMIT 500;
` `
for i := 0; i < 1; i++ { for i := 0; i < 1; i++ {
query := FilmActor. query := FilmActor.
@ -222,9 +222,9 @@ LIMIT 50;
). ).
//WHERE(FilmActor.ActorID.GtEqL(1).AND(FilmActor.ActorID.LtEqL(2))). //WHERE(FilmActor.ActorID.GtEqL(1).AND(FilmActor.ActorID.LtEqL(2))).
ORDER_BY(Film.FilmID.ASC()). ORDER_BY(Film.FilmID.ASC()).
LIMIT(50) LIMIT(500)
assertQuery(t, query, expectedSql, int64(50)) assertQuery(t, query, expectedSql, int64(500))
var languageActorFilm []struct { var languageActorFilm []struct {
model.Language model.Language
@ -247,7 +247,7 @@ LIMIT 50;
assert.NilError(t, err) assert.NilError(t, err)
assert.Equal(t, len(languageActorFilm), 1) 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) 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.actor_id AS "film_actor.actor_id",
film_actor.film_id AS "film_actor.film_id", film_actor.film_id AS "film_actor.film_id",
film_actor.last_update AS "film_actor.last_update", film_actor.last_update AS "film_actor.last_update",
films."film.title" AS "film.title", rfilms."film.title" AS "film.title"
films."film.rating" AS "film.rating"
FROM dvds.actor FROM dvds.actor
INNER JOIN dvds.film_actor ON (actor.actor_id = film_actor.film_id) INNER JOIN dvds.film_actor ON (actor.actor_id = film_actor.film_id)
INNER JOIN ( INNER JOIN (
@ -718,28 +717,28 @@ FROM dvds.actor
film.rating AS "film.rating" film.rating AS "film.rating"
FROM dvds.film FROM dvds.film
WHERE film.rating = 'R' 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( SELECT(
Film.FilmID, Film.FilmID,
Film.Title, Film.Title,
Film.Rating, Film.Rating,
). ).
WHERE(Film.Rating.EQ(enum.MpaaRating.R)). 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. query := Actor.
INNER_JOIN(FilmActor, Actor.ActorID.EQ(FilmActor.FilmID)). INNER_JOIN(FilmActor, Actor.ActorID.EQ(FilmActor.FilmID)).
INNER_JOIN(rFilmsOnly, FilmActor.FilmID.EQ(rFilmId)). INNER_JOIN(rRatingFilms, FilmActor.FilmID.EQ(rFilmId)).
SELECT( SELECT(
Actor.AllColumns, Actor.AllColumns,
FilmActor.AllColumns, FilmActor.AllColumns,
rFilmsOnly.RefStringColumn(Film.Title).AS("film.title"), rTitle.AS("film.title"),
rFilmsOnly.RefStringColumn(Film.Rating).AS("film.rating"),
) )
fmt.Println(query.Sql()) fmt.Println(query.Sql())
@ -914,24 +913,24 @@ FROM dvds.customer
ORDER BY customer_payment_sum.amount_sum ASC; ORDER BY customer_payment_sum.amount_sum ASC;
` `
customersPaymentSubQuery := Payment. customersPayments := Payment.
SELECT( SELECT(
Payment.CustomerID, Payment.CustomerID,
SUMf(Payment.Amount).AS("amount_sum"), SUMf(Payment.Amount).AS("amount_sum"),
). ).
GROUP_BY(Payment.CustomerID) GROUP_BY(Payment.CustomerID).
AsTable("customer_payment_sum")
customersPaymentTable := customersPaymentSubQuery.AsTable("customer_payment_sum") customerId := Payment.CustomerID.From(customersPayments)
amountSumColumn := customersPaymentTable.RefIntColumnName("amount_sum") amountSum := FloatColumn("amount_sum").From(customersPayments)
customerId := customersPaymentTable.RefIntColumn(Payment.CustomerID)
query := Customer. query := Customer.
INNER_JOIN(customersPaymentTable, Customer.CustomerID.EQ(customerId)). INNER_JOIN(customersPayments, Customer.CustomerID.EQ(customerId)).
SELECT( SELECT(
Customer.AllColumns, 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) assertQuery(t, query, expectedSql)
@ -1039,7 +1038,7 @@ OFFSET 20;
SELECT(Payment.PaymentID, Payment.Amount). SELECT(Payment.PaymentID, Payment.Amount).
WHERE(Payment.Amount.GT_EQ(Float(200))), 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). LIMIT(10).
OFFSET(20) OFFSET(20)