From 3013dc36479b05eebf05afd85bc8be98e8f354f5 Mon Sep 17 00:00:00 2001 From: go-jet Date: Sun, 9 Feb 2020 18:37:48 +0100 Subject: [PATCH] Add support for PostgreSQL interval column --- .../internal/metadata/column_meta_data.go | 4 +- internal/jet/alias.go | 3 +- internal/jet/bool_expression.go | 10 +- internal/jet/column.go | 51 ++- internal/jet/column_test.go | 2 +- internal/jet/column_types.go | 103 ++---- internal/jet/column_types_test.go | 71 +++- internal/jet/date_expression.go | 20 +- internal/jet/expression.go | 3 +- internal/jet/float_expression.go | 49 ++- internal/jet/integer_expression.go | 58 +-- internal/jet/interval.go | 17 +- internal/jet/literal_expression.go | 4 +- internal/jet/operators.go | 55 ++- internal/jet/string_expression.go | 18 +- internal/jet/time_expression.go | 20 +- internal/jet/timestamp_expression.go | 20 +- internal/jet/timestampz_expression.go | 20 +- internal/jet/timez_expression.go | 20 +- internal/jet/utils.go | 20 + internal/jet/utils_test.go | 19 + postgres/columns.go | 35 +- postgres/columns_test.go | 20 + postgres/expressions.go | 3 + .../{interval.go => interval_expression.go} | 101 +++++- ...al_test.go => interval_expression_test.go} | 24 +- postgres/utils_test.go | 4 + tests/postgres/alltypes_test.go | 161 +++++++- tests/postgres/generator_test.go | 343 +++++++++++++++++- tests/postgres/main_test.go | 15 + 30 files changed, 1038 insertions(+), 255 deletions(-) create mode 100644 internal/jet/utils_test.go create mode 100644 postgres/columns_test.go rename postgres/{interval.go => interval_expression.go} (52%) rename postgres/{interval_test.go => interval_expression_test.go} (61%) diff --git a/generator/internal/metadata/column_meta_data.go b/generator/internal/metadata/column_meta_data.go index 69a16f7..56ae54c 100644 --- a/generator/internal/metadata/column_meta_data.go +++ b/generator/internal/metadata/column_meta_data.go @@ -57,8 +57,10 @@ func (c ColumnMetaData) getSqlBuilderColumnType() string { return "Time" case "time with time zone": return "Timez" + case "interval": + return "Interval" case "USER-DEFINED", "enum", "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", "line", "ARRAY", "char", "varchar", "binary", "varbinary", "tinyblob", "blob", "mediumblob", "longblob", "tinytext", "mediumtext", "longtext": // MySQL return "String" diff --git a/internal/jet/alias.go b/internal/jet/alias.go index a2b1ae3..57f55cd 100644 --- a/internal/jet/alias.go +++ b/internal/jet/alias.go @@ -13,8 +13,7 @@ func newAlias(expression Expression, aliasName string) Projection { } func (a *alias) fromImpl(subQuery SelectTable) Projection { - column := newColumn(a.alias, "", nil) - column.Parent = &column + column := NewColumnImpl(a.alias, "", nil) column.subQuery = subQuery return &column diff --git a/internal/jet/bool_expression.go b/internal/jet/bool_expression.go index 1a05ab6..b4015b2 100644 --- a/internal/jet/bool_expression.go +++ b/internal/jet/bool_expression.go @@ -37,19 +37,19 @@ type boolInterfaceImpl struct { } func (b *boolInterfaceImpl) EQ(expression BoolExpression) BoolExpression { - return eq(b.parent, expression) + return Eq(b.parent, expression) } func (b *boolInterfaceImpl) NOT_EQ(expression BoolExpression) BoolExpression { - return notEq(b.parent, expression) + return NotEq(b.parent, expression) } func (b *boolInterfaceImpl) IS_DISTINCT_FROM(rhs BoolExpression) BoolExpression { - return isDistinctFrom(b.parent, rhs) + return IsDistinctFrom(b.parent, rhs) } func (b *boolInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs BoolExpression) BoolExpression { - return isNotDistinctFrom(b.parent, rhs) + return IsNotDistinctFrom(b.parent, rhs) } func (b *boolInterfaceImpl) AND(expression BoolExpression) BoolExpression { @@ -86,7 +86,7 @@ func (b *boolInterfaceImpl) IS_NOT_UNKNOWN() BoolExpression { //---------------------------------------------------// func newBinaryBoolOperatorExpression(lhs, rhs Expression, operator string, additionalParams ...Expression) BoolExpression { - return BoolExp(newBinaryOperatorExpression(lhs, rhs, operator, additionalParams...)) + return BoolExp(NewBinaryOperatorExpression(lhs, rhs, operator, additionalParams...)) } //---------------------------------------------------// diff --git a/internal/jet/column.go b/internal/jet/column.go index c7a5f41..85c053e 100644 --- a/internal/jet/column.go +++ b/internal/jet/column.go @@ -18,8 +18,8 @@ type ColumnExpression interface { Expression } -// The base type for real materialized columns. -type columnImpl struct { +// ColumnExpressionImpl is base type for sql columns. +type ColumnExpressionImpl struct { ExpressionInterfaceImpl name string @@ -28,34 +28,41 @@ type columnImpl struct { subQuery SelectTable } -func newColumn(name string, tableName string, parent ColumnExpression) columnImpl { - bc := columnImpl{ +// NewColumnImpl creates new ColumnExpressionImpl +func NewColumnImpl(name string, tableName string, parent ColumnExpression) ColumnExpressionImpl { + bc := ColumnExpressionImpl{ name: name, tableName: tableName, } - bc.ExpressionInterfaceImpl.Parent = parent + if parent != nil { + bc.ExpressionInterfaceImpl.Parent = parent + } else { + bc.ExpressionInterfaceImpl.Parent = &bc + } return bc } -func (c *columnImpl) Name() string { +// Name returns name of the column +func (c *ColumnExpressionImpl) Name() string { return c.name } -func (c *columnImpl) TableName() string { +// TableName returns column table name +func (c *ColumnExpressionImpl) TableName() string { return c.tableName } -func (c *columnImpl) setTableName(table string) { +func (c *ColumnExpressionImpl) setTableName(table string) { c.tableName = table } -func (c *columnImpl) setSubQuery(subQuery SelectTable) { +func (c *ColumnExpressionImpl) setSubQuery(subQuery SelectTable) { c.subQuery = subQuery } -func (c *columnImpl) defaultAlias() string { +func (c *ColumnExpressionImpl) defaultAlias() string { if c.tableName != "" { return c.tableName + "." + c.name } @@ -63,25 +70,31 @@ func (c *columnImpl) defaultAlias() string { return c.name } -func (c *columnImpl) serializeForOrderBy(statement StatementType, out *SQLBuilder) { +func (c *ColumnExpressionImpl) fromImpl(subQuery SelectTable) Projection { + newColumn := NewColumnImpl(c.name, c.tableName, nil) + newColumn.setSubQuery(subQuery) + + return &newColumn +} + +func (c *ColumnExpressionImpl) serializeForOrderBy(statement StatementType, out *SQLBuilder) { if statement == SetStatementType { // set Statement (UNION, EXCEPT ...) can reference only select projections in order by clause out.WriteAlias(c.defaultAlias()) //always quote - return } c.serialize(statement, out) } -func (c columnImpl) serializeForProjection(statement StatementType, out *SQLBuilder) { +func (c ColumnExpressionImpl) serializeForProjection(statement StatementType, out *SQLBuilder) { c.serialize(statement, out) out.WriteString("AS") out.WriteAlias(c.defaultAlias()) } -func (c columnImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) { +func (c ColumnExpressionImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) { if c.subQuery != nil { out.WriteIdentifier(c.subQuery.Alias()) @@ -128,3 +141,13 @@ func (cl ColumnList) TableName() string { return "" } func (cl ColumnList) setTableName(name string) {} func (cl ColumnList) setSubQuery(subQuery SelectTable) {} func (cl ColumnList) defaultAlias() string { return "" } + +// SetTableName is utility function to set table name from outside of jet package to avoid making public setTableName +func SetTableName(columnExpression ColumnExpression, tableName string) { + columnExpression.setTableName(tableName) +} + +// SetSubQuery is utility function to set table name from outside of jet package to avoid making public setSubQuery +func SetSubQuery(columnExpression ColumnExpression, subQuery SelectTable) { + columnExpression.setSubQuery(subQuery) +} diff --git a/internal/jet/column_test.go b/internal/jet/column_test.go index ca3f5f6..7e6c0ed 100644 --- a/internal/jet/column_test.go +++ b/internal/jet/column_test.go @@ -3,7 +3,7 @@ package jet import "testing" func TestColumn(t *testing.T) { - column := newColumn("col", "", nil) + column := NewColumnImpl("col", "", nil) column.ExpressionInterfaceImpl.Parent = &column assertClauseSerialize(t, column, "col") diff --git a/internal/jet/column_types.go b/internal/jet/column_types.go index 5443911..58f6751 100644 --- a/internal/jet/column_types.go +++ b/internal/jet/column_types.go @@ -10,11 +10,10 @@ type ColumnBool interface { type boolColumnImpl struct { boolInterfaceImpl - - columnImpl + ColumnExpressionImpl } -func (i *boolColumnImpl) fromImpl(subQuery SelectTable) Projection { +func (i *boolColumnImpl) From(subQuery SelectTable) ColumnBool { newBoolColumn := BoolColumn(i.name) newBoolColumn.setTableName(i.tableName) newBoolColumn.setSubQuery(subQuery) @@ -22,16 +21,10 @@ func (i *boolColumnImpl) fromImpl(subQuery SelectTable) Projection { return newBoolColumn } -func (i *boolColumnImpl) From(subQuery SelectTable) ColumnBool { - newBoolColumn := i.fromImpl(subQuery).(ColumnBool) - - return newBoolColumn -} - // BoolColumn creates named bool column. func BoolColumn(name string) ColumnBool { boolColumn := &boolColumnImpl{} - boolColumn.columnImpl = newColumn(name, "", boolColumn) + boolColumn.ColumnExpressionImpl = NewColumnImpl(name, "", boolColumn) boolColumn.boolInterfaceImpl.parent = boolColumn return boolColumn @@ -49,19 +42,13 @@ type ColumnFloat interface { type floatColumnImpl struct { floatInterfaceImpl - columnImpl -} - -func (i *floatColumnImpl) fromImpl(subQuery SelectTable) Projection { - newFloatColumn := FloatColumn(i.name) - newFloatColumn.setTableName(i.tableName) - newFloatColumn.setSubQuery(subQuery) - - return newFloatColumn + ColumnExpressionImpl } func (i *floatColumnImpl) From(subQuery SelectTable) ColumnFloat { - newFloatColumn := i.fromImpl(subQuery).(ColumnFloat) + newFloatColumn := FloatColumn(i.name) + newFloatColumn.setTableName(i.tableName) + newFloatColumn.setSubQuery(subQuery) return newFloatColumn } @@ -70,7 +57,7 @@ func (i *floatColumnImpl) From(subQuery SelectTable) ColumnFloat { func FloatColumn(name string) ColumnFloat { floatColumn := &floatColumnImpl{} floatColumn.floatInterfaceImpl.parent = floatColumn - floatColumn.columnImpl = newColumn(name, "", floatColumn) + floatColumn.ColumnExpressionImpl = NewColumnImpl(name, "", floatColumn) return floatColumn } @@ -88,10 +75,10 @@ type ColumnInteger interface { type integerColumnImpl struct { integerInterfaceImpl - columnImpl + ColumnExpressionImpl } -func (i *integerColumnImpl) fromImpl(subQuery SelectTable) Projection { +func (i *integerColumnImpl) From(subQuery SelectTable) ColumnInteger { newIntColumn := IntegerColumn(i.name) newIntColumn.setTableName(i.tableName) newIntColumn.setSubQuery(subQuery) @@ -99,15 +86,11 @@ func (i *integerColumnImpl) fromImpl(subQuery SelectTable) Projection { return newIntColumn } -func (i *integerColumnImpl) From(subQuery SelectTable) ColumnInteger { - return i.fromImpl(subQuery).(ColumnInteger) -} - // IntegerColumn creates named integer column. func IntegerColumn(name string) ColumnInteger { integerColumn := &integerColumnImpl{} integerColumn.integerInterfaceImpl.parent = integerColumn - integerColumn.columnImpl = newColumn(name, "", integerColumn) + integerColumn.ColumnExpressionImpl = NewColumnImpl(name, "", integerColumn) return integerColumn } @@ -126,10 +109,10 @@ type ColumnString interface { type stringColumnImpl struct { stringInterfaceImpl - columnImpl + ColumnExpressionImpl } -func (i *stringColumnImpl) fromImpl(subQuery SelectTable) Projection { +func (i *stringColumnImpl) From(subQuery SelectTable) ColumnString { newStrColumn := StringColumn(i.name) newStrColumn.setTableName(i.tableName) newStrColumn.setSubQuery(subQuery) @@ -137,15 +120,11 @@ func (i *stringColumnImpl) fromImpl(subQuery SelectTable) Projection { return newStrColumn } -func (i *stringColumnImpl) From(subQuery SelectTable) ColumnString { - return i.fromImpl(subQuery).(ColumnString) -} - // StringColumn creates named string column. func StringColumn(name string) ColumnString { stringColumn := &stringColumnImpl{} stringColumn.stringInterfaceImpl.parent = stringColumn - stringColumn.columnImpl = newColumn(name, "", stringColumn) + stringColumn.ColumnExpressionImpl = NewColumnImpl(name, "", stringColumn) return stringColumn } @@ -162,10 +141,10 @@ type ColumnTime interface { type timeColumnImpl struct { timeInterfaceImpl - columnImpl + ColumnExpressionImpl } -func (i *timeColumnImpl) fromImpl(subQuery SelectTable) Projection { +func (i *timeColumnImpl) From(subQuery SelectTable) ColumnTime { newTimeColumn := TimeColumn(i.name) newTimeColumn.setTableName(i.tableName) newTimeColumn.setSubQuery(subQuery) @@ -173,15 +152,11 @@ func (i *timeColumnImpl) fromImpl(subQuery SelectTable) Projection { return newTimeColumn } -func (i *timeColumnImpl) From(subQuery SelectTable) ColumnTime { - return i.fromImpl(subQuery).(ColumnTime) -} - // TimeColumn creates named time column func TimeColumn(name string) ColumnTime { timeColumn := &timeColumnImpl{} timeColumn.timeInterfaceImpl.parent = timeColumn - timeColumn.columnImpl = newColumn(name, "", timeColumn) + timeColumn.ColumnExpressionImpl = NewColumnImpl(name, "", timeColumn) return timeColumn } @@ -197,11 +172,10 @@ type ColumnTimez interface { type timezColumnImpl struct { timezInterfaceImpl - - columnImpl + ColumnExpressionImpl } -func (i *timezColumnImpl) fromImpl(subQuery SelectTable) Projection { +func (i *timezColumnImpl) From(subQuery SelectTable) ColumnTimez { newTimezColumn := TimezColumn(i.name) newTimezColumn.setTableName(i.tableName) newTimezColumn.setSubQuery(subQuery) @@ -209,15 +183,11 @@ func (i *timezColumnImpl) fromImpl(subQuery SelectTable) Projection { return newTimezColumn } -func (i *timezColumnImpl) From(subQuery SelectTable) ColumnTimez { - return i.fromImpl(subQuery).(ColumnTimez) -} - // TimezColumn creates named time with time zone column. func TimezColumn(name string) ColumnTimez { timezColumn := &timezColumnImpl{} timezColumn.timezInterfaceImpl.parent = timezColumn - timezColumn.columnImpl = newColumn(name, "", timezColumn) + timezColumn.ColumnExpressionImpl = NewColumnImpl(name, "", timezColumn) return timezColumn } @@ -234,11 +204,10 @@ type ColumnTimestamp interface { type timestampColumnImpl struct { timestampInterfaceImpl - - columnImpl + ColumnExpressionImpl } -func (i *timestampColumnImpl) fromImpl(subQuery SelectTable) Projection { +func (i *timestampColumnImpl) From(subQuery SelectTable) ColumnTimestamp { newTimestampColumn := TimestampColumn(i.name) newTimestampColumn.setTableName(i.tableName) newTimestampColumn.setSubQuery(subQuery) @@ -246,15 +215,11 @@ func (i *timestampColumnImpl) fromImpl(subQuery SelectTable) Projection { return newTimestampColumn } -func (i *timestampColumnImpl) From(subQuery SelectTable) ColumnTimestamp { - return i.fromImpl(subQuery).(ColumnTimestamp) -} - // TimestampColumn creates named timestamp column func TimestampColumn(name string) ColumnTimestamp { timestampColumn := ×tampColumnImpl{} timestampColumn.timestampInterfaceImpl.parent = timestampColumn - timestampColumn.columnImpl = newColumn(name, "", timestampColumn) + timestampColumn.ColumnExpressionImpl = NewColumnImpl(name, "", timestampColumn) return timestampColumn } @@ -271,11 +236,10 @@ type ColumnTimestampz interface { type timestampzColumnImpl struct { timestampzInterfaceImpl - - columnImpl + ColumnExpressionImpl } -func (i *timestampzColumnImpl) fromImpl(subQuery SelectTable) Projection { +func (i *timestampzColumnImpl) From(subQuery SelectTable) ColumnTimestampz { newTimestampzColumn := TimestampzColumn(i.name) newTimestampzColumn.setTableName(i.tableName) newTimestampzColumn.setSubQuery(subQuery) @@ -283,15 +247,11 @@ func (i *timestampzColumnImpl) fromImpl(subQuery SelectTable) Projection { return newTimestampzColumn } -func (i *timestampzColumnImpl) From(subQuery SelectTable) ColumnTimestampz { - return i.fromImpl(subQuery).(ColumnTimestampz) -} - // TimestampzColumn creates named timestamp with time zone column. func TimestampzColumn(name string) ColumnTimestampz { timestampzColumn := ×tampzColumnImpl{} timestampzColumn.timestampzInterfaceImpl.parent = timestampzColumn - timestampzColumn.columnImpl = newColumn(name, "", timestampzColumn) + timestampzColumn.ColumnExpressionImpl = NewColumnImpl(name, "", timestampzColumn) return timestampzColumn } @@ -308,11 +268,10 @@ type ColumnDate interface { type dateColumnImpl struct { dateInterfaceImpl - - columnImpl + ColumnExpressionImpl } -func (i *dateColumnImpl) fromImpl(subQuery SelectTable) Projection { +func (i *dateColumnImpl) From(subQuery SelectTable) ColumnDate { newDateColumn := DateColumn(i.name) newDateColumn.setTableName(i.tableName) newDateColumn.setSubQuery(subQuery) @@ -320,14 +279,10 @@ func (i *dateColumnImpl) fromImpl(subQuery SelectTable) Projection { return newDateColumn } -func (i *dateColumnImpl) From(subQuery SelectTable) ColumnDate { - return i.fromImpl(subQuery).(ColumnDate) -} - // DateColumn creates named date column. func DateColumn(name string) ColumnDate { dateColumn := &dateColumnImpl{} dateColumn.dateInterfaceImpl.parent = dateColumn - dateColumn.columnImpl = newColumn(name, "", dateColumn) + dateColumn.ColumnExpressionImpl = NewColumnImpl(name, "", dateColumn) return dateColumn } diff --git a/internal/jet/column_types_test.go b/internal/jet/column_types_test.go index 8c00d5b..9cff309 100644 --- a/internal/jet/column_types_test.go +++ b/internal/jet/column_types_test.go @@ -43,5 +43,74 @@ func TestNewFloatColumnColumn(t *testing.T) { assertClauseSerialize(t, floatColumn2, `sub_query."table1.col_float"`) assertClauseSerialize(t, floatColumn2.EQ(Float(2.22)), `(sub_query."table1.col_float" = $1)`, float64(2.22)) assertProjectionSerialize(t, floatColumn2, `sub_query."table1.col_float" AS "table1.col_float"`) - +} + +func TestNewDateColumnColumn(t *testing.T) { + dateColumn := DateColumn("col_date").From(subQuery) + assertClauseSerialize(t, dateColumn, `sub_query."col_date"`) + assertClauseSerialize(t, dateColumn.EQ(Date(2002, 2, 3)), + `(sub_query."col_date" = $1)`, "2002-02-03") + assertProjectionSerialize(t, dateColumn, `sub_query."col_date" AS "col_date"`) + + dateColumn2 := table1ColDate.From(subQuery) + assertClauseSerialize(t, dateColumn2, `sub_query."table1.col_date"`) + assertClauseSerialize(t, dateColumn2.EQ(Date(2002, 2, 3)), + `(sub_query."table1.col_date" = $1)`, "2002-02-03") + assertProjectionSerialize(t, dateColumn2, `sub_query."table1.col_date" AS "table1.col_date"`) +} + +func TestNewTimeColumnColumn(t *testing.T) { + timeColumn := TimeColumn("col_time").From(subQuery) + assertClauseSerialize(t, timeColumn, `sub_query."col_time"`) + assertClauseSerialize(t, timeColumn.EQ(Time(1, 1, 1, 1)), + `(sub_query."col_time" = $1)`, "01:01:01.000000001") + assertProjectionSerialize(t, timeColumn, `sub_query."col_time" AS "col_time"`) + + timeColumn2 := table1ColTime.From(subQuery) + assertClauseSerialize(t, timeColumn2, `sub_query."table1.col_time"`) + assertClauseSerialize(t, timeColumn2.EQ(Time(2, 2, 2)), + `(sub_query."table1.col_time" = $1)`, "02:02:02") + assertProjectionSerialize(t, timeColumn2, `sub_query."table1.col_time" AS "table1.col_time"`) +} + +func TestNewTimezColumnColumn(t *testing.T) { + timezColumn := TimezColumn("col_timez").From(subQuery) + assertClauseSerialize(t, timezColumn, `sub_query."col_timez"`) + assertClauseSerialize(t, timezColumn.EQ(Timez(1, 1, 1, 1, "UTC")), + `(sub_query."col_timez" = $1)`, "01:01:01.000000001 UTC") + assertProjectionSerialize(t, timezColumn, `sub_query."col_timez" AS "col_timez"`) + + timezColumn2 := table1ColTimez.From(subQuery) + assertClauseSerialize(t, timezColumn2, `sub_query."table1.col_timez"`) + assertClauseSerialize(t, timezColumn2.EQ(Timez(2, 2, 2, 0, "UTC")), + `(sub_query."table1.col_timez" = $1)`, "02:02:02 UTC") + assertProjectionSerialize(t, timezColumn2, `sub_query."table1.col_timez" AS "table1.col_timez"`) +} + +func TestNewTimestampColumnColumn(t *testing.T) { + timestampColumn := TimestampColumn("col_timestamp").From(subQuery) + assertClauseSerialize(t, timestampColumn, `sub_query."col_timestamp"`) + assertClauseSerialize(t, timestampColumn.EQ(Timestamp(1, 1, 1, 1, 1, 1)), + `(sub_query."col_timestamp" = $1)`, "0001-01-01 01:01:01") + assertProjectionSerialize(t, timestampColumn, `sub_query."col_timestamp" AS "col_timestamp"`) + + timestampColumn2 := table1ColTimestamp.From(subQuery) + assertClauseSerialize(t, timestampColumn2, `sub_query."table1.col_timestamp"`) + assertClauseSerialize(t, timestampColumn2.EQ(Timestamp(2, 2, 2, 2, 2, 2)), + `(sub_query."table1.col_timestamp" = $1)`, "0002-02-02 02:02:02") + assertProjectionSerialize(t, timestampColumn2, `sub_query."table1.col_timestamp" AS "table1.col_timestamp"`) +} + +func TestNewTimestampzColumnColumn(t *testing.T) { + timestampzColumn := TimestampzColumn("col_timestampz").From(subQuery) + assertClauseSerialize(t, timestampzColumn, `sub_query."col_timestampz"`) + assertClauseSerialize(t, timestampzColumn.EQ(Timestampz(1, 1, 1, 1, 1, 1, 0, "UTC")), + `(sub_query."col_timestampz" = $1)`, "0001-01-01 01:01:01 UTC") + assertProjectionSerialize(t, timestampzColumn, `sub_query."col_timestampz" AS "col_timestampz"`) + + timestampzColumn2 := table1ColTimestampz.From(subQuery) + assertClauseSerialize(t, timestampzColumn2, `sub_query."table1.col_timestampz"`) + assertClauseSerialize(t, timestampzColumn2.EQ(Timestampz(2, 2, 2, 2, 2, 2, 0, "UTC")), + `(sub_query."table1.col_timestampz" = $1)`, "0002-02-02 02:02:02 UTC") + assertProjectionSerialize(t, timestampzColumn2, `sub_query."table1.col_timestampz" AS "table1.col_timestampz"`) } diff --git a/internal/jet/date_expression.go b/internal/jet/date_expression.go index 8b0a524..27c2035 100644 --- a/internal/jet/date_expression.go +++ b/internal/jet/date_expression.go @@ -23,43 +23,43 @@ type dateInterfaceImpl struct { } func (d *dateInterfaceImpl) EQ(rhs DateExpression) BoolExpression { - return eq(d.parent, rhs) + return Eq(d.parent, rhs) } func (d *dateInterfaceImpl) NOT_EQ(rhs DateExpression) BoolExpression { - return notEq(d.parent, rhs) + return NotEq(d.parent, rhs) } func (d *dateInterfaceImpl) IS_DISTINCT_FROM(rhs DateExpression) BoolExpression { - return isDistinctFrom(d.parent, rhs) + return IsDistinctFrom(d.parent, rhs) } func (d *dateInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs DateExpression) BoolExpression { - return isNotDistinctFrom(d.parent, rhs) + return IsNotDistinctFrom(d.parent, rhs) } func (d *dateInterfaceImpl) LT(rhs DateExpression) BoolExpression { - return lt(d.parent, rhs) + return Lt(d.parent, rhs) } func (d *dateInterfaceImpl) LT_EQ(rhs DateExpression) BoolExpression { - return ltEq(d.parent, rhs) + return LtEq(d.parent, rhs) } func (d *dateInterfaceImpl) GT(rhs DateExpression) BoolExpression { - return gt(d.parent, rhs) + return Gt(d.parent, rhs) } func (d *dateInterfaceImpl) GT_EQ(rhs DateExpression) BoolExpression { - return gtEq(d.parent, rhs) + return GtEq(d.parent, rhs) } func (d *dateInterfaceImpl) ADD(rhs Interval) TimestampExpression { - return TimestampExp(newBinaryOperatorExpression(d.parent, rhs, "+")) + return TimestampExp(Add(d.parent, rhs)) } func (d *dateInterfaceImpl) SUB(rhs Interval) TimestampExpression { - return TimestampExp(newBinaryOperatorExpression(d.parent, rhs, "-")) + return TimestampExp(Sub(d.parent, rhs)) } //---------------------------------------------------// diff --git a/internal/jet/expression.go b/internal/jet/expression.go index 26b9186..a463b76 100644 --- a/internal/jet/expression.go +++ b/internal/jet/expression.go @@ -92,7 +92,8 @@ type binaryOperatorExpression struct { operator string } -func newBinaryOperatorExpression(lhs, rhs Serializer, operator string, additionalParam ...Expression) *binaryOperatorExpression { +// NewBinaryOperatorExpression creates new binaryOperatorExpression +func NewBinaryOperatorExpression(lhs, rhs Serializer, operator string, additionalParam ...Expression) *binaryOperatorExpression { binaryExpression := &binaryOperatorExpression{ lhs: lhs, rhs: rhs, diff --git a/internal/jet/float_expression.go b/internal/jet/float_expression.go index aa821ba..20c8d3f 100644 --- a/internal/jet/float_expression.go +++ b/internal/jet/float_expression.go @@ -29,64 +29,59 @@ type floatInterfaceImpl struct { } func (n *floatInterfaceImpl) EQ(rhs FloatExpression) BoolExpression { - return eq(n.parent, rhs) + return Eq(n.parent, rhs) } func (n *floatInterfaceImpl) NOT_EQ(rhs FloatExpression) BoolExpression { - return notEq(n.parent, rhs) + return NotEq(n.parent, rhs) } func (n *floatInterfaceImpl) IS_DISTINCT_FROM(rhs FloatExpression) BoolExpression { - return isDistinctFrom(n.parent, rhs) + return IsDistinctFrom(n.parent, rhs) } func (n *floatInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs FloatExpression) BoolExpression { - return isNotDistinctFrom(n.parent, rhs) + return IsNotDistinctFrom(n.parent, rhs) } func (n *floatInterfaceImpl) GT(rhs FloatExpression) BoolExpression { - return gt(n.parent, rhs) + return Gt(n.parent, rhs) } func (n *floatInterfaceImpl) GT_EQ(rhs FloatExpression) BoolExpression { - return gtEq(n.parent, rhs) + return GtEq(n.parent, rhs) } -func (n *floatInterfaceImpl) LT(expression FloatExpression) BoolExpression { - return lt(n.parent, expression) +func (n *floatInterfaceImpl) LT(rhs FloatExpression) BoolExpression { + return Lt(n.parent, rhs) } -func (n *floatInterfaceImpl) LT_EQ(expression FloatExpression) BoolExpression { - return ltEq(n.parent, expression) +func (n *floatInterfaceImpl) LT_EQ(rhs FloatExpression) BoolExpression { + return LtEq(n.parent, rhs) } -func (n *floatInterfaceImpl) ADD(expression NumericExpression) FloatExpression { - return newBinaryFloatExpression(n.parent, expression, "+") +func (n *floatInterfaceImpl) ADD(rhs NumericExpression) FloatExpression { + return FloatExp(Add(n.parent, rhs)) } -func (n *floatInterfaceImpl) SUB(expression NumericExpression) FloatExpression { - return newBinaryFloatExpression(n.parent, expression, "-") +func (n *floatInterfaceImpl) SUB(rhs NumericExpression) FloatExpression { + return FloatExp(Sub(n.parent, rhs)) } -func (n *floatInterfaceImpl) MUL(expression NumericExpression) FloatExpression { - return newBinaryFloatExpression(n.parent, expression, "*") +func (n *floatInterfaceImpl) MUL(rhs NumericExpression) FloatExpression { + return FloatExp(Mul(n.parent, rhs)) } -func (n *floatInterfaceImpl) DIV(expression NumericExpression) FloatExpression { - return newBinaryFloatExpression(n.parent, expression, "/") +func (n *floatInterfaceImpl) DIV(rhs NumericExpression) FloatExpression { + return FloatExp(Div(n.parent, rhs)) } -func (n *floatInterfaceImpl) MOD(expression NumericExpression) FloatExpression { - return newBinaryFloatExpression(n.parent, expression, "%") +func (n *floatInterfaceImpl) MOD(rhs NumericExpression) FloatExpression { + return FloatExp(Mod(n.parent, rhs)) } -func (n *floatInterfaceImpl) POW(expression NumericExpression) FloatExpression { - return POW(n.parent, expression) -} - -//---------------------------------------------------// -func newBinaryFloatExpression(lhs, rhs Expression, operator string) FloatExpression { - return FloatExp(newBinaryOperatorExpression(lhs, rhs, operator)) +func (n *floatInterfaceImpl) POW(rhs NumericExpression) FloatExpression { + return POW(n.parent, rhs) } //---------------------------------------------------// diff --git a/internal/jet/integer_expression.go b/internal/jet/integer_expression.go index c004437..ff2a0a0 100644 --- a/internal/jet/integer_expression.go +++ b/internal/jet/integer_expression.go @@ -54,71 +54,71 @@ type integerInterfaceImpl struct { } func (i *integerInterfaceImpl) EQ(rhs IntegerExpression) BoolExpression { - return eq(i.parent, rhs) + return Eq(i.parent, rhs) } func (i *integerInterfaceImpl) NOT_EQ(rhs IntegerExpression) BoolExpression { - return notEq(i.parent, rhs) + return NotEq(i.parent, rhs) } func (i *integerInterfaceImpl) IS_DISTINCT_FROM(rhs IntegerExpression) BoolExpression { - return isDistinctFrom(i.parent, rhs) + return IsDistinctFrom(i.parent, rhs) } func (i *integerInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs IntegerExpression) BoolExpression { - return isNotDistinctFrom(i.parent, rhs) + return IsNotDistinctFrom(i.parent, rhs) } func (i *integerInterfaceImpl) GT(rhs IntegerExpression) BoolExpression { - return gt(i.parent, rhs) + return Gt(i.parent, rhs) } func (i *integerInterfaceImpl) GT_EQ(rhs IntegerExpression) BoolExpression { - return gtEq(i.parent, rhs) + return GtEq(i.parent, rhs) } -func (i *integerInterfaceImpl) LT(expression IntegerExpression) BoolExpression { - return lt(i.parent, expression) +func (i *integerInterfaceImpl) LT(rhs IntegerExpression) BoolExpression { + return Lt(i.parent, rhs) } -func (i *integerInterfaceImpl) LT_EQ(expression IntegerExpression) BoolExpression { - return ltEq(i.parent, expression) +func (i *integerInterfaceImpl) LT_EQ(rhs IntegerExpression) BoolExpression { + return LtEq(i.parent, rhs) } -func (i *integerInterfaceImpl) ADD(expression IntegerExpression) IntegerExpression { - return newBinaryIntegerOperatorExpression(i.parent, expression, "+") +func (i *integerInterfaceImpl) ADD(rhs IntegerExpression) IntegerExpression { + return IntExp(Add(i.parent, rhs)) } -func (i *integerInterfaceImpl) SUB(expression IntegerExpression) IntegerExpression { - return newBinaryIntegerOperatorExpression(i.parent, expression, "-") +func (i *integerInterfaceImpl) SUB(rhs IntegerExpression) IntegerExpression { + return IntExp(Sub(i.parent, rhs)) } -func (i *integerInterfaceImpl) MUL(expression IntegerExpression) IntegerExpression { - return newBinaryIntegerOperatorExpression(i.parent, expression, "*") +func (i *integerInterfaceImpl) MUL(rhs IntegerExpression) IntegerExpression { + return IntExp(Mul(i.parent, rhs)) } -func (i *integerInterfaceImpl) DIV(expression IntegerExpression) IntegerExpression { - return newBinaryIntegerOperatorExpression(i.parent, expression, "/") +func (i *integerInterfaceImpl) DIV(rhs IntegerExpression) IntegerExpression { + return IntExp(Div(i.parent, rhs)) } -func (i *integerInterfaceImpl) MOD(expression IntegerExpression) IntegerExpression { - return newBinaryIntegerOperatorExpression(i.parent, expression, "%") +func (i *integerInterfaceImpl) MOD(rhs IntegerExpression) IntegerExpression { + return IntExp(Mod(i.parent, rhs)) } -func (i *integerInterfaceImpl) POW(expression IntegerExpression) IntegerExpression { - return IntExp(POW(i.parent, expression)) +func (i *integerInterfaceImpl) POW(rhs IntegerExpression) IntegerExpression { + return IntExp(POW(i.parent, rhs)) } -func (i *integerInterfaceImpl) BIT_AND(expression IntegerExpression) IntegerExpression { - return newBinaryIntegerOperatorExpression(i.parent, expression, "&") +func (i *integerInterfaceImpl) BIT_AND(rhs IntegerExpression) IntegerExpression { + return newBinaryIntegerOperatorExpression(i.parent, rhs, "&") } -func (i *integerInterfaceImpl) BIT_OR(expression IntegerExpression) IntegerExpression { - return newBinaryIntegerOperatorExpression(i.parent, expression, "|") +func (i *integerInterfaceImpl) BIT_OR(rhs IntegerExpression) IntegerExpression { + return newBinaryIntegerOperatorExpression(i.parent, rhs, "|") } -func (i *integerInterfaceImpl) BIT_XOR(expression IntegerExpression) IntegerExpression { - return newBinaryIntegerOperatorExpression(i.parent, expression, "#") +func (i *integerInterfaceImpl) BIT_XOR(rhs IntegerExpression) IntegerExpression { + return newBinaryIntegerOperatorExpression(i.parent, rhs, "#") } func (i *integerInterfaceImpl) BIT_SHIFT_LEFT(intExpression IntegerExpression) IntegerExpression { @@ -131,7 +131,7 @@ func (i *integerInterfaceImpl) BIT_SHIFT_RIGHT(intExpression IntegerExpression) //---------------------------------------------------// func newBinaryIntegerOperatorExpression(lhs, rhs IntegerExpression, operator string) IntegerExpression { - return IntExp(newBinaryOperatorExpression(lhs, rhs, operator)) + return IntExp(NewBinaryOperatorExpression(lhs, rhs, operator)) } //---------------------------------------------------// diff --git a/internal/jet/interval.go b/internal/jet/interval.go index e66ca56..fab84e0 100644 --- a/internal/jet/interval.go +++ b/internal/jet/interval.go @@ -11,22 +11,27 @@ type IsInterval interface { isInterval() } +// IsIntervalImpl is implementation of IsInterval interface +type IsIntervalImpl struct{} + +func (i *IsIntervalImpl) isInterval() {} + // NewInterval creates new interval from serializer -func NewInterval(s Serializer) Interval { - newInterval := &intervalImpl{ +func NewInterval(s Serializer) *IntervalImpl { + newInterval := &IntervalImpl{ interval: s, } return newInterval } -type intervalImpl struct { +// IntervalImpl is implementation of Interval type +type IntervalImpl struct { interval Serializer + IsIntervalImpl } -func (i intervalImpl) isInterval() {} - -func (i intervalImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) { +func (i IntervalImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) { out.WriteString("INTERVAL") i.interval.serialize(statement, out, options...) } diff --git a/internal/jet/literal_expression.go b/internal/jet/literal_expression.go index 499b7b4..15c8a4a 100644 --- a/internal/jet/literal_expression.go +++ b/internal/jet/literal_expression.go @@ -346,9 +346,9 @@ func (n *rawExpression) serialize(statement StatementType, out *SQLBuilder, opti // Raw can be used for any unsupported functions, operators or expressions. // For example: Raw("current_database()") -func Raw(raw string) Expression { +func Raw(raw string, parent ...Expression) Expression { rawExp := &rawExpression{Raw: raw} - rawExp.ExpressionInterfaceImpl.Parent = rawExp + rawExp.ExpressionInterfaceImpl.Parent = OptionalOrDefaultExpression(rawExp, parent...) return rawExp } diff --git a/internal/jet/operators.go b/internal/jet/operators.go index fad1e26..d17081c 100644 --- a/internal/jet/operators.go +++ b/internal/jet/operators.go @@ -29,44 +29,71 @@ func EXISTS(subQuery Expression) BoolExpression { return newPrefixBoolOperatorExpression(subQuery, "EXISTS") } -// Returns a representation of "a=b" -func eq(lhs, rhs Expression) BoolExpression { +// Eq returns a representation of "a=b" +func Eq(lhs, rhs Expression) BoolExpression { return newBinaryBoolOperatorExpression(lhs, rhs, "=") } -// Returns a representation of "a!=b" -func notEq(lhs, rhs Expression) BoolExpression { +// NotEq returns a representation of "a!=b" +func NotEq(lhs, rhs Expression) BoolExpression { return newBinaryBoolOperatorExpression(lhs, rhs, "!=") } -func isDistinctFrom(lhs, rhs Expression) BoolExpression { +// IsDistinctFrom returns a representation of "a IS DISTINCT FROM b" +func IsDistinctFrom(lhs, rhs Expression) BoolExpression { return newBinaryBoolOperatorExpression(lhs, rhs, "IS DISTINCT FROM") } -func isNotDistinctFrom(lhs, rhs Expression) BoolExpression { +// IsNotDistinctFrom returns a representation of "a IS NOT DISTINCT FROM b" +func IsNotDistinctFrom(lhs, rhs Expression) BoolExpression { return newBinaryBoolOperatorExpression(lhs, rhs, "IS NOT DISTINCT FROM") } -// Returns a representation of "ab" -func gt(lhs, rhs Expression) BoolExpression { +// Gt returns a representation of "a>b" +func Gt(lhs, rhs Expression) BoolExpression { return newBinaryBoolOperatorExpression(lhs, rhs, ">") } -// Returns a representation of "a>=b" -func gtEq(lhs, rhs Expression) BoolExpression { +// GtEq returns a representation of "a>=b" +func GtEq(lhs, rhs Expression) BoolExpression { return newBinaryBoolOperatorExpression(lhs, rhs, ">=") } +// Add notEq returns a representation of "a + b" +func Add(lhs, rhs Serializer) Expression { + return NewBinaryOperatorExpression(lhs, rhs, "+") +} + +// Sub notEq returns a representation of "a - b" +func Sub(lhs, rhs Serializer) Expression { + return NewBinaryOperatorExpression(lhs, rhs, "-") +} + +// Mul returns a representation of "a * b" +func Mul(lhs, rhs Serializer) Expression { + return NewBinaryOperatorExpression(lhs, rhs, "*") +} + +// Div returns a representation of "a / b" +func Div(lhs, rhs Serializer) Expression { + return NewBinaryOperatorExpression(lhs, rhs, "/") +} + +// Mod returns a representation of "a % b" +func Mod(lhs, rhs Serializer) Expression { + return NewBinaryOperatorExpression(lhs, rhs, "%") +} + // --------------- CASE operator -------------------// // CaseOperator is interface for SQL case operator diff --git a/internal/jet/string_expression.go b/internal/jet/string_expression.go index 29ceca6..3c56896 100644 --- a/internal/jet/string_expression.go +++ b/internal/jet/string_expression.go @@ -28,35 +28,35 @@ type stringInterfaceImpl struct { } func (s *stringInterfaceImpl) EQ(rhs StringExpression) BoolExpression { - return eq(s.parent, rhs) + return Eq(s.parent, rhs) } func (s *stringInterfaceImpl) NOT_EQ(rhs StringExpression) BoolExpression { - return notEq(s.parent, rhs) + return NotEq(s.parent, rhs) } func (s *stringInterfaceImpl) IS_DISTINCT_FROM(rhs StringExpression) BoolExpression { - return isDistinctFrom(s.parent, rhs) + return IsDistinctFrom(s.parent, rhs) } func (s *stringInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs StringExpression) BoolExpression { - return isNotDistinctFrom(s.parent, rhs) + return IsNotDistinctFrom(s.parent, rhs) } func (s *stringInterfaceImpl) GT(rhs StringExpression) BoolExpression { - return gt(s.parent, rhs) + return Gt(s.parent, rhs) } func (s *stringInterfaceImpl) GT_EQ(rhs StringExpression) BoolExpression { - return gtEq(s.parent, rhs) + return GtEq(s.parent, rhs) } func (s *stringInterfaceImpl) LT(rhs StringExpression) BoolExpression { - return lt(s.parent, rhs) + return Lt(s.parent, rhs) } func (s *stringInterfaceImpl) LT_EQ(rhs StringExpression) BoolExpression { - return ltEq(s.parent, rhs) + return LtEq(s.parent, rhs) } func (s *stringInterfaceImpl) CONCAT(rhs Expression) StringExpression { @@ -81,7 +81,7 @@ func (s *stringInterfaceImpl) NOT_REGEXP_LIKE(pattern StringExpression, caseSens //---------------------------------------------------// func newBinaryStringOperatorExpression(lhs, rhs Expression, operator string) StringExpression { - return StringExp(newBinaryOperatorExpression(lhs, rhs, operator)) + return StringExp(NewBinaryOperatorExpression(lhs, rhs, operator)) } //---------------------------------------------------// diff --git a/internal/jet/time_expression.go b/internal/jet/time_expression.go index b83f731..4fd7047 100644 --- a/internal/jet/time_expression.go +++ b/internal/jet/time_expression.go @@ -23,43 +23,43 @@ type timeInterfaceImpl struct { } func (t *timeInterfaceImpl) EQ(rhs TimeExpression) BoolExpression { - return eq(t.parent, rhs) + return Eq(t.parent, rhs) } func (t *timeInterfaceImpl) NOT_EQ(rhs TimeExpression) BoolExpression { - return notEq(t.parent, rhs) + return NotEq(t.parent, rhs) } func (t *timeInterfaceImpl) IS_DISTINCT_FROM(rhs TimeExpression) BoolExpression { - return isDistinctFrom(t.parent, rhs) + return IsDistinctFrom(t.parent, rhs) } func (t *timeInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs TimeExpression) BoolExpression { - return isNotDistinctFrom(t.parent, rhs) + return IsNotDistinctFrom(t.parent, rhs) } func (t *timeInterfaceImpl) LT(rhs TimeExpression) BoolExpression { - return lt(t.parent, rhs) + return Lt(t.parent, rhs) } func (t *timeInterfaceImpl) LT_EQ(rhs TimeExpression) BoolExpression { - return ltEq(t.parent, rhs) + return LtEq(t.parent, rhs) } func (t *timeInterfaceImpl) GT(rhs TimeExpression) BoolExpression { - return gt(t.parent, rhs) + return Gt(t.parent, rhs) } func (t *timeInterfaceImpl) GT_EQ(rhs TimeExpression) BoolExpression { - return gtEq(t.parent, rhs) + return GtEq(t.parent, rhs) } func (t *timeInterfaceImpl) ADD(rhs Interval) TimeExpression { - return TimeExp(newBinaryOperatorExpression(t.parent, rhs, "+")) + return TimeExp(Add(t.parent, rhs)) } func (t *timeInterfaceImpl) SUB(rhs Interval) TimeExpression { - return TimeExp(newBinaryOperatorExpression(t.parent, rhs, "-")) + return TimeExp(Sub(t.parent, rhs)) } //---------------------------------------------------// diff --git a/internal/jet/timestamp_expression.go b/internal/jet/timestamp_expression.go index 81eda61..f4cdd0b 100644 --- a/internal/jet/timestamp_expression.go +++ b/internal/jet/timestamp_expression.go @@ -23,43 +23,43 @@ type timestampInterfaceImpl struct { } func (t *timestampInterfaceImpl) EQ(rhs TimestampExpression) BoolExpression { - return eq(t.parent, rhs) + return Eq(t.parent, rhs) } func (t *timestampInterfaceImpl) NOT_EQ(rhs TimestampExpression) BoolExpression { - return notEq(t.parent, rhs) + return NotEq(t.parent, rhs) } func (t *timestampInterfaceImpl) IS_DISTINCT_FROM(rhs TimestampExpression) BoolExpression { - return isDistinctFrom(t.parent, rhs) + return IsDistinctFrom(t.parent, rhs) } func (t *timestampInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs TimestampExpression) BoolExpression { - return isNotDistinctFrom(t.parent, rhs) + return IsNotDistinctFrom(t.parent, rhs) } func (t *timestampInterfaceImpl) LT(rhs TimestampExpression) BoolExpression { - return lt(t.parent, rhs) + return Lt(t.parent, rhs) } func (t *timestampInterfaceImpl) LT_EQ(rhs TimestampExpression) BoolExpression { - return ltEq(t.parent, rhs) + return LtEq(t.parent, rhs) } func (t *timestampInterfaceImpl) GT(rhs TimestampExpression) BoolExpression { - return gt(t.parent, rhs) + return Gt(t.parent, rhs) } func (t *timestampInterfaceImpl) GT_EQ(rhs TimestampExpression) BoolExpression { - return gtEq(t.parent, rhs) + return GtEq(t.parent, rhs) } func (t *timestampInterfaceImpl) ADD(rhs Interval) TimestampExpression { - return TimestampExp(newBinaryOperatorExpression(t.parent, rhs, "+")) + return TimestampExp(Add(t.parent, rhs)) } func (t *timestampInterfaceImpl) SUB(rhs Interval) TimestampExpression { - return TimestampExp(newBinaryOperatorExpression(t.parent, rhs, "-")) + return TimestampExp(Sub(t.parent, rhs)) } //------------------------------------------------- diff --git a/internal/jet/timestampz_expression.go b/internal/jet/timestampz_expression.go index a9f8c9f..0112a3c 100644 --- a/internal/jet/timestampz_expression.go +++ b/internal/jet/timestampz_expression.go @@ -23,43 +23,43 @@ type timestampzInterfaceImpl struct { } func (t *timestampzInterfaceImpl) EQ(rhs TimestampzExpression) BoolExpression { - return eq(t.parent, rhs) + return Eq(t.parent, rhs) } func (t *timestampzInterfaceImpl) NOT_EQ(rhs TimestampzExpression) BoolExpression { - return notEq(t.parent, rhs) + return NotEq(t.parent, rhs) } func (t *timestampzInterfaceImpl) IS_DISTINCT_FROM(rhs TimestampzExpression) BoolExpression { - return isDistinctFrom(t.parent, rhs) + return IsDistinctFrom(t.parent, rhs) } func (t *timestampzInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs TimestampzExpression) BoolExpression { - return isNotDistinctFrom(t.parent, rhs) + return IsNotDistinctFrom(t.parent, rhs) } func (t *timestampzInterfaceImpl) LT(rhs TimestampzExpression) BoolExpression { - return lt(t.parent, rhs) + return Lt(t.parent, rhs) } func (t *timestampzInterfaceImpl) LT_EQ(rhs TimestampzExpression) BoolExpression { - return ltEq(t.parent, rhs) + return LtEq(t.parent, rhs) } func (t *timestampzInterfaceImpl) GT(rhs TimestampzExpression) BoolExpression { - return gt(t.parent, rhs) + return Gt(t.parent, rhs) } func (t *timestampzInterfaceImpl) GT_EQ(rhs TimestampzExpression) BoolExpression { - return gtEq(t.parent, rhs) + return GtEq(t.parent, rhs) } func (t *timestampzInterfaceImpl) ADD(rhs Interval) TimestampzExpression { - return TimestampzExp(newBinaryOperatorExpression(t.parent, rhs, "+")) + return TimestampzExp(Add(t.parent, rhs)) } func (t *timestampzInterfaceImpl) SUB(rhs Interval) TimestampzExpression { - return TimestampzExp(newBinaryOperatorExpression(t.parent, rhs, "-")) + return TimestampzExp(Sub(t.parent, rhs)) } //------------------------------------------------- diff --git a/internal/jet/timez_expression.go b/internal/jet/timez_expression.go index c791c62..d36ec80 100644 --- a/internal/jet/timez_expression.go +++ b/internal/jet/timez_expression.go @@ -23,43 +23,43 @@ type timezInterfaceImpl struct { } func (t *timezInterfaceImpl) EQ(rhs TimezExpression) BoolExpression { - return eq(t.parent, rhs) + return Eq(t.parent, rhs) } func (t *timezInterfaceImpl) NOT_EQ(rhs TimezExpression) BoolExpression { - return notEq(t.parent, rhs) + return NotEq(t.parent, rhs) } func (t *timezInterfaceImpl) IS_DISTINCT_FROM(rhs TimezExpression) BoolExpression { - return isDistinctFrom(t.parent, rhs) + return IsDistinctFrom(t.parent, rhs) } func (t *timezInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs TimezExpression) BoolExpression { - return isNotDistinctFrom(t.parent, rhs) + return IsNotDistinctFrom(t.parent, rhs) } func (t *timezInterfaceImpl) LT(rhs TimezExpression) BoolExpression { - return lt(t.parent, rhs) + return Lt(t.parent, rhs) } func (t *timezInterfaceImpl) LT_EQ(rhs TimezExpression) BoolExpression { - return ltEq(t.parent, rhs) + return LtEq(t.parent, rhs) } func (t *timezInterfaceImpl) GT(rhs TimezExpression) BoolExpression { - return gt(t.parent, rhs) + return Gt(t.parent, rhs) } func (t *timezInterfaceImpl) GT_EQ(rhs TimezExpression) BoolExpression { - return gtEq(t.parent, rhs) + return GtEq(t.parent, rhs) } func (t *timezInterfaceImpl) ADD(rhs Interval) TimezExpression { - return TimezExp(newBinaryOperatorExpression(t.parent, rhs, "+")) + return TimezExp(Add(t.parent, rhs)) } func (t *timezInterfaceImpl) SUB(rhs Interval) TimezExpression { - return TimezExp(newBinaryOperatorExpression(t.parent, rhs, "-")) + return TimezExp(Sub(t.parent, rhs)) } //---------------------------------------------------// diff --git a/internal/jet/utils.go b/internal/jet/utils.go index 58394f4..126ce32 100644 --- a/internal/jet/utils.go +++ b/internal/jet/utils.go @@ -187,3 +187,23 @@ func UnwidColumnList(columns []Column) []Column { return ret } + +// OptionalOrDefaultString will return first value from variable argument list str or +// defaultStr if variable argument list is empty +func OptionalOrDefaultString(defaultStr string, str ...string) string { + if len(str) > 0 { + return str[0] + } + + return defaultStr +} + +// OptionalOrDefaultExpression will return first value from variable argument list expression or +// defaultExpression if variable argument list is empty +func OptionalOrDefaultExpression(defaultExpression Expression, expression ...Expression) Expression { + if len(expression) > 0 { + return expression[0] + } + + return defaultExpression +} diff --git a/internal/jet/utils_test.go b/internal/jet/utils_test.go new file mode 100644 index 0000000..ce1935a --- /dev/null +++ b/internal/jet/utils_test.go @@ -0,0 +1,19 @@ +package jet + +import ( + "gotest.tools/assert" + "testing" +) + +func TestOptionalOrDefaultString(t *testing.T) { + assert.Equal(t, OptionalOrDefaultString("default"), "default") + assert.Equal(t, OptionalOrDefaultString("default", "optional"), "optional") +} + +func TestOptionalOrDefaultExpression(t *testing.T) { + defaultExpression := table2ColFloat + optionalExpression := table1Col1 + + assert.Equal(t, OptionalOrDefaultExpression(defaultExpression), defaultExpression) + assert.Equal(t, OptionalOrDefaultExpression(defaultExpression, optionalExpression), optionalExpression) +} diff --git a/postgres/columns.go b/postgres/columns.go index 3109bd3..c62f202 100644 --- a/postgres/columns.go +++ b/postgres/columns.go @@ -1,6 +1,8 @@ package postgres -import "github.com/go-jet/jet/internal/jet" +import ( + "github.com/go-jet/jet/internal/jet" +) // Column is common column interface for all types of columns. type Column = jet.ColumnExpression @@ -62,3 +64,34 @@ type ColumnTimestampz = jet.ColumnTimestampz // TimestampzColumn creates named timestamp with time zone column. var TimestampzColumn = jet.TimestampzColumn + +//------------------------------------------------------// + +// ColumnInterval is interface of PostgreSQL interval columns. +type ColumnInterval interface { + IntervalExpression + jet.Column + + From(subQuery SelectTable) ColumnInterval +} + +type intervalColumnImpl struct { + jet.ColumnExpressionImpl + intervalInterfaceImpl +} + +func (i *intervalColumnImpl) From(subQuery SelectTable) ColumnInterval { + newIntervalColumn := IntervalColumn(i.Name()) + jet.SetTableName(newIntervalColumn, i.TableName()) + jet.SetSubQuery(newIntervalColumn, subQuery) + + return newIntervalColumn +} + +// IntervalColumn creates named interval column. +func IntervalColumn(name string) ColumnInterval { + intervalColumn := &intervalColumnImpl{} + intervalColumn.ColumnExpressionImpl = jet.NewColumnImpl(name, "", intervalColumn) + intervalColumn.intervalInterfaceImpl.parent = intervalColumn + return intervalColumn +} diff --git a/postgres/columns_test.go b/postgres/columns_test.go new file mode 100644 index 0000000..b00c4c6 --- /dev/null +++ b/postgres/columns_test.go @@ -0,0 +1,20 @@ +package postgres + +import ( + "testing" +) + +func TestNewIntervalColumn(t *testing.T) { + subQuery := SELECT(Int(1)).AsTable("sub_query") + + subQueryIntervalColumn := IntervalColumn("col_interval").From(subQuery) + assertSerialize(t, subQueryIntervalColumn, `sub_query."col_interval"`) + assertSerialize(t, subQueryIntervalColumn.EQ(INTERVAL(2, HOUR, 10, MINUTE)), + `(sub_query."col_interval" = INTERVAL '2 HOUR 10 MINUTE')`) + assertProjectionSerialize(t, subQueryIntervalColumn, `sub_query."col_interval" AS "col_interval"`) + + subQueryIntervalColumn2 := table1ColInterval.From(subQuery) + assertSerialize(t, subQueryIntervalColumn2, `sub_query."table1.col_interval"`) + assertSerialize(t, subQueryIntervalColumn2.EQ(INTERVAL(1, DAY)), `(sub_query."table1.col_interval" = INTERVAL '1 DAY')`) + assertProjectionSerialize(t, subQueryIntervalColumn2, `sub_query."table1.col_interval" AS "table1.col_interval"`) +} diff --git a/postgres/expressions.go b/postgres/expressions.go index 0383eee..93072b8 100644 --- a/postgres/expressions.go +++ b/postgres/expressions.go @@ -12,6 +12,9 @@ type BoolExpression = jet.BoolExpression // StringExpression interface type StringExpression = jet.StringExpression +// NumericExpression interface +type NumericExpression = jet.NumericExpression + // IntegerExpression interface type IntegerExpression = jet.IntegerExpression diff --git a/postgres/interval.go b/postgres/interval_expression.go similarity index 52% rename from postgres/interval.go rename to postgres/interval_expression.go index 80592e8..50d29da 100644 --- a/postgres/interval.go +++ b/postgres/interval_expression.go @@ -27,39 +27,109 @@ const ( MILLENNIUM ) -type intervalExpressionImpl struct { - jet.Interval - jet.ExpressionInterfaceImpl -} - // IntervalExpression is representation of postgres INTERVAL type IntervalExpression interface { jet.IsInterval jet.Expression + + EQ(rhs IntervalExpression) BoolExpression + NOT_EQ(rhs IntervalExpression) BoolExpression + IS_DISTINCT_FROM(rhs IntervalExpression) BoolExpression + IS_NOT_DISTINCT_FROM(rhs IntervalExpression) BoolExpression + + LT(rhs IntervalExpression) BoolExpression + LT_EQ(rhs IntervalExpression) BoolExpression + GT(rhs IntervalExpression) BoolExpression + GT_EQ(rhs IntervalExpression) BoolExpression + + ADD(rhs IntervalExpression) IntervalExpression + SUB(rhs IntervalExpression) IntervalExpression + + MUL(rhs NumericExpression) IntervalExpression + DIV(rhs NumericExpression) IntervalExpression +} + +type intervalInterfaceImpl struct { + jet.IsIntervalImpl + + parent IntervalExpression +} + +func (i *intervalInterfaceImpl) EQ(rhs IntervalExpression) BoolExpression { + return jet.Eq(i.parent, rhs) +} + +func (i *intervalInterfaceImpl) NOT_EQ(rhs IntervalExpression) BoolExpression { + return jet.NotEq(i.parent, rhs) +} + +func (i *intervalInterfaceImpl) IS_DISTINCT_FROM(rhs IntervalExpression) BoolExpression { + return jet.IsDistinctFrom(i.parent, rhs) +} + +func (i *intervalInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs IntervalExpression) BoolExpression { + return jet.IsNotDistinctFrom(i.parent, rhs) +} + +func (i *intervalInterfaceImpl) LT(rhs IntervalExpression) BoolExpression { + return jet.Lt(i.parent, rhs) +} + +func (i *intervalInterfaceImpl) LT_EQ(rhs IntervalExpression) BoolExpression { + return jet.LtEq(i.parent, rhs) +} + +func (i *intervalInterfaceImpl) GT(rhs IntervalExpression) BoolExpression { + return jet.Gt(i.parent, rhs) +} + +func (i *intervalInterfaceImpl) GT_EQ(rhs IntervalExpression) BoolExpression { + return jet.GtEq(i.parent, rhs) +} + +func (i *intervalInterfaceImpl) ADD(rhs IntervalExpression) IntervalExpression { + return IntervalExp(jet.Add(i.parent, rhs)) +} + +func (i *intervalInterfaceImpl) SUB(rhs IntervalExpression) IntervalExpression { + return IntervalExp(jet.Sub(i.parent, rhs)) +} + +func (i *intervalInterfaceImpl) MUL(rhs NumericExpression) IntervalExpression { + return IntervalExp(jet.Mul(i.parent, rhs)) +} + +func (i *intervalInterfaceImpl) DIV(rhs NumericExpression) IntervalExpression { + return IntervalExp(jet.Div(i.parent, rhs)) +} + +type intervalExpression struct { + jet.Expression + intervalInterfaceImpl } // INTERVAL creates new interval expression from the list of quantity-unit pairs. // For example: INTERVAL(1, DAY, 3, MINUTE) func INTERVAL(quantityAndUnit ...quantityAndUnit) IntervalExpression { - if len(quantityAndUnit)%2 != 0 { + quantityAndUnitLen := len(quantityAndUnit) + if quantityAndUnitLen == 0 || quantityAndUnitLen%2 != 0 { panic("jet: invalid number of quantity and unit fields") } fields := []string{} for i := 0; i < len(quantityAndUnit); i += 2 { - quantity := strconv.FormatFloat(float64(quantityAndUnit[i]), 'f', -1, 64) + quantity := strconv.FormatFloat(quantityAndUnit[i], 'f', -1, 64) unitString := unitToString(quantityAndUnit[i+1]) fields = append(fields, quantity+" "+unitString) } - intervalStr := fmt.Sprintf("'%s'", strings.Join(fields, " ")) + intervalStr := fmt.Sprintf("INTERVAL '%s'", strings.Join(fields, " ")) - newInterval := &intervalExpressionImpl{ - Interval: jet.NewInterval(jet.Raw(intervalStr)), - } + newInterval := &intervalExpression{} - newInterval.ExpressionInterfaceImpl.Parent = newInterval + newInterval.Expression = jet.Raw(intervalStr, newInterval) + newInterval.intervalInterfaceImpl.parent = newInterval return newInterval } @@ -136,13 +206,14 @@ func unitToString(unit quantityAndUnit) string { //---------------------------------------------------// type intervalWrapper struct { - jet.IsInterval + intervalInterfaceImpl Expression } func newIntervalExpressionWrap(expression Expression) IntervalExpression { - intervalWrap := intervalWrapper{Expression: expression} - return &intervalWrap + intervalWrap := &intervalWrapper{Expression: expression} + intervalWrap.intervalInterfaceImpl.parent = intervalWrap + return intervalWrap } // IntervalExp is interval expression wrapper around arbitrary expression. diff --git a/postgres/interval_test.go b/postgres/interval_expression_test.go similarity index 61% rename from postgres/interval_test.go rename to postgres/interval_expression_test.go index 785f1d5..f2fa9fe 100644 --- a/postgres/interval_test.go +++ b/postgres/interval_expression_test.go @@ -44,11 +44,12 @@ func TestINTERVALd(t *testing.T) { } func TestINTERVAL_InvalidParams(t *testing.T) { + assertPanicErr(t, func() { INTERVAL() }, "jet: invalid number of quantity and unit fields") assertPanicErr(t, func() { INTERVAL(1) }, "jet: invalid number of quantity and unit fields") assertPanicErr(t, func() { INTERVAL(1, 2) }, "jet: invalid INTERVAL unit type") } -func TestIntervalArithmetic(t *testing.T) { +func TestDateTimeIntervalArithmetic(t *testing.T) { assertSerialize(t, table2ColDate.ADD(INTERVAL(1, HOUR)), "(table2.col_date + INTERVAL '1 HOUR')") assertSerialize(t, table2ColDate.SUB(INTERVAL(1, HOUR)), "(table2.col_date - INTERVAL '1 HOUR')") assertSerialize(t, table2ColTime.ADD(INTERVAL(1, HOUR)), "(table2.col_time + INTERVAL '1 HOUR')") @@ -60,3 +61,24 @@ func TestIntervalArithmetic(t *testing.T) { assertSerialize(t, table2ColTimestampz.ADD(INTERVAL(1, HOUR)), "(table2.col_timestampz + INTERVAL '1 HOUR')") assertSerialize(t, table2ColTimestampz.SUB(INTERVAL(1, HOUR)), "(table2.col_timestampz - INTERVAL '1 HOUR')") } + +func TestIntervalExpressionMethods(t *testing.T) { + assertSerialize(t, table1ColInterval.EQ(table2ColInterval), "(table1.col_interval = table2.col_interval)") + assertSerialize(t, table1ColInterval.EQ(INTERVAL(10, SECOND)), "(table1.col_interval = INTERVAL '10 SECOND')") + assertSerialize(t, table1ColInterval.EQ(INTERVALd(11*time.Minute)), "(table1.col_interval = INTERVAL '11 MINUTE')") + assertSerialize(t, table1ColInterval.EQ(INTERVALd(11*time.Minute)).EQ(Bool(false)), + "((table1.col_interval = INTERVAL '11 MINUTE') = $1)", false) + assertSerialize(t, table1ColInterval.NOT_EQ(table2ColInterval), "(table1.col_interval != table2.col_interval)") + assertSerialize(t, table1ColInterval.IS_DISTINCT_FROM(table2ColInterval), "(table1.col_interval IS DISTINCT FROM table2.col_interval)") + assertSerialize(t, table1ColInterval.IS_NOT_DISTINCT_FROM(table2ColInterval), "(table1.col_interval IS NOT DISTINCT FROM table2.col_interval)") + assertSerialize(t, table1ColInterval.LT(table2ColInterval), "(table1.col_interval < table2.col_interval)") + assertSerialize(t, table1ColInterval.LT_EQ(table2ColInterval), "(table1.col_interval <= table2.col_interval)") + assertSerialize(t, table1ColInterval.GT(table2ColInterval), "(table1.col_interval > table2.col_interval)") + assertSerialize(t, table1ColInterval.GT_EQ(table2ColInterval), "(table1.col_interval >= table2.col_interval)") + assertSerialize(t, table1ColInterval.ADD(table2ColInterval), "(table1.col_interval + table2.col_interval)") + assertSerialize(t, table1ColInterval.SUB(table2ColInterval), "(table1.col_interval - table2.col_interval)") + assertSerialize(t, table1ColInterval.MUL(table2ColInt), "(table1.col_interval * table2.col_int)") + assertSerialize(t, table1ColInterval.MUL(table2ColFloat), "(table1.col_interval * table2.col_float)") + assertSerialize(t, table1ColInterval.DIV(table2ColInt), "(table1.col_interval / table2.col_int)") + assertSerialize(t, table1ColInterval.DIV(table2ColFloat), "(table1.col_interval / table2.col_float)") +} diff --git a/postgres/utils_test.go b/postgres/utils_test.go index 4a80954..38c429c 100644 --- a/postgres/utils_test.go +++ b/postgres/utils_test.go @@ -17,6 +17,7 @@ var table1ColTimestamp = TimestampColumn("col_timestamp") var table1ColTimestampz = TimestampzColumn("col_timestampz") var table1ColBool = BoolColumn("col_bool") var table1ColDate = DateColumn("col_date") +var table1ColInterval = IntervalColumn("col_interval") var table1 = NewTable( "db", @@ -31,6 +32,7 @@ var table1 = NewTable( table1ColDate, table1ColTimestamp, table1ColTimestampz, + table1ColInterval, ) var table2Col3 = IntegerColumn("col3") @@ -44,6 +46,7 @@ var table2ColTimez = TimezColumn("col_timez") var table2ColTimestamp = TimestampColumn("col_timestamp") var table2ColTimestampz = TimestampzColumn("col_timestampz") var table2ColDate = DateColumn("col_date") +var table2ColInterval = IntervalColumn("col_interval") var table2 = NewTable( "db", @@ -59,6 +62,7 @@ var table2 = NewTable( table2ColDate, table2ColTimestamp, table2ColTimestampz, + table2ColInterval, ) var table3Col1 = IntegerColumn("col1") diff --git a/tests/postgres/alltypes_test.go b/tests/postgres/alltypes_test.go index 7b9d1d6..fbcffd8 100644 --- a/tests/postgres/alltypes_test.go +++ b/tests/postgres/alltypes_test.go @@ -71,6 +71,152 @@ func TestAllTypesInsertQuery(t *testing.T) { assert.DeepEqual(t, dest[1], allTypesRow1) } +func TestAllTypesFromSubQuery(t *testing.T) { + + subQuery := SELECT(AllTypes.AllColumns). + FROM(AllTypes). + AsTable("allTypesSubQuery") + + mainQuery := SELECT(subQuery.AllColumns()). + FROM(subQuery). + LIMIT(2) + + assert.Equal(t, mainQuery.DebugSql(), ` +SELECT "allTypesSubQuery"."all_types.small_int_ptr" AS "all_types.small_int_ptr", + "allTypesSubQuery"."all_types.small_int" AS "all_types.small_int", + "allTypesSubQuery"."all_types.integer_ptr" AS "all_types.integer_ptr", + "allTypesSubQuery"."all_types.integer" AS "all_types.integer", + "allTypesSubQuery"."all_types.big_int_ptr" AS "all_types.big_int_ptr", + "allTypesSubQuery"."all_types.big_int" AS "all_types.big_int", + "allTypesSubQuery"."all_types.decimal_ptr" AS "all_types.decimal_ptr", + "allTypesSubQuery"."all_types.decimal" AS "all_types.decimal", + "allTypesSubQuery"."all_types.numeric_ptr" AS "all_types.numeric_ptr", + "allTypesSubQuery"."all_types.numeric" AS "all_types.numeric", + "allTypesSubQuery"."all_types.real_ptr" AS "all_types.real_ptr", + "allTypesSubQuery"."all_types.real" AS "all_types.real", + "allTypesSubQuery"."all_types.double_precision_ptr" AS "all_types.double_precision_ptr", + "allTypesSubQuery"."all_types.double_precision" AS "all_types.double_precision", + "allTypesSubQuery"."all_types.smallserial" AS "all_types.smallserial", + "allTypesSubQuery"."all_types.serial" AS "all_types.serial", + "allTypesSubQuery"."all_types.bigserial" AS "all_types.bigserial", + "allTypesSubQuery"."all_types.var_char_ptr" AS "all_types.var_char_ptr", + "allTypesSubQuery"."all_types.var_char" AS "all_types.var_char", + "allTypesSubQuery"."all_types.char_ptr" AS "all_types.char_ptr", + "allTypesSubQuery"."all_types.char" AS "all_types.char", + "allTypesSubQuery"."all_types.text_ptr" AS "all_types.text_ptr", + "allTypesSubQuery"."all_types.text" AS "all_types.text", + "allTypesSubQuery"."all_types.bytea_ptr" AS "all_types.bytea_ptr", + "allTypesSubQuery"."all_types.bytea" AS "all_types.bytea", + "allTypesSubQuery"."all_types.timestampz_ptr" AS "all_types.timestampz_ptr", + "allTypesSubQuery"."all_types.timestampz" AS "all_types.timestampz", + "allTypesSubQuery"."all_types.timestamp_ptr" AS "all_types.timestamp_ptr", + "allTypesSubQuery"."all_types.timestamp" AS "all_types.timestamp", + "allTypesSubQuery"."all_types.date_ptr" AS "all_types.date_ptr", + "allTypesSubQuery"."all_types.date" AS "all_types.date", + "allTypesSubQuery"."all_types.timez_ptr" AS "all_types.timez_ptr", + "allTypesSubQuery"."all_types.timez" AS "all_types.timez", + "allTypesSubQuery"."all_types.time_ptr" AS "all_types.time_ptr", + "allTypesSubQuery"."all_types.time" AS "all_types.time", + "allTypesSubQuery"."all_types.interval_ptr" AS "all_types.interval_ptr", + "allTypesSubQuery"."all_types.interval" AS "all_types.interval", + "allTypesSubQuery"."all_types.boolean_ptr" AS "all_types.boolean_ptr", + "allTypesSubQuery"."all_types.boolean" AS "all_types.boolean", + "allTypesSubQuery"."all_types.point_ptr" AS "all_types.point_ptr", + "allTypesSubQuery"."all_types.bit_ptr" AS "all_types.bit_ptr", + "allTypesSubQuery"."all_types.bit" AS "all_types.bit", + "allTypesSubQuery"."all_types.bit_varying_ptr" AS "all_types.bit_varying_ptr", + "allTypesSubQuery"."all_types.bit_varying" AS "all_types.bit_varying", + "allTypesSubQuery"."all_types.tsvector_ptr" AS "all_types.tsvector_ptr", + "allTypesSubQuery"."all_types.tsvector" AS "all_types.tsvector", + "allTypesSubQuery"."all_types.uuid_ptr" AS "all_types.uuid_ptr", + "allTypesSubQuery"."all_types.uuid" AS "all_types.uuid", + "allTypesSubQuery"."all_types.xml_ptr" AS "all_types.xml_ptr", + "allTypesSubQuery"."all_types.xml" AS "all_types.xml", + "allTypesSubQuery"."all_types.json_ptr" AS "all_types.json_ptr", + "allTypesSubQuery"."all_types.json" AS "all_types.json", + "allTypesSubQuery"."all_types.jsonb_ptr" AS "all_types.jsonb_ptr", + "allTypesSubQuery"."all_types.jsonb" AS "all_types.jsonb", + "allTypesSubQuery"."all_types.integer_array_ptr" AS "all_types.integer_array_ptr", + "allTypesSubQuery"."all_types.integer_array" AS "all_types.integer_array", + "allTypesSubQuery"."all_types.text_array_ptr" AS "all_types.text_array_ptr", + "allTypesSubQuery"."all_types.text_array" AS "all_types.text_array", + "allTypesSubQuery"."all_types.jsonb_array" AS "all_types.jsonb_array", + "allTypesSubQuery"."all_types.text_multi_dim_array_ptr" AS "all_types.text_multi_dim_array_ptr", + "allTypesSubQuery"."all_types.text_multi_dim_array" AS "all_types.text_multi_dim_array" +FROM ( + SELECT all_types.small_int_ptr AS "all_types.small_int_ptr", + all_types.small_int AS "all_types.small_int", + all_types.integer_ptr AS "all_types.integer_ptr", + all_types.integer AS "all_types.integer", + all_types.big_int_ptr AS "all_types.big_int_ptr", + all_types.big_int AS "all_types.big_int", + all_types.decimal_ptr AS "all_types.decimal_ptr", + all_types.decimal AS "all_types.decimal", + all_types.numeric_ptr AS "all_types.numeric_ptr", + all_types.numeric AS "all_types.numeric", + all_types.real_ptr AS "all_types.real_ptr", + all_types.real AS "all_types.real", + all_types.double_precision_ptr AS "all_types.double_precision_ptr", + all_types.double_precision AS "all_types.double_precision", + all_types.smallserial AS "all_types.smallserial", + all_types.serial AS "all_types.serial", + all_types.bigserial AS "all_types.bigserial", + all_types.var_char_ptr AS "all_types.var_char_ptr", + all_types.var_char AS "all_types.var_char", + all_types.char_ptr AS "all_types.char_ptr", + all_types.char AS "all_types.char", + all_types.text_ptr AS "all_types.text_ptr", + all_types.text AS "all_types.text", + all_types.bytea_ptr AS "all_types.bytea_ptr", + all_types.bytea AS "all_types.bytea", + all_types.timestampz_ptr AS "all_types.timestampz_ptr", + all_types.timestampz AS "all_types.timestampz", + all_types.timestamp_ptr AS "all_types.timestamp_ptr", + all_types.timestamp AS "all_types.timestamp", + all_types.date_ptr AS "all_types.date_ptr", + all_types.date AS "all_types.date", + all_types.timez_ptr AS "all_types.timez_ptr", + all_types.timez AS "all_types.timez", + all_types.time_ptr AS "all_types.time_ptr", + all_types.time AS "all_types.time", + all_types.interval_ptr AS "all_types.interval_ptr", + all_types.interval AS "all_types.interval", + all_types.boolean_ptr AS "all_types.boolean_ptr", + all_types.boolean AS "all_types.boolean", + all_types.point_ptr AS "all_types.point_ptr", + all_types.bit_ptr AS "all_types.bit_ptr", + all_types.bit AS "all_types.bit", + all_types.bit_varying_ptr AS "all_types.bit_varying_ptr", + all_types.bit_varying AS "all_types.bit_varying", + all_types.tsvector_ptr AS "all_types.tsvector_ptr", + all_types.tsvector AS "all_types.tsvector", + all_types.uuid_ptr AS "all_types.uuid_ptr", + all_types.uuid AS "all_types.uuid", + all_types.xml_ptr AS "all_types.xml_ptr", + all_types.xml AS "all_types.xml", + all_types.json_ptr AS "all_types.json_ptr", + all_types.json AS "all_types.json", + all_types.jsonb_ptr AS "all_types.jsonb_ptr", + all_types.jsonb AS "all_types.jsonb", + all_types.integer_array_ptr AS "all_types.integer_array_ptr", + all_types.integer_array AS "all_types.integer_array", + all_types.text_array_ptr AS "all_types.text_array_ptr", + all_types.text_array AS "all_types.text_array", + all_types.jsonb_array AS "all_types.jsonb_array", + all_types.text_multi_dim_array_ptr AS "all_types.text_multi_dim_array_ptr", + all_types.text_multi_dim_array AS "all_types.text_multi_dim_array" + FROM test_sample.all_types + ) AS "allTypesSubQuery" +LIMIT 2; +`) + + dest := []model.AllTypes{} + err := mainQuery.Query(db, &dest) + + assert.NilError(t, err) + assert.Equal(t, len(dest), 2) +} + func TestExpressionOperators(t *testing.T) { query := AllTypes.SELECT( AllTypes.Integer.IS_NULL().AS("result.is_null"), @@ -671,7 +817,20 @@ func TestInterval(t *testing.T) { INTERVALd(1*time.Hour), INTERVALd(24*time.Hour), INTERVALd(24*time.Hour+2*time.Hour+3*time.Minute+4*time.Second+5*time.Microsecond), - ) + + AllTypes.Interval.EQ(INTERVAL(2, HOUR, 20, MINUTE)).EQ(Bool(true)), + AllTypes.IntervalPtr.NOT_EQ(INTERVAL(2, HOUR, 20, MINUTE)).EQ(Bool(false)), + AllTypes.Interval.IS_DISTINCT_FROM(INTERVAL(2, HOUR, 20, MINUTE)).EQ(AllTypes.Boolean), + AllTypes.IntervalPtr.IS_NOT_DISTINCT_FROM(INTERVALd(10*time.Microsecond)).EQ(AllTypes.Boolean), + AllTypes.Interval.LT(AllTypes.IntervalPtr).EQ(AllTypes.BooleanPtr), + AllTypes.Interval.LT_EQ(AllTypes.IntervalPtr).EQ(AllTypes.BooleanPtr), + AllTypes.Interval.GT(AllTypes.IntervalPtr).EQ(AllTypes.BooleanPtr), + AllTypes.Interval.GT_EQ(AllTypes.IntervalPtr).EQ(AllTypes.BooleanPtr), + AllTypes.Interval.ADD(AllTypes.IntervalPtr).EQ(INTERVALd(17*time.Second)), + AllTypes.Interval.SUB(AllTypes.IntervalPtr).EQ(INTERVAL(100, MICROSECOND)), + AllTypes.IntervalPtr.MUL(Int(11)).EQ(AllTypes.Interval), + AllTypes.IntervalPtr.DIV(Float(22.222)).EQ(AllTypes.IntervalPtr), + ).FROM(AllTypes) //fmt.Println(stmt.DebugSql()) diff --git a/tests/postgres/generator_test.go b/tests/postgres/generator_test.go index b8a9ba4..5100801 100644 --- a/tests/postgres/generator_test.go +++ b/tests/postgres/generator_test.go @@ -15,7 +15,6 @@ import ( ) func TestGeneratedModel(t *testing.T) { - actor := model.Actor{} assert.Equal(t, reflect.TypeOf(actor.ActorID).String(), "int32") @@ -275,3 +274,345 @@ func newActorInfoTable() *ActorInfoTable { } } ` + +func TestGeneratedAllTypesSQLBuilderFiles(t *testing.T) { + enumDir := testRoot + ".gentestdata/jetdb/test_sample/enum/" + modelDir := testRoot + ".gentestdata/jetdb/test_sample/model/" + tableDir := testRoot + ".gentestdata/jetdb/test_sample/table/" + + enumFiles, err := ioutil.ReadDir(enumDir) + assert.NilError(t, err) + + testutils.AssertFileNamesEqual(t, enumFiles, "mood.go") + testutils.AssertFileContent(t, enumDir+"mood.go", "\npackage enum", moodEnumContent) + + modelFiles, err := ioutil.ReadDir(modelDir) + assert.NilError(t, err) + + testutils.AssertFileNamesEqual(t, modelFiles, "all_types.go", "all_types_view.go", "employee.go", "link.go", + "mood.go", "person.go", "person_phone.go", "weird_names_table.go") + + testutils.AssertFileContent(t, modelDir+"all_types.go", "\npackage model", allTypesModelContent) + + tableFiles, err := ioutil.ReadDir(tableDir) + assert.NilError(t, err) + + testutils.AssertFileNamesEqual(t, tableFiles, "all_types.go", "employee.go", "link.go", + "person.go", "person_phone.go", "weird_names_table.go") + + testutils.AssertFileContent(t, tableDir+"all_types.go", "\npackage table", allTypesTableContent) +} + +var moodEnumContent = ` +package enum + +import "github.com/go-jet/jet/postgres" + +var Mood = &struct { + Sad postgres.StringExpression + Ok postgres.StringExpression + Happy postgres.StringExpression +}{ + Sad: postgres.NewEnumValue("sad"), + Ok: postgres.NewEnumValue("ok"), + Happy: postgres.NewEnumValue("happy"), +} +` + +var allTypesModelContent = ` +package model + +import ( + "github.com/google/uuid" + "time" +) + +type AllTypes struct { + SmallIntPtr *int16 + SmallInt int16 + IntegerPtr *int32 + Integer int32 + BigIntPtr *int64 + BigInt int64 + DecimalPtr *float64 + Decimal float64 + NumericPtr *float64 + Numeric float64 + RealPtr *float32 + Real float32 + DoublePrecisionPtr *float64 + DoublePrecision float64 + Smallserial int16 + Serial int32 + Bigserial int64 + VarCharPtr *string + VarChar string + CharPtr *string + Char string + TextPtr *string + Text string + ByteaPtr *[]byte + Bytea []byte + TimestampzPtr *time.Time + Timestampz time.Time + TimestampPtr *time.Time + Timestamp time.Time + DatePtr *time.Time + Date time.Time + TimezPtr *time.Time + Timez time.Time + TimePtr *time.Time + Time time.Time + IntervalPtr *string + Interval string + BooleanPtr *bool + Boolean bool + PointPtr *string + BitPtr *string + Bit string + BitVaryingPtr *string + BitVarying string + TsvectorPtr *string + Tsvector string + UUIDPtr *uuid.UUID + UUID uuid.UUID + XMLPtr *string + XML string + JSONPtr *string + JSON string + JsonbPtr *string + Jsonb string + IntegerArrayPtr *string + IntegerArray string + TextArrayPtr *string + TextArray string + JsonbArray string + TextMultiDimArrayPtr *string + TextMultiDimArray string +} +` + +var allTypesTableContent = ` +package table + +import ( + "github.com/go-jet/jet/postgres" +) + +var AllTypes = newAllTypesTable() + +type AllTypesTable struct { + postgres.Table + + //Columns + SmallIntPtr postgres.ColumnInteger + SmallInt postgres.ColumnInteger + IntegerPtr postgres.ColumnInteger + Integer postgres.ColumnInteger + BigIntPtr postgres.ColumnInteger + BigInt postgres.ColumnInteger + DecimalPtr postgres.ColumnFloat + Decimal postgres.ColumnFloat + NumericPtr postgres.ColumnFloat + Numeric postgres.ColumnFloat + RealPtr postgres.ColumnFloat + Real postgres.ColumnFloat + DoublePrecisionPtr postgres.ColumnFloat + DoublePrecision postgres.ColumnFloat + Smallserial postgres.ColumnInteger + Serial postgres.ColumnInteger + Bigserial postgres.ColumnInteger + VarCharPtr postgres.ColumnString + VarChar postgres.ColumnString + CharPtr postgres.ColumnString + Char postgres.ColumnString + TextPtr postgres.ColumnString + Text postgres.ColumnString + ByteaPtr postgres.ColumnString + Bytea postgres.ColumnString + TimestampzPtr postgres.ColumnTimestampz + Timestampz postgres.ColumnTimestampz + TimestampPtr postgres.ColumnTimestamp + Timestamp postgres.ColumnTimestamp + DatePtr postgres.ColumnDate + Date postgres.ColumnDate + TimezPtr postgres.ColumnTimez + Timez postgres.ColumnTimez + TimePtr postgres.ColumnTime + Time postgres.ColumnTime + IntervalPtr postgres.ColumnInterval + Interval postgres.ColumnInterval + BooleanPtr postgres.ColumnBool + Boolean postgres.ColumnBool + PointPtr postgres.ColumnString + BitPtr postgres.ColumnString + Bit postgres.ColumnString + BitVaryingPtr postgres.ColumnString + BitVarying postgres.ColumnString + TsvectorPtr postgres.ColumnString + Tsvector postgres.ColumnString + UUIDPtr postgres.ColumnString + UUID postgres.ColumnString + XMLPtr postgres.ColumnString + XML postgres.ColumnString + JSONPtr postgres.ColumnString + JSON postgres.ColumnString + JsonbPtr postgres.ColumnString + Jsonb postgres.ColumnString + IntegerArrayPtr postgres.ColumnString + IntegerArray postgres.ColumnString + TextArrayPtr postgres.ColumnString + TextArray postgres.ColumnString + JsonbArray postgres.ColumnString + TextMultiDimArrayPtr postgres.ColumnString + TextMultiDimArray postgres.ColumnString + + AllColumns postgres.ColumnList + MutableColumns postgres.ColumnList +} + +// creates new AllTypesTable with assigned alias +func (a *AllTypesTable) AS(alias string) *AllTypesTable { + aliasTable := newAllTypesTable() + + aliasTable.Table.AS(alias) + + return aliasTable +} + +func newAllTypesTable() *AllTypesTable { + var ( + SmallIntPtrColumn = postgres.IntegerColumn("small_int_ptr") + SmallIntColumn = postgres.IntegerColumn("small_int") + IntegerPtrColumn = postgres.IntegerColumn("integer_ptr") + IntegerColumn = postgres.IntegerColumn("integer") + BigIntPtrColumn = postgres.IntegerColumn("big_int_ptr") + BigIntColumn = postgres.IntegerColumn("big_int") + DecimalPtrColumn = postgres.FloatColumn("decimal_ptr") + DecimalColumn = postgres.FloatColumn("decimal") + NumericPtrColumn = postgres.FloatColumn("numeric_ptr") + NumericColumn = postgres.FloatColumn("numeric") + RealPtrColumn = postgres.FloatColumn("real_ptr") + RealColumn = postgres.FloatColumn("real") + DoublePrecisionPtrColumn = postgres.FloatColumn("double_precision_ptr") + DoublePrecisionColumn = postgres.FloatColumn("double_precision") + SmallserialColumn = postgres.IntegerColumn("smallserial") + SerialColumn = postgres.IntegerColumn("serial") + BigserialColumn = postgres.IntegerColumn("bigserial") + VarCharPtrColumn = postgres.StringColumn("var_char_ptr") + VarCharColumn = postgres.StringColumn("var_char") + CharPtrColumn = postgres.StringColumn("char_ptr") + CharColumn = postgres.StringColumn("char") + TextPtrColumn = postgres.StringColumn("text_ptr") + TextColumn = postgres.StringColumn("text") + ByteaPtrColumn = postgres.StringColumn("bytea_ptr") + ByteaColumn = postgres.StringColumn("bytea") + TimestampzPtrColumn = postgres.TimestampzColumn("timestampz_ptr") + TimestampzColumn = postgres.TimestampzColumn("timestampz") + TimestampPtrColumn = postgres.TimestampColumn("timestamp_ptr") + TimestampColumn = postgres.TimestampColumn("timestamp") + DatePtrColumn = postgres.DateColumn("date_ptr") + DateColumn = postgres.DateColumn("date") + TimezPtrColumn = postgres.TimezColumn("timez_ptr") + TimezColumn = postgres.TimezColumn("timez") + TimePtrColumn = postgres.TimeColumn("time_ptr") + TimeColumn = postgres.TimeColumn("time") + IntervalPtrColumn = postgres.IntervalColumn("interval_ptr") + IntervalColumn = postgres.IntervalColumn("interval") + BooleanPtrColumn = postgres.BoolColumn("boolean_ptr") + BooleanColumn = postgres.BoolColumn("boolean") + PointPtrColumn = postgres.StringColumn("point_ptr") + BitPtrColumn = postgres.StringColumn("bit_ptr") + BitColumn = postgres.StringColumn("bit") + BitVaryingPtrColumn = postgres.StringColumn("bit_varying_ptr") + BitVaryingColumn = postgres.StringColumn("bit_varying") + TsvectorPtrColumn = postgres.StringColumn("tsvector_ptr") + TsvectorColumn = postgres.StringColumn("tsvector") + UUIDPtrColumn = postgres.StringColumn("uuid_ptr") + UUIDColumn = postgres.StringColumn("uuid") + XMLPtrColumn = postgres.StringColumn("xml_ptr") + XMLColumn = postgres.StringColumn("xml") + JSONPtrColumn = postgres.StringColumn("json_ptr") + JSONColumn = postgres.StringColumn("json") + JsonbPtrColumn = postgres.StringColumn("jsonb_ptr") + JsonbColumn = postgres.StringColumn("jsonb") + IntegerArrayPtrColumn = postgres.StringColumn("integer_array_ptr") + IntegerArrayColumn = postgres.StringColumn("integer_array") + TextArrayPtrColumn = postgres.StringColumn("text_array_ptr") + TextArrayColumn = postgres.StringColumn("text_array") + JsonbArrayColumn = postgres.StringColumn("jsonb_array") + TextMultiDimArrayPtrColumn = postgres.StringColumn("text_multi_dim_array_ptr") + TextMultiDimArrayColumn = postgres.StringColumn("text_multi_dim_array") + ) + + return &AllTypesTable{ + Table: postgres.NewTable("test_sample", "all_types", SmallIntPtrColumn, SmallIntColumn, IntegerPtrColumn, IntegerColumn, BigIntPtrColumn, BigIntColumn, DecimalPtrColumn, DecimalColumn, NumericPtrColumn, NumericColumn, RealPtrColumn, RealColumn, DoublePrecisionPtrColumn, DoublePrecisionColumn, SmallserialColumn, SerialColumn, BigserialColumn, VarCharPtrColumn, VarCharColumn, CharPtrColumn, CharColumn, TextPtrColumn, TextColumn, ByteaPtrColumn, ByteaColumn, TimestampzPtrColumn, TimestampzColumn, TimestampPtrColumn, TimestampColumn, DatePtrColumn, DateColumn, TimezPtrColumn, TimezColumn, TimePtrColumn, TimeColumn, IntervalPtrColumn, IntervalColumn, BooleanPtrColumn, BooleanColumn, PointPtrColumn, BitPtrColumn, BitColumn, BitVaryingPtrColumn, BitVaryingColumn, TsvectorPtrColumn, TsvectorColumn, UUIDPtrColumn, UUIDColumn, XMLPtrColumn, XMLColumn, JSONPtrColumn, JSONColumn, JsonbPtrColumn, JsonbColumn, IntegerArrayPtrColumn, IntegerArrayColumn, TextArrayPtrColumn, TextArrayColumn, JsonbArrayColumn, TextMultiDimArrayPtrColumn, TextMultiDimArrayColumn), + + //Columns + SmallIntPtr: SmallIntPtrColumn, + SmallInt: SmallIntColumn, + IntegerPtr: IntegerPtrColumn, + Integer: IntegerColumn, + BigIntPtr: BigIntPtrColumn, + BigInt: BigIntColumn, + DecimalPtr: DecimalPtrColumn, + Decimal: DecimalColumn, + NumericPtr: NumericPtrColumn, + Numeric: NumericColumn, + RealPtr: RealPtrColumn, + Real: RealColumn, + DoublePrecisionPtr: DoublePrecisionPtrColumn, + DoublePrecision: DoublePrecisionColumn, + Smallserial: SmallserialColumn, + Serial: SerialColumn, + Bigserial: BigserialColumn, + VarCharPtr: VarCharPtrColumn, + VarChar: VarCharColumn, + CharPtr: CharPtrColumn, + Char: CharColumn, + TextPtr: TextPtrColumn, + Text: TextColumn, + ByteaPtr: ByteaPtrColumn, + Bytea: ByteaColumn, + TimestampzPtr: TimestampzPtrColumn, + Timestampz: TimestampzColumn, + TimestampPtr: TimestampPtrColumn, + Timestamp: TimestampColumn, + DatePtr: DatePtrColumn, + Date: DateColumn, + TimezPtr: TimezPtrColumn, + Timez: TimezColumn, + TimePtr: TimePtrColumn, + Time: TimeColumn, + IntervalPtr: IntervalPtrColumn, + Interval: IntervalColumn, + BooleanPtr: BooleanPtrColumn, + Boolean: BooleanColumn, + PointPtr: PointPtrColumn, + BitPtr: BitPtrColumn, + Bit: BitColumn, + BitVaryingPtr: BitVaryingPtrColumn, + BitVarying: BitVaryingColumn, + TsvectorPtr: TsvectorPtrColumn, + Tsvector: TsvectorColumn, + UUIDPtr: UUIDPtrColumn, + UUID: UUIDColumn, + XMLPtr: XMLPtrColumn, + XML: XMLColumn, + JSONPtr: JSONPtrColumn, + JSON: JSONColumn, + JsonbPtr: JsonbPtrColumn, + Jsonb: JsonbColumn, + IntegerArrayPtr: IntegerArrayPtrColumn, + IntegerArray: IntegerArrayColumn, + TextArrayPtr: TextArrayPtrColumn, + TextArray: TextArrayColumn, + JsonbArray: JsonbArrayColumn, + TextMultiDimArrayPtr: TextMultiDimArrayPtrColumn, + TextMultiDimArray: TextMultiDimArrayColumn, + + AllColumns: postgres.ColumnList{SmallIntPtrColumn, SmallIntColumn, IntegerPtrColumn, IntegerColumn, BigIntPtrColumn, BigIntColumn, DecimalPtrColumn, DecimalColumn, NumericPtrColumn, NumericColumn, RealPtrColumn, RealColumn, DoublePrecisionPtrColumn, DoublePrecisionColumn, SmallserialColumn, SerialColumn, BigserialColumn, VarCharPtrColumn, VarCharColumn, CharPtrColumn, CharColumn, TextPtrColumn, TextColumn, ByteaPtrColumn, ByteaColumn, TimestampzPtrColumn, TimestampzColumn, TimestampPtrColumn, TimestampColumn, DatePtrColumn, DateColumn, TimezPtrColumn, TimezColumn, TimePtrColumn, TimeColumn, IntervalPtrColumn, IntervalColumn, BooleanPtrColumn, BooleanColumn, PointPtrColumn, BitPtrColumn, BitColumn, BitVaryingPtrColumn, BitVaryingColumn, TsvectorPtrColumn, TsvectorColumn, UUIDPtrColumn, UUIDColumn, XMLPtrColumn, XMLColumn, JSONPtrColumn, JSONColumn, JsonbPtrColumn, JsonbColumn, IntegerArrayPtrColumn, IntegerArrayColumn, TextArrayPtrColumn, TextArrayColumn, JsonbArrayColumn, TextMultiDimArrayPtrColumn, TextMultiDimArrayColumn}, + MutableColumns: postgres.ColumnList{SmallIntPtrColumn, SmallIntColumn, IntegerPtrColumn, IntegerColumn, BigIntPtrColumn, BigIntColumn, DecimalPtrColumn, DecimalColumn, NumericPtrColumn, NumericColumn, RealPtrColumn, RealColumn, DoublePrecisionPtrColumn, DoublePrecisionColumn, SmallserialColumn, SerialColumn, BigserialColumn, VarCharPtrColumn, VarCharColumn, CharPtrColumn, CharColumn, TextPtrColumn, TextColumn, ByteaPtrColumn, ByteaColumn, TimestampzPtrColumn, TimestampzColumn, TimestampPtrColumn, TimestampColumn, DatePtrColumn, DateColumn, TimezPtrColumn, TimezColumn, TimePtrColumn, TimeColumn, IntervalPtrColumn, IntervalColumn, BooleanPtrColumn, BooleanColumn, PointPtrColumn, BitPtrColumn, BitColumn, BitVaryingPtrColumn, BitVaryingColumn, TsvectorPtrColumn, TsvectorColumn, UUIDPtrColumn, UUIDColumn, XMLPtrColumn, XMLColumn, JSONPtrColumn, JSONColumn, JsonbPtrColumn, JsonbColumn, IntegerArrayPtrColumn, IntegerArrayColumn, TextArrayPtrColumn, TextArrayColumn, JsonbArrayColumn, TextMultiDimArrayPtrColumn, TextMultiDimArrayColumn}, + } +} +` diff --git a/tests/postgres/main_test.go b/tests/postgres/main_test.go index dda4fe7..49ab868 100644 --- a/tests/postgres/main_test.go +++ b/tests/postgres/main_test.go @@ -6,14 +6,19 @@ import ( _ "github.com/lib/pq" "github.com/pkg/profile" "os" + "os/exec" + "strings" "testing" ) var db *sql.DB +var testRoot string func TestMain(m *testing.M) { defer profile.Start().Stop() + setTestRoot() + var err error db, err = sql.Open("postgres", dbconfig.PostgresConnectString) if err != nil { @@ -25,3 +30,13 @@ func TestMain(m *testing.M) { os.Exit(ret) } + +func setTestRoot() { + cmd := exec.Command("git", "rev-parse", "--show-toplevel") + byteArr, err := cmd.Output() + if err != nil { + panic(err) + } + + testRoot = strings.TrimSpace(string(byteArr)) + "/tests/" +}