From 7f5ba98819a55f3d18eff4da6ef53ab38c8a7ea3 Mon Sep 17 00:00:00 2001 From: zer0sub Date: Thu, 30 May 2019 14:49:36 +0200 Subject: [PATCH] Time, Timez, Timestamp, Timestampz, Date column types added. --- generator/postgres-metadata/column_info.go | 10 +- sqlbuilder/bool_expresion.go | 10 ++ sqlbuilder/clause.go | 4 +- sqlbuilder/column_types.go | 84 ++++++++++- sqlbuilder/date_expression.go | 51 +++++++ sqlbuilder/expression.go | 10 -- sqlbuilder/expression_test.go | 4 +- sqlbuilder/literal_expression.go | 73 +++++++++- sqlbuilder/numeric_expression.go | 39 +++-- sqlbuilder/operators.go | 8 ++ sqlbuilder/string_expression.go | 11 ++ sqlbuilder/string_expression_test.go | 12 +- sqlbuilder/test_utils.go | 2 +- sqlbuilder/time_expression.go | 15 +- sqlbuilder/time_expression_test.go | 13 +- sqlbuilder/timestamp_expression.go | 51 +++++++ sqlbuilder/timestampz_expression.go | 51 +++++++ sqlbuilder/timez_expression.go | 69 +++++++++ tests/select_test.go | 6 +- tests/types_test.go | 157 ++++++++++++++++++++- 20 files changed, 620 insertions(+), 60 deletions(-) create mode 100644 sqlbuilder/date_expression.go create mode 100644 sqlbuilder/timestamp_expression.go create mode 100644 sqlbuilder/timestampz_expression.go create mode 100644 sqlbuilder/timez_expression.go diff --git a/generator/postgres-metadata/column_info.go b/generator/postgres-metadata/column_info.go index 7e76b3b..88d2587 100644 --- a/generator/postgres-metadata/column_info.go +++ b/generator/postgres-metadata/column_info.go @@ -20,8 +20,16 @@ func (c ColumnInfo) SqlBuilderColumnType() string { return "BoolColumn" case "smallint", "integer", "bigint": return "IntegerColumn" - case "date", "timestamp without time zone", "timestamp with time zone", "time with time zone", "time without time zone": + case "date": + return "DateColumn" + case "timestamp without time zone": + return "TimestampColumn" + case "timestamp with time zone": + return "TimestampzColumn" + case "time without time zone": return "TimeColumn" + case "time with time zone": + return "TimezColumn" case "USER-DEFINED", "text", "character", "character varying", "bytea", "uuid", "tsvector", "bit", "bit varying", "money", "json", "jsonb", "xml", "point", "interval", "line", "ARRAY": return "StringColumn" diff --git a/sqlbuilder/bool_expresion.go b/sqlbuilder/bool_expresion.go index 4f623f9..ef50959 100644 --- a/sqlbuilder/bool_expresion.go +++ b/sqlbuilder/bool_expresion.go @@ -5,6 +5,8 @@ type boolExpression interface { EQ(expression boolExpression) boolExpression NOT_EQ(expression boolExpression) boolExpression + IS_DISTINCT_FROM(rhs boolExpression) boolExpression + IS_NOT_DISTINCT_FROM(rhs boolExpression) boolExpression IS_TRUE() boolExpression IS_NOT_TRUE() boolExpression @@ -29,6 +31,14 @@ func (b *boolInterfaceImpl) NOT_EQ(expression boolExpression) boolExpression { return NOT_EQ(b.parent, expression) } +func (b *boolInterfaceImpl) IS_DISTINCT_FROM(rhs boolExpression) boolExpression { + return IS_DISTINCT_FROM(b.parent, rhs) +} + +func (b *boolInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs boolExpression) boolExpression { + return IS_NOT_DISTINCT_FROM(b.parent, rhs) +} + func (b *boolInterfaceImpl) AND(expression boolExpression) boolExpression { return And(b.parent, expression) } diff --git a/sqlbuilder/clause.go b/sqlbuilder/clause.go index a8d73e5..ebd0856 100644 --- a/sqlbuilder/clause.go +++ b/sqlbuilder/clause.go @@ -192,10 +192,8 @@ func ArgToString(value interface{}) string { case string: return `'` + bindVal + `'` case []byte: - return string(bindVal) + return `'` + string(bindVal) + `'` //TODO: implement - //case time.Time: - // return bindVal.String()) default: return "[Unknown type]" } diff --git a/sqlbuilder/column_types.go b/sqlbuilder/column_types.go index 14442d9..e484803 100644 --- a/sqlbuilder/column_types.go +++ b/sqlbuilder/column_types.go @@ -85,13 +85,89 @@ type TimeColumn struct { // Representation of any integer column // This function will panic if name is not valid func NewTimeColumn(name string, nullable NullableColumn) *TimeColumn { - stringColumn := &TimeColumn{} + timeColumn := &TimeColumn{} - stringColumn.timeInterfaceImpl.parent = stringColumn + timeColumn.timeInterfaceImpl.parent = timeColumn - stringColumn.baseColumn = newBaseColumn(name, nullable, "", stringColumn) + timeColumn.baseColumn = newBaseColumn(name, nullable, "", timeColumn) - return stringColumn + return timeColumn +} + +//------------------------------------------------------// +type TimezColumn struct { + timezInterfaceImpl + + baseColumn +} + +// Representation of any integer column +// This function will panic if name is not valid +func NewTimezColumn(name string, nullable NullableColumn) *TimezColumn { + timezColumn := &TimezColumn{} + + timezColumn.timezInterfaceImpl.parent = timezColumn + + timezColumn.baseColumn = newBaseColumn(name, nullable, "", timezColumn) + + return timezColumn +} + +//------------------------------------------------------// +type TimestampColumn struct { + timestampInterfaceImpl + + baseColumn +} + +// Representation of any integer column +// This function will panic if name is not valid +func NewTimestampColumn(name string, nullable NullableColumn) *TimestampColumn { + timestampColumn := &TimestampColumn{} + + timestampColumn.timestampInterfaceImpl.parent = timestampColumn + + timestampColumn.baseColumn = newBaseColumn(name, nullable, "", timestampColumn) + + return timestampColumn +} + +//------------------------------------------------------// +type TimestampzColumn struct { + timestampzInterfaceImpl + + baseColumn +} + +// Representation of any integer column +// This function will panic if name is not valid +func NewTimestampzColumn(name string, nullable NullableColumn) *TimestampzColumn { + timestampzColumn := &TimestampzColumn{} + + timestampzColumn.timestampzInterfaceImpl.parent = timestampzColumn + + timestampzColumn.baseColumn = newBaseColumn(name, nullable, "", timestampzColumn) + + return timestampzColumn +} + +//------------------------------------------------------// +type DateColumn struct { + dateInterfaceImpl + + baseColumn +} + +// Representation of any integer column +// This function will panic if name is not valid +func NewDateColumn(name string, nullable NullableColumn) *DateColumn { + dateColumn := &DateColumn{} + + dateColumn.dateInterfaceImpl.parent = dateColumn + + dateColumn.baseColumn = newBaseColumn(name, nullable, "", dateColumn) + + return dateColumn } // ------------------------------------------------------// diff --git a/sqlbuilder/date_expression.go b/sqlbuilder/date_expression.go new file mode 100644 index 0000000..0b0dd21 --- /dev/null +++ b/sqlbuilder/date_expression.go @@ -0,0 +1,51 @@ +package sqlbuilder + +type DateExpression interface { + expression + + EQ(rhs DateExpression) boolExpression + NOT_EQ(rhs DateExpression) boolExpression + IS_DISTINCT_FROM(rhs DateExpression) boolExpression + IS_NOT_DISTINCT_FROM(rhs DateExpression) boolExpression + + LT(rhs DateExpression) boolExpression + LT_EQ(rhs DateExpression) boolExpression + GT(rhs DateExpression) boolExpression + GT_EQ(rhs DateExpression) boolExpression +} + +type dateInterfaceImpl struct { + parent DateExpression +} + +func (t *dateInterfaceImpl) EQ(rhs DateExpression) boolExpression { + return EQ(t.parent, rhs) +} + +func (t *dateInterfaceImpl) NOT_EQ(rhs DateExpression) boolExpression { + return NOT_EQ(t.parent, rhs) +} + +func (t *dateInterfaceImpl) IS_DISTINCT_FROM(rhs DateExpression) boolExpression { + return IS_DISTINCT_FROM(t.parent, rhs) +} + +func (t *dateInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs DateExpression) boolExpression { + return IS_NOT_DISTINCT_FROM(t.parent, rhs) +} + +func (t *dateInterfaceImpl) LT(rhs DateExpression) boolExpression { + return LT(t.parent, rhs) +} + +func (t *dateInterfaceImpl) LT_EQ(rhs DateExpression) boolExpression { + return LT_EQ(t.parent, rhs) +} + +func (t *dateInterfaceImpl) GT(rhs DateExpression) boolExpression { + return GT(t.parent, rhs) +} + +func (t *dateInterfaceImpl) GT_EQ(rhs DateExpression) boolExpression { + return GT_EQ(t.parent, rhs) +} diff --git a/sqlbuilder/expression.go b/sqlbuilder/expression.go index db00eb0..a964ab1 100644 --- a/sqlbuilder/expression.go +++ b/sqlbuilder/expression.go @@ -13,8 +13,6 @@ type expression interface { IS_NULL() boolExpression IS_NOT_NULL() boolExpression - IS_DISTINCT_FROM(expression expression) boolExpression - IS_NOT_DISTINCT_FROM(expression expression) boolExpression IN(subQuery selectStatement) boolExpression NOT_IN(subQuery selectStatement) boolExpression @@ -37,14 +35,6 @@ func (e *expressionInterfaceImpl) IS_NOT_NULL() boolExpression { return newPostifxBoolExpression(e.parent, "IS NOT NULL") } -func (e *expressionInterfaceImpl) IS_DISTINCT_FROM(expression expression) boolExpression { - return newBinaryBoolExpression(e.parent, expression, "IS DISTINCT FROM") -} - -func (e *expressionInterfaceImpl) IS_NOT_DISTINCT_FROM(expression expression) boolExpression { - return newBinaryBoolExpression(e.parent, expression, "IS NOT DISTINCT FROM") -} - func (e *expressionInterfaceImpl) IN(subQuery selectStatement) boolExpression { return newBinaryBoolExpression(e.parent, subQuery, "IN") } diff --git a/sqlbuilder/expression_test.go b/sqlbuilder/expression_test.go index 89a0c2a..f479c0e 100644 --- a/sqlbuilder/expression_test.go +++ b/sqlbuilder/expression_test.go @@ -16,11 +16,11 @@ func TestExpressionIS_NOT_NULL(t *testing.T) { } func TestExpressionIS_DISTINCT_FROM(t *testing.T) { - assert.Equal(t, getTestSerialize(t, table2Col3.IS_DISTINCT_FROM(table2StrCol)), "table2.col3 IS DISTINCT FROM table2.col4") + assert.Equal(t, getTestSerialize(t, table2Col3.IS_DISTINCT_FROM(table2Col4)), "table2.col3 IS DISTINCT FROM table2.col4") assert.Equal(t, getTestSerialize(t, table2Col3.ADD(table2Col3).IS_DISTINCT_FROM(Int(23))), "(table2.col3 + table2.col3 IS DISTINCT FROM $1)") } func TestExpressionIS_NOT_DISTINCT_FROM(t *testing.T) { - assert.Equal(t, getTestSerialize(t, table2Col3.IS_NOT_DISTINCT_FROM(table2StrCol)), "table2.col3 IS NOT DISTINCT FROM table2.col4") + assert.Equal(t, getTestSerialize(t, table2Col3.IS_NOT_DISTINCT_FROM(table2Col4)), "table2.col3 IS NOT DISTINCT FROM table2.col4") assert.Equal(t, getTestSerialize(t, table2Col3.ADD(table2Col3).IS_NOT_DISTINCT_FROM(Int(23))), "(table2.col3 + table2.col3 IS NOT DISTINCT FROM $1)") } diff --git a/sqlbuilder/literal_expression.go b/sqlbuilder/literal_expression.go index daf5e30..7ee975d 100644 --- a/sqlbuilder/literal_expression.go +++ b/sqlbuilder/literal_expression.go @@ -1,6 +1,6 @@ package sqlbuilder -import "time" +import "fmt" // Representation of an escaped literal type literalExpression struct { @@ -88,11 +88,78 @@ type timeLiteral struct { literalExpression } -func Time(value time.Time) timeExpression { +func Time(hour, minute, second, milliseconds int) timeExpression { timeLiteral := timeLiteral{} - timeLiteral.literalExpression = *Literal(value) + timeStr := fmt.Sprintf("%02d:%02d:%02d.%03d", hour, minute, second, milliseconds) + timeLiteral.literalExpression = *Literal(timeStr) timeLiteral.timeInterfaceImpl.parent = &timeLiteral return &timeLiteral } + +//---------------------------------------------------// +type timezLiteral struct { + timezInterfaceImpl + literalExpression +} + +func Timez(hour, minute, second, milliseconds, timezone int) timezExpression { + timezLiteral := timezLiteral{} + timeStr := fmt.Sprintf("%02d:%02d:%02d.%03d %+03d", hour, minute, second, milliseconds, timezone) + timezLiteral.literalExpression = *Literal(timeStr) + + timezLiteral.timezInterfaceImpl.parent = &timezLiteral + + return &timezLiteral +} + +//---------------------------------------------------// +type timestampLiteral struct { + timestampInterfaceImpl + literalExpression +} + +func Timestamp(year, month, day, hour, minute, second, milliseconds int) TimestampExpression { + timestampLiteral := timestampLiteral{} + timeStr := fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d.%03d", year, month, day, hour, minute, second, milliseconds) + timestampLiteral.literalExpression = *Literal(timeStr) + + timestampLiteral.timestampInterfaceImpl.parent = ×tampLiteral + + return ×tampLiteral +} + +//---------------------------------------------------// +type timestampzLiteral struct { + timestampzInterfaceImpl + literalExpression +} + +func Timestampz(year, month, day, hour, minute, second, milliseconds, timezone int) TimestampzExpression { + timestampzLiteral := timestampzLiteral{} + timeStr := fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d.%03d %+04d", + year, month, day, hour, minute, second, milliseconds, timezone) + + timestampzLiteral.literalExpression = *Literal(timeStr) + + timestampzLiteral.timestampzInterfaceImpl.parent = ×tampzLiteral + + return ×tampzLiteral +} + +//---------------------------------------------------// +type dateLiteral struct { + dateInterfaceImpl + literalExpression +} + +func Date(year, month, day int) DateExpression { + dateLiteral := dateLiteral{} + + timeStr := fmt.Sprintf("%04d-%02d-%02d", year, month, day) + dateLiteral.literalExpression = *Literal(timeStr) + dateLiteral.dateInterfaceImpl.parent = &dateLiteral + + return &dateLiteral +} diff --git a/sqlbuilder/numeric_expression.go b/sqlbuilder/numeric_expression.go index c4308a1..6d1b69d 100644 --- a/sqlbuilder/numeric_expression.go +++ b/sqlbuilder/numeric_expression.go @@ -5,37 +5,48 @@ import "errors" type numericExpression interface { expression - EQ(expression numericExpression) boolExpression - NOT_EQ(expression numericExpression) boolExpression + EQ(rhs numericExpression) boolExpression + NOT_EQ(rhs numericExpression) boolExpression + IS_DISTINCT_FROM(rhs numericExpression) boolExpression + IS_NOT_DISTINCT_FROM(rhs numericExpression) boolExpression + LT(rhs numericExpression) boolExpression LT_EQ(rhs numericExpression) boolExpression GT(rhs numericExpression) boolExpression GT_EQ(rhs numericExpression) boolExpression - ADD(expression numericExpression) numericExpression - SUB(expression numericExpression) numericExpression - MUL(expression numericExpression) numericExpression - DIV(expression numericExpression) numericExpression + ADD(rhs numericExpression) numericExpression + SUB(rhs numericExpression) numericExpression + MUL(rhs numericExpression) numericExpression + DIV(rhs numericExpression) numericExpression } type numericInterfaceImpl struct { parent numericExpression } -func (n *numericInterfaceImpl) EQ(expression numericExpression) boolExpression { - return EQ(n.parent, expression) +func (n *numericInterfaceImpl) EQ(rhs numericExpression) boolExpression { + return EQ(n.parent, rhs) } -func (n *numericInterfaceImpl) NOT_EQ(expression numericExpression) boolExpression { - return NOT_EQ(n.parent, expression) +func (n *numericInterfaceImpl) NOT_EQ(rhs numericExpression) boolExpression { + return NOT_EQ(n.parent, rhs) } -func (n *numericInterfaceImpl) GT(expression numericExpression) boolExpression { - return GT(n.parent, expression) +func (n *numericInterfaceImpl) IS_DISTINCT_FROM(rhs numericExpression) boolExpression { + return IS_DISTINCT_FROM(n.parent, rhs) } -func (n *numericInterfaceImpl) GT_EQ(expression numericExpression) boolExpression { - return GT_EQ(n.parent, expression) +func (n *numericInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs numericExpression) boolExpression { + return IS_NOT_DISTINCT_FROM(n.parent, rhs) +} + +func (n *numericInterfaceImpl) GT(rhs numericExpression) boolExpression { + return GT(n.parent, rhs) +} + +func (n *numericInterfaceImpl) GT_EQ(rhs numericExpression) boolExpression { + return GT_EQ(n.parent, rhs) } func (n *numericInterfaceImpl) LT(expression numericExpression) boolExpression { diff --git a/sqlbuilder/operators.go b/sqlbuilder/operators.go index c79b078..85abd55 100644 --- a/sqlbuilder/operators.go +++ b/sqlbuilder/operators.go @@ -19,6 +19,14 @@ func NOT_EQ(lhs, rhs expression) boolExpression { return newBinaryBoolExpression(lhs, rhs, "!=") } +func IS_DISTINCT_FROM(lhs, rhs expression) boolExpression { + return newBinaryBoolExpression(lhs, rhs, "IS DISTINCT FROM") +} + +func IS_NOT_DISTINCT_FROM(lhs, rhs expression) boolExpression { + return newBinaryBoolExpression(lhs, rhs, "IS NOT DISTINCT FROM") +} + // Returns a representation of "a table2.col4") + assert.Equal(t, out.buff.String(), "table3.col2 > table2.colStr") } func TestStringGT_EQ(t *testing.T) { @@ -52,7 +52,7 @@ func TestStringGT_EQ(t *testing.T) { err := exp.serialize(select_statement, &out) assert.NilError(t, err) - assert.Equal(t, out.buff.String(), "table3.col2 >= table2.col4") + assert.Equal(t, out.buff.String(), "table3.col2 >= table2.colStr") } func TestStringLT(t *testing.T) { @@ -62,7 +62,7 @@ func TestStringLT(t *testing.T) { err := exp.serialize(select_statement, &out) assert.NilError(t, err) - assert.Equal(t, out.buff.String(), "table3.col2 < table2.col4") + assert.Equal(t, out.buff.String(), "table3.col2 < table2.colStr") } func TestStringLT_EQ(t *testing.T) { @@ -72,5 +72,5 @@ func TestStringLT_EQ(t *testing.T) { err := exp.serialize(select_statement, &out) assert.NilError(t, err) - assert.Equal(t, out.buff.String(), "table3.col2 <= table2.col4") + assert.Equal(t, out.buff.String(), "table3.col2 <= table2.colStr") } diff --git a/sqlbuilder/test_utils.go b/sqlbuilder/test_utils.go index ae35188..8023b29 100644 --- a/sqlbuilder/test_utils.go +++ b/sqlbuilder/test_utils.go @@ -22,7 +22,7 @@ var table1 = NewTable( var table2Col3 = NewIntegerColumn("col3", Nullable) var table2Col4 = NewIntegerColumn("col4", Nullable) -var table2StrCol = NewStringColumn("col4", Nullable) +var table2StrCol = NewStringColumn("colStr", Nullable) var table2ColBool = NewBoolColumn("colBool", Nullable) var table2ColTime = NewTimeColumn("colTime", Nullable) diff --git a/sqlbuilder/time_expression.go b/sqlbuilder/time_expression.go index b699f13..ab90a67 100644 --- a/sqlbuilder/time_expression.go +++ b/sqlbuilder/time_expression.go @@ -5,6 +5,9 @@ type timeExpression interface { EQ(rhs timeExpression) boolExpression NOT_EQ(rhs timeExpression) boolExpression + IS_DISTINCT_FROM(rhs timeExpression) boolExpression + IS_NOT_DISTINCT_FROM(rhs timeExpression) boolExpression + LT(rhs timeExpression) boolExpression LT_EQ(rhs timeExpression) boolExpression GT(rhs timeExpression) boolExpression @@ -23,6 +26,14 @@ func (t *timeInterfaceImpl) NOT_EQ(rhs timeExpression) boolExpression { return NOT_EQ(t.parent, rhs) } +func (t *timeInterfaceImpl) IS_DISTINCT_FROM(rhs timeExpression) boolExpression { + return IS_DISTINCT_FROM(t.parent, rhs) +} + +func (t *timeInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs timeExpression) boolExpression { + return IS_NOT_DISTINCT_FROM(t.parent, rhs) +} + func (t *timeInterfaceImpl) LT(rhs timeExpression) boolExpression { return LT(t.parent, rhs) } @@ -47,7 +58,7 @@ type prefixTimeExpression struct { prefixOpExpression } -func newPrefixTimeExpression(expression expression, operator string) timeExpression { +func newPrefixTimeExpression(operator string, expression expression) timeExpression { timeExpr := prefixTimeExpression{} timeExpr.prefixOpExpression = newPrefixExpression(expression, operator) @@ -58,5 +69,5 @@ func newPrefixTimeExpression(expression expression, operator string) timeExpress } func INTERVAL(interval string) expression { - return newPrefixTimeExpression(Literal(interval), "INTERVAL") + return newPrefixTimeExpression("INTERVAL", Literal(interval)) } diff --git a/sqlbuilder/time_expression_test.go b/sqlbuilder/time_expression_test.go index 425ae3d..2a35938 100644 --- a/sqlbuilder/time_expression_test.go +++ b/sqlbuilder/time_expression_test.go @@ -3,35 +3,34 @@ package sqlbuilder import ( "gotest.tools/assert" "testing" - "time" ) func TestTimeExpressionEQ(t *testing.T) { assert.Equal(t, getTestSerialize(t, table1ColTime.EQ(table2ColTime)), "table1.colTime = table2.colTime") - assert.Equal(t, getTestSerialize(t, table1ColTime.EQ(Time(time.Now()))), "table1.colTime = $1") + assert.Equal(t, getTestSerialize(t, table1ColTime.EQ(Time(10, 20, 0, 0))), "table1.colTime = $1") } func TestTimeExpressionNOT_EQ(t *testing.T) { assert.Equal(t, getTestSerialize(t, table1ColTime.NOT_EQ(table2ColTime)), "table1.colTime != table2.colTime") - assert.Equal(t, getTestSerialize(t, table1ColTime.NOT_EQ(Time(time.Now()))), "table1.colTime != $1") + assert.Equal(t, getTestSerialize(t, table1ColTime.NOT_EQ(Time(10, 20, 0, 0))), "table1.colTime != $1") } func TestTimeExpressionLT(t *testing.T) { assert.Equal(t, getTestSerialize(t, table1ColTime.LT(table2ColTime)), "table1.colTime < table2.colTime") - assert.Equal(t, getTestSerialize(t, table1ColTime.LT(Time(time.Now()))), "table1.colTime < $1") + assert.Equal(t, getTestSerialize(t, table1ColTime.LT(Time(10, 20, 0, 0))), "table1.colTime < $1") } func TestTimeExpressionLT_EQ(t *testing.T) { assert.Equal(t, getTestSerialize(t, table1ColTime.LT_EQ(table2ColTime)), "table1.colTime <= table2.colTime") - assert.Equal(t, getTestSerialize(t, table1ColTime.LT_EQ(Time(time.Now()))), "table1.colTime <= $1") + assert.Equal(t, getTestSerialize(t, table1ColTime.LT_EQ(Time(10, 20, 0, 0))), "table1.colTime <= $1") } func TestTimeExpressionGT(t *testing.T) { assert.Equal(t, getTestSerialize(t, table1ColTime.GT(table2ColTime)), "table1.colTime > table2.colTime") - assert.Equal(t, getTestSerialize(t, table1ColTime.GT(Time(time.Now()))), "table1.colTime > $1") + assert.Equal(t, getTestSerialize(t, table1ColTime.GT(Time(10, 20, 0, 0))), "table1.colTime > $1") } func TestTimeExpressionGT_EQ(t *testing.T) { assert.Equal(t, getTestSerialize(t, table1ColTime.GT_EQ(table2ColTime)), "table1.colTime >= table2.colTime") - assert.Equal(t, getTestSerialize(t, table1ColTime.GT_EQ(Time(time.Now()))), "table1.colTime >= $1") + assert.Equal(t, getTestSerialize(t, table1ColTime.GT_EQ(Time(10, 20, 0, 0))), "table1.colTime >= $1") } diff --git a/sqlbuilder/timestamp_expression.go b/sqlbuilder/timestamp_expression.go new file mode 100644 index 0000000..f8ef4a3 --- /dev/null +++ b/sqlbuilder/timestamp_expression.go @@ -0,0 +1,51 @@ +package sqlbuilder + +type TimestampExpression interface { + expression + + EQ(rhs TimestampExpression) boolExpression + NOT_EQ(rhs TimestampExpression) boolExpression + IS_DISTINCT_FROM(rhs TimestampExpression) boolExpression + IS_NOT_DISTINCT_FROM(rhs TimestampExpression) boolExpression + + LT(rhs TimestampExpression) boolExpression + LT_EQ(rhs TimestampExpression) boolExpression + GT(rhs TimestampExpression) boolExpression + GT_EQ(rhs TimestampExpression) boolExpression +} + +type timestampInterfaceImpl struct { + parent TimestampExpression +} + +func (t *timestampInterfaceImpl) EQ(rhs TimestampExpression) boolExpression { + return EQ(t.parent, rhs) +} + +func (t *timestampInterfaceImpl) NOT_EQ(rhs TimestampExpression) boolExpression { + return NOT_EQ(t.parent, rhs) +} + +func (t *timestampInterfaceImpl) IS_DISTINCT_FROM(rhs TimestampExpression) boolExpression { + return IS_DISTINCT_FROM(t.parent, rhs) +} + +func (t *timestampInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs TimestampExpression) boolExpression { + return IS_NOT_DISTINCT_FROM(t.parent, rhs) +} + +func (t *timestampInterfaceImpl) LT(rhs TimestampExpression) boolExpression { + return LT(t.parent, rhs) +} + +func (t *timestampInterfaceImpl) LT_EQ(rhs TimestampExpression) boolExpression { + return LT_EQ(t.parent, rhs) +} + +func (t *timestampInterfaceImpl) GT(rhs TimestampExpression) boolExpression { + return GT(t.parent, rhs) +} + +func (t *timestampInterfaceImpl) GT_EQ(rhs TimestampExpression) boolExpression { + return GT_EQ(t.parent, rhs) +} diff --git a/sqlbuilder/timestampz_expression.go b/sqlbuilder/timestampz_expression.go new file mode 100644 index 0000000..40d96e0 --- /dev/null +++ b/sqlbuilder/timestampz_expression.go @@ -0,0 +1,51 @@ +package sqlbuilder + +type TimestampzExpression interface { + expression + + EQ(rhs TimestampzExpression) boolExpression + NOT_EQ(rhs TimestampzExpression) boolExpression + IS_DISTINCT_FROM(rhs TimestampzExpression) boolExpression + IS_NOT_DISTINCT_FROM(rhs TimestampzExpression) boolExpression + + LT(rhs TimestampzExpression) boolExpression + LT_EQ(rhs TimestampzExpression) boolExpression + GT(rhs TimestampzExpression) boolExpression + GT_EQ(rhs TimestampzExpression) boolExpression +} + +type timestampzInterfaceImpl struct { + parent TimestampzExpression +} + +func (t *timestampzInterfaceImpl) EQ(rhs TimestampzExpression) boolExpression { + return EQ(t.parent, rhs) +} + +func (t *timestampzInterfaceImpl) NOT_EQ(rhs TimestampzExpression) boolExpression { + return NOT_EQ(t.parent, rhs) +} + +func (t *timestampzInterfaceImpl) IS_DISTINCT_FROM(rhs TimestampzExpression) boolExpression { + return IS_DISTINCT_FROM(t.parent, rhs) +} + +func (t *timestampzInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs TimestampzExpression) boolExpression { + return IS_NOT_DISTINCT_FROM(t.parent, rhs) +} + +func (t *timestampzInterfaceImpl) LT(rhs TimestampzExpression) boolExpression { + return LT(t.parent, rhs) +} + +func (t *timestampzInterfaceImpl) LT_EQ(rhs TimestampzExpression) boolExpression { + return LT_EQ(t.parent, rhs) +} + +func (t *timestampzInterfaceImpl) GT(rhs TimestampzExpression) boolExpression { + return GT(t.parent, rhs) +} + +func (t *timestampzInterfaceImpl) GT_EQ(rhs TimestampzExpression) boolExpression { + return GT_EQ(t.parent, rhs) +} diff --git a/sqlbuilder/timez_expression.go b/sqlbuilder/timez_expression.go new file mode 100644 index 0000000..e00ade1 --- /dev/null +++ b/sqlbuilder/timez_expression.go @@ -0,0 +1,69 @@ +package sqlbuilder + +type timezExpression interface { + expression + + EQ(rhs timezExpression) boolExpression + NOT_EQ(rhs timezExpression) boolExpression + IS_DISTINCT_FROM(rhs timezExpression) boolExpression + IS_NOT_DISTINCT_FROM(rhs timezExpression) boolExpression + + LT(rhs timezExpression) boolExpression + LT_EQ(rhs timezExpression) boolExpression + GT(rhs timezExpression) boolExpression + GT_EQ(rhs timezExpression) boolExpression +} + +type timezInterfaceImpl struct { + parent timezExpression +} + +func (t *timezInterfaceImpl) EQ(rhs timezExpression) boolExpression { + return EQ(t.parent, rhs) +} + +func (t *timezInterfaceImpl) NOT_EQ(rhs timezExpression) boolExpression { + return NOT_EQ(t.parent, rhs) +} + +func (t *timezInterfaceImpl) IS_DISTINCT_FROM(rhs timezExpression) boolExpression { + return IS_DISTINCT_FROM(t.parent, rhs) +} + +func (t *timezInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs timezExpression) boolExpression { + return IS_NOT_DISTINCT_FROM(t.parent, rhs) +} + +func (t *timezInterfaceImpl) LT(rhs timezExpression) boolExpression { + return LT(t.parent, rhs) +} + +func (t *timezInterfaceImpl) LT_EQ(rhs timezExpression) boolExpression { + return LT_EQ(t.parent, rhs) +} + +func (t *timezInterfaceImpl) GT(rhs timezExpression) boolExpression { + return GT(t.parent, rhs) +} + +func (t *timezInterfaceImpl) GT_EQ(rhs timezExpression) boolExpression { + return GT_EQ(t.parent, rhs) +} + +//---------------------------------------------------// +type prefixTimezExpression struct { + expressionInterfaceImpl + timezInterfaceImpl + + prefixOpExpression +} + +func newPrefixTimezExpression(operator string, expression expression) timezExpression { + timeExpr := prefixTimezExpression{} + timeExpr.prefixOpExpression = newPrefixExpression(expression, operator) + + timeExpr.expressionInterfaceImpl.parent = &timeExpr + timeExpr.timezInterfaceImpl.parent = &timeExpr + + return &timeExpr +} diff --git a/tests/select_test.go b/tests/select_test.go index 2433c7d..20fe0b6 100644 --- a/tests/select_test.go +++ b/tests/select_test.go @@ -971,15 +971,15 @@ SELECT payment.payment_id AS "payment.payment_id", payment.amount AS "payment.amount", payment.payment_date AS "payment.payment_date" FROM dvds.payment -WHERE payment.payment_date <= '2007-02-14 22:16:01' +WHERE payment.payment_date < '2007-02-14 22:16:01.000' ORDER BY payment.payment_date ASC; ` query := Payment.SELECT(Payment.AllColumns). - WHERE(Payment.PaymentDate.LtEqL("2007-02-14 22:16:01")). + WHERE(Payment.PaymentDate.LT(Timestamp(2007, 02, 14, 22, 16, 01, 0))). ORDER_BY(Payment.PaymentDate.ASC()) - assertQuery(t, query, expectedSql, "2007-02-14 22:16:01") + assertQuery(t, query, expectedSql, "2007-02-14 22:16:01.000") payments := []model.Payment{} diff --git a/tests/types_test.go b/tests/types_test.go index eb80b89..52846db 100644 --- a/tests/types_test.go +++ b/tests/types_test.go @@ -3,6 +3,7 @@ package tests import ( "fmt" "github.com/google/uuid" + . "github.com/sub0zero/go-sqlbuilder/sqlbuilder" "github.com/sub0zero/go-sqlbuilder/tests/.test_files/dvd_rental/test_sample/model" . "github.com/sub0zero/go-sqlbuilder/tests/.test_files/dvd_rental/test_sample/table" "gotest.tools/assert" @@ -20,11 +21,159 @@ func TestAllTypesSelect(t *testing.T) { assert.Equal(t, len(dest), 2) - assert.DeepEqual(t, dest[0], dest0) - assert.DeepEqual(t, dest[1], dest1) + assert.DeepEqual(t, dest[0], allTypesRow0) + assert.DeepEqual(t, dest[1], allTypesRow1) } -var dest0 = model.AllTypes{ +func TestStringOperators(t *testing.T) { + query := AllTypes.SELECT( + AllTypes.Text.EQ(AllTypes.Character), + AllTypes.Text.EQ(String("Text")), + AllTypes.Text.NOT_EQ(AllTypes.CharacterVaryingPtr), + AllTypes.Text.NOT_EQ(String("Text")), + AllTypes.Text.GT(AllTypes.Text), + AllTypes.Text.GT(String("Text")), + AllTypes.Text.GT_EQ(AllTypes.TextPtr), + AllTypes.Text.GT_EQ(String("Text")), + AllTypes.Text.LT(AllTypes.Character), + AllTypes.Text.LT(String("Text")), + AllTypes.Text.LT_EQ(AllTypes.CharacterVaryingPtr), + AllTypes.Text.LT_EQ(String("Text")), + ) + + fmt.Println(query.DebugSql()) + + err := query.Query(db, &struct{}{}) + + assert.NilError(t, err) +} + +func TestExpressionOperators(t *testing.T) { + query := AllTypes.SELECT( + AllTypes.Integer.IS_NULL(), + AllTypes.Timestamp.IS_NOT_NULL(), + ) + + fmt.Println(query.DebugSql()) + + err := query.Query(db, &struct{}{}) + + assert.NilError(t, err) +} + +func TestBoolOperators(t *testing.T) { + query := AllTypes.SELECT( + AllTypes.Boolean.EQ(AllTypes.BooleanPtr), + AllTypes.Boolean.EQ(Bool(true)), + AllTypes.Boolean.NOT_EQ(AllTypes.BooleanPtr), + AllTypes.Boolean.NOT_EQ(Bool(false)), + AllTypes.Boolean.IS_DISTINCT_FROM(AllTypes.BooleanPtr), + AllTypes.Boolean.IS_DISTINCT_FROM(Bool(true)), + AllTypes.Boolean.IS_NOT_DISTINCT_FROM(AllTypes.BooleanPtr), + AllTypes.Boolean.IS_NOT_DISTINCT_FROM(Bool(true)), + AllTypes.Boolean.IS_TRUE(), + AllTypes.Boolean.IS_NOT_TRUE(), + AllTypes.Boolean.IS_NOT_FALSE(), + AllTypes.Boolean.IS_UNKNOWN(), + AllTypes.Boolean.IS_NOT_UNKNOWN(), + + AllTypes.Boolean.AND(AllTypes.Boolean).EQ(AllTypes.Boolean.AND(AllTypes.Boolean)), + AllTypes.Boolean.OR(AllTypes.Boolean).EQ(AllTypes.Boolean.AND(AllTypes.Boolean)), + ) + + fmt.Println(query.DebugSql()) + + err := query.Query(db, &struct{}{}) + + assert.NilError(t, err) +} + +func TestNumericOperators(t *testing.T) { + query := AllTypes.SELECT( + AllTypes.Numeric.EQ(AllTypes.Numeric), + AllTypes.Decimal.EQ(Int(12)), + AllTypes.Real.EQ(Float(12.12)), + AllTypes.Smallint.NOT_EQ(AllTypes.Real), + AllTypes.Integer.NOT_EQ(Int(12)), + AllTypes.Bigint.NOT_EQ(Float(12)), + AllTypes.Numeric.IS_DISTINCT_FROM(AllTypes.Numeric), + AllTypes.Decimal.IS_DISTINCT_FROM(Int(12)), + AllTypes.Real.IS_DISTINCT_FROM(Float(12.12)), + AllTypes.Numeric.IS_NOT_DISTINCT_FROM(AllTypes.Numeric), + AllTypes.Decimal.IS_NOT_DISTINCT_FROM(Int(12)), + AllTypes.Real.IS_NOT_DISTINCT_FROM(Float(12.12)), + AllTypes.Numeric.LT(AllTypes.Integer), + AllTypes.Numeric.LT(Int(124)), + AllTypes.Numeric.LT(Float(34.56)), + AllTypes.Smallint.LT_EQ(AllTypes.Numeric), + AllTypes.Integer.LT_EQ(Int(45)), + AllTypes.Bigint.LT_EQ(Float(65)), + AllTypes.Numeric.GT(AllTypes.Smallint), + AllTypes.Numeric.GT(Int(124)), + AllTypes.Numeric.GT(Float(34.56)), + AllTypes.Smallint.GT_EQ(AllTypes.Numeric), + AllTypes.Integer.GT_EQ(Int(45)), + AllTypes.Bigint.GT_EQ(Float(65)), + ) + + fmt.Println(query.DebugSql()) + + err := query.Query(db, &struct{}{}) + + assert.NilError(t, err) +} + +func TestTimeOperators(t *testing.T) { + query := AllTypes.SELECT( + AllTypes.Time.EQ(AllTypes.Time), + AllTypes.Time.EQ(Time(23, 6, 6, 1)), + AllTypes.Timez.EQ(AllTypes.TimezPtr), + AllTypes.Timez.EQ(Timez(23, 6, 6, 222, +200)), + AllTypes.Timestamp.EQ(AllTypes.TimestampPtr), + AllTypes.Timestamp.EQ(Timestamp(2010, 10, 21, 15, 30, 12, 333)), + AllTypes.Timestampz.EQ(AllTypes.TimestampzPtr), + AllTypes.Timestampz.EQ(Timestampz(2010, 10, 21, 15, 30, 12, 444, 0)), + AllTypes.Date.EQ(AllTypes.DatePtr), + AllTypes.Date.EQ(Date(2010, 12, 3)), + + AllTypes.Time.NOT_EQ(AllTypes.Time), + AllTypes.Time.NOT_EQ(Time(23, 6, 6, 10)), + AllTypes.Timez.NOT_EQ(AllTypes.TimezPtr), + AllTypes.Timez.NOT_EQ(Timez(23, 6, 6, 555, +200)), + AllTypes.Timestamp.NOT_EQ(AllTypes.TimestampPtr), + AllTypes.Timestamp.NOT_EQ(Timestamp(2010, 10, 21, 15, 30, 12, 666)), + AllTypes.Timestampz.NOT_EQ(AllTypes.TimestampzPtr), + AllTypes.Timestampz.NOT_EQ(Timestampz(2010, 10, 21, 15, 30, 12, 777, 0)), + AllTypes.Date.NOT_EQ(AllTypes.DatePtr), + AllTypes.Date.NOT_EQ(Date(2010, 12, 3)), + + AllTypes.Time.IS_DISTINCT_FROM(AllTypes.Time), + AllTypes.Time.IS_DISTINCT_FROM(Time(23, 6, 6, 100)), + + AllTypes.Time.IS_NOT_DISTINCT_FROM(AllTypes.Time), + AllTypes.Time.IS_NOT_DISTINCT_FROM(Time(23, 6, 6, 200)), + + AllTypes.Time.LT(AllTypes.Time), + AllTypes.Time.LT(Time(23, 6, 6, 22)), + + AllTypes.Time.LT_EQ(AllTypes.Time), + AllTypes.Time.LT_EQ(Time(23, 6, 6, 33)), + + AllTypes.Time.GT(AllTypes.Time), + AllTypes.Time.GT(Time(23, 6, 6, 0)), + + AllTypes.Time.GT_EQ(AllTypes.Time), + AllTypes.Time.GT_EQ(Time(23, 6, 6, 1)), + ) + + fmt.Println(query.DebugSql()) + + err := query.Query(db, &struct{}{}) + + assert.NilError(t, err) +} + +var allTypesRow0 = model.AllTypes{ SmallintPtr: int16Ptr(1), Smallint: 1, IntegerPtr: int32Ptr(300), @@ -90,7 +239,7 @@ var dest0 = model.AllTypes{ TextMultiDimArray: "{{meeting,lunch},{training,presentation}}", } -var dest1 = model.AllTypes{ +var allTypesRow1 = model.AllTypes{ SmallintPtr: nil, Smallint: 1, IntegerPtr: nil,