diff --git a/internal/jet/cast.go b/internal/jet/cast.go index 4c05f76..b7768af 100644 --- a/internal/jet/cast.go +++ b/internal/jet/cast.go @@ -1,17 +1,7 @@ package jet -import "strconv" - type Cast interface { AS(castType string) Expression - - AS_CHAR(lenght ...int) StringExpression - // Cast expression AS date type - AS_DATE() DateExpression - // Cast expression AS numeric type, using precision and optionally scale - AS_DECIMAL() FloatExpression - // Cast expression AS time type - AS_TIME() TimeExpression } type CastImpl struct { @@ -37,29 +27,6 @@ func (b *CastImpl) AS(castType string) Expression { return castExp } -func (b *CastImpl) AS_CHAR(lenght ...int) StringExpression { - if len(lenght) > 0 { - return StringExp(b.AS("CHAR(" + strconv.Itoa(lenght[0]) + ")")) - } - - return StringExp(b.AS("CHAR")) -} - -// Cast expression AS date type -func (b *CastImpl) AS_DATE() DateExpression { - return DateExp(b.AS("DATE")) -} - -// Cast expression AS date type -func (b *CastImpl) AS_DECIMAL() FloatExpression { - return FloatExp(b.AS("DECIMAL")) -} - -// Cast expression AS date type -func (b *CastImpl) AS_TIME() TimeExpression { - return TimeExp(b.AS("TIME")) -} - type castExpression struct { ExpressionInterfaceImpl diff --git a/internal/jet/literal_expression.go b/internal/jet/literal_expression.go index 1ba06c3..a5fbbaa 100644 --- a/internal/jet/literal_expression.go +++ b/internal/jet/literal_expression.go @@ -2,11 +2,10 @@ package jet import ( "fmt" - "strconv" "time" ) -// Representation of an escaped literal +// LiteralExpression is representation of an escaped literal type LiteralExpression interface { Expression @@ -129,76 +128,149 @@ func String(value string, constant ...bool) StringExpression { return &stringLiteral } -func formatMilliseconds(milliseconds ...int) string { - if len(milliseconds) > 0 { - if milliseconds[0] < 1000 { - return fmt.Sprintf(".%03d", milliseconds[0]) - } else { - return "." + strconv.Itoa(milliseconds[0]) - } - } +//---------------------------------------------------// - return "" +type timeLiteral struct { + timeInterfaceImpl + literalExpressionImpl } // Time creates new time literal expression -func Time(hour, minute, second int, milliseconds ...int) TimeExpression { +func Time(hour, minute, second int, nanoseconds ...time.Duration) TimeExpression { + timeLiteral := &timeLiteral{} timeStr := fmt.Sprintf("%02d:%02d:%02d", hour, minute, second) + timeStr += formatNanoseconds(nanoseconds...) + timeLiteral.literalExpressionImpl = *literal(timeStr) - timeStr += formatMilliseconds(milliseconds...) + timeLiteral.timeInterfaceImpl.parent = timeLiteral - return TimeExp(literal(timeStr)) + return timeLiteral } func TimeT(t time.Time) TimeExpression { - return TimeExp(literal(t)) + timeLiteral := &timeLiteral{} + timeLiteral.literalExpressionImpl = *literal(t) + timeLiteral.timeInterfaceImpl.parent = timeLiteral + + return timeLiteral +} + +//---------------------------------------------------// + +type timezLiteral struct { + timezInterfaceImpl + literalExpressionImpl } // Timez creates new time with time zone literal expression -func Timez(hour, minute, second, milliseconds, timezone int) TimezExpression { - timeStr := fmt.Sprintf("%02d:%02d:%02d.%03d %+03d", hour, minute, second, milliseconds, timezone) +func Timez(hour, minute, second int, nanoseconds time.Duration, timezone string) TimezExpression { + timezLiteral := timezLiteral{} + timeStr := fmt.Sprintf("%02d:%02d:%02d", hour, minute, second) + timeStr += formatNanoseconds(nanoseconds) + timeStr += " " + timezone + timezLiteral.literalExpressionImpl = *literal(timeStr) return TimezExp(literal(timeStr)) } func TimezT(t time.Time) TimezExpression { - return TimezExp(literal(t)) + timeLiteral := &timezLiteral{} + timeLiteral.literalExpressionImpl = *literal(t) + timeLiteral.timezInterfaceImpl.parent = timeLiteral + + return timeLiteral +} + +//---------------------------------------------------// + +type timestampLiteral struct { + timestampInterfaceImpl + literalExpressionImpl } // Timestamp creates new timestamp literal expression -func Timestamp(year int, month time.Month, day, hour, minute, second int, milliseconds ...int) TimestampExpression { +func Timestamp(year int, month time.Month, day, hour, minute, second int, nanoseconds ...time.Duration) TimestampExpression { + timestamp := ×tampLiteral{} timeStr := fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, second) - - timeStr += formatMilliseconds(milliseconds...) - - return TimestampExp(literal(timeStr)) + timeStr += formatNanoseconds(nanoseconds...) + timestamp.literalExpressionImpl = *literal(timeStr) + timestamp.timestampInterfaceImpl.parent = timestamp + return timestamp } func TimestampT(t time.Time) TimestampExpression { - return TimestampExp(literal(t)) + timestamp := ×tampLiteral{} + timestamp.literalExpressionImpl = *literal(t) + timestamp.timestampInterfaceImpl.parent = timestamp + return timestamp } -// Timestampz creates new timestamp with time zone literal expression -func Timestampz(year, month, day, hour, minute, second, milliseconds, timezone int) TimestampzExpression { - timeStr := fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d.%03d %+04d", - year, month, day, hour, minute, second, milliseconds, timezone) +//---------------------------------------------------// - return TimestampzExp(literal(timeStr)) +type timestampzLiteral struct { + timestampzInterfaceImpl + literalExpressionImpl +} + +// Timestamp creates new timestamp literal expression +func Timestampz(year int, month time.Month, day, hour, minute, second int, nanoseconds time.Duration, timezone string) TimestampzExpression { + timestamp := ×tampzLiteral{} + timeStr := fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, second) + timeStr += formatNanoseconds(nanoseconds) + timeStr += " " + timezone + + timestamp.literalExpressionImpl = *literal(timeStr) + timestamp.timestampzInterfaceImpl.parent = timestamp + return timestamp } func TimestampzT(t time.Time) TimestampzExpression { - return TimestampzExp(literal(t)) + timestamp := ×tampzLiteral{} + timestamp.literalExpressionImpl = *literal(t) + timestamp.timestampzInterfaceImpl.parent = timestamp + return timestamp +} + +//---------------------------------------------------// + +type dateLiteral struct { + dateInterfaceImpl + literalExpressionImpl } //Date creates new date expression func Date(year int, month time.Month, day int) DateExpression { - timeStr := fmt.Sprintf("%04d-%02d-%02d", year, month, day) + dateLiteral := &dateLiteral{} - return DateExp(literal(timeStr)) + timeStr := fmt.Sprintf("%04d-%02d-%02d", year, month, day) + dateLiteral.literalExpressionImpl = *literal(timeStr) + dateLiteral.dateInterfaceImpl.parent = dateLiteral + + return dateLiteral } func DateT(t time.Time) DateExpression { - return DateExp(literal(t)) + dateLiteral := &dateLiteral{} + dateLiteral.literalExpressionImpl = *literal(t) + dateLiteral.dateInterfaceImpl.parent = dateLiteral + + return dateLiteral +} + +func formatNanoseconds(nanoseconds ...time.Duration) string { + if len(nanoseconds) > 0 && nanoseconds[0] != 0 { + duration := fmt.Sprintf("%09d", nanoseconds[0]) + i := len(duration) - 1 + for ; i >= 3; i-- { + if duration[i] != '0' { + break + } + } + + return "." + duration[0:i+1] + } + + return "" } //--------------------------------------------------// diff --git a/internal/jet/literal_expression_test.go b/internal/jet/literal_expression_test.go index 0b671d5..5142f27 100644 --- a/internal/jet/literal_expression_test.go +++ b/internal/jet/literal_expression_test.go @@ -12,3 +12,49 @@ func TestRawExpression(t *testing.T) { assertClauseSerialize(t, DateT(timeT), "$1", timeT) } + +func TestTimeLiteral(t *testing.T) { + assertClauseDebugSerialize(t, Time(11, 5, 30), "'11:05:30'") + assertClauseDebugSerialize(t, Time(11, 5, 30, 0), "'11:05:30'") + assertClauseDebugSerialize(t, Time(11, 5, 30, 3*time.Millisecond), "'11:05:30.003'") + assertClauseDebugSerialize(t, Time(11, 5, 30, 30*time.Millisecond), "'11:05:30.030'") + assertClauseDebugSerialize(t, Time(11, 5, 30, 300*time.Millisecond), "'11:05:30.300'") + assertClauseDebugSerialize(t, Time(11, 5, 30, 300*time.Microsecond), "'11:05:30.0003'") + assertClauseDebugSerialize(t, Time(11, 5, 30, 4*time.Nanosecond), "'11:05:30.000000004'") +} + +func TestTimeT(t *testing.T) { + timeT := time.Date(2000, 1, 1, 11, 40, 20, 124, time.UTC) + assertClauseDebugSerialize(t, TimeT(timeT), `'2000-01-01 11:40:20.000000124Z'`) +} + +func TestTimezLiteral(t *testing.T) { + assertClauseDebugSerialize(t, Timez(11, 5, 30, 10*time.Nanosecond, "UTC"), "'11:05:30.00000001 UTC'") + assertClauseDebugSerialize(t, Timez(11, 5, 30, 0, "+1"), "'11:05:30 +1'") + assertClauseDebugSerialize(t, Timez(11, 5, 30, 3*time.Microsecond, "-7"), "'11:05:30.000003 -7'") + assertClauseDebugSerialize(t, Timez(11, 5, 30, 30*time.Millisecond, "+8:00"), "'11:05:30.030 +8:00'") + assertClauseDebugSerialize(t, Timez(11, 5, 30, 300*time.Nanosecond, "America/New_Yor"), "'11:05:30.0000003 America/New_Yor'") + assertClauseDebugSerialize(t, Timez(11, 5, 30, 3000*time.Nanosecond, "zulu"), "'11:05:30.000003 zulu'") +} + +func TestTimestampLiteral(t *testing.T) { + assertClauseDebugSerialize(t, Timestamp(2011, 1, 8, 11, 5, 30), "'2011-01-08 11:05:30'") + assertClauseDebugSerialize(t, Timestamp(2011, 2, 7, 11, 5, 30, 0), "'2011-02-07 11:05:30'") + assertClauseDebugSerialize(t, Timestamp(2011, 3, 6, 11, 5, 30, 3*time.Millisecond), "'2011-03-06 11:05:30.003'") + assertClauseDebugSerialize(t, Timestamp(2011, 4, 5, 11, 5, 30, 30*time.Millisecond), "'2011-04-05 11:05:30.030'") + assertClauseDebugSerialize(t, Timestamp(2011, 5, 4, 11, 5, 30, 300*time.Millisecond), "'2011-05-04 11:05:30.300'") + assertClauseDebugSerialize(t, Timestamp(2011, 6, 3, 11, 5, 30, 3000*time.Microsecond), "'2011-06-03 11:05:30.003'") +} + +func TestTimestampzLiteral(t *testing.T) { + assertClauseDebugSerialize(t, Timestampz(2011, 1, 8, 11, 5, 30, 0, "UTC"), "'2011-01-08 11:05:30 UTC'") + assertClauseDebugSerialize(t, Timestampz(2011, 2, 7, 11, 5, 30, 0, "PST"), "'2011-02-07 11:05:30 PST'") + assertClauseDebugSerialize(t, Timestampz(2011, 3, 6, 11, 5, 30, 3, "+4:00"), "'2011-03-06 11:05:30.000000003 +4:00'") + assertClauseDebugSerialize(t, Timestampz(2011, 4, 5, 11, 5, 30, 30, "-8:00"), "'2011-04-05 11:05:30.00000003 -8:00'") + assertClauseDebugSerialize(t, Timestampz(2011, 5, 4, 11, 5, 30, 300, "400"), "'2011-05-04 11:05:30.0000003 400'") + assertClauseDebugSerialize(t, Timestampz(2011, 6, 3, 11, 5, 30, 3000, "zulu"), "'2011-06-03 11:05:30.000003 zulu'") +} + +func TestDate(t *testing.T) { + assertClauseDebugSerialize(t, Date(2019, 8, 8), `'2019-08-08'`) +} diff --git a/internal/jet/serializer_test.go b/internal/jet/serializer_test.go index 0d55807..ad84b96 100644 --- a/internal/jet/serializer_test.go +++ b/internal/jet/serializer_test.go @@ -24,6 +24,7 @@ func TestArgToString(t *testing.T) { assert.Equal(t, argToString(float64(1.11)), "1.11") assert.Equal(t, argToString("john"), "'john'") + assert.Equal(t, argToString("It's text"), "'It''s text'") assert.Equal(t, argToString([]byte("john")), "'john'") assert.Equal(t, argToString(uuid.MustParse("b68dbff4-a87d-11e9-a7f2-98ded00c39c6")), "'b68dbff4-a87d-11e9-a7f2-98ded00c39c6'") diff --git a/internal/jet/testutils.go b/internal/jet/testutils.go index 9680e95..ebcf5ce 100644 --- a/internal/jet/testutils.go +++ b/internal/jet/testutils.go @@ -67,6 +67,18 @@ func assertClauseSerializeErr(t *testing.T, clause Serializer, errString string) assert.Error(t, err, errString) } +func assertClauseDebugSerialize(t *testing.T, clause Serializer, query string, args ...interface{}) { + out := SqlBuilder{Dialect: DefaultDialect, debug: true} + err := clause.serialize(SelectStatementType, &out) + + assert.NilError(t, err) + + //fmt.Println(out.Buff.String()) + + assert.DeepEqual(t, out.Buff.String(), query) + assert.DeepEqual(t, out.Args, args) +} + func assertProjectionSerialize(t *testing.T, projection Projection, query string, args ...interface{}) { out := SqlBuilder{Dialect: DefaultDialect} err := projection.serializeForProjection(SelectStatementType, &out) diff --git a/internal/jet/time_expression_test.go b/internal/jet/time_expression_test.go index 8f595a2..2b3d015 100644 --- a/internal/jet/time_expression_test.go +++ b/internal/jet/time_expression_test.go @@ -2,52 +2,53 @@ package jet import ( "testing" + "time" ) var timeVar = Time(10, 20, 0, 0) func TestTimeExpressionEQ(t *testing.T) { assertClauseSerialize(t, table1ColTime.EQ(table2ColTime), "(table1.col_time = table2.col_time)") - assertClauseSerialize(t, table1ColTime.EQ(timeVar), "(table1.col_time = $1)", "10:20:00.000") + assertClauseSerialize(t, table1ColTime.EQ(timeVar), "(table1.col_time = $1)", "10:20:00") } func TestTimeExpressionNOT_EQ(t *testing.T) { assertClauseSerialize(t, table1ColTime.NOT_EQ(table2ColTime), "(table1.col_time != table2.col_time)") - assertClauseSerialize(t, table1ColTime.NOT_EQ(timeVar), "(table1.col_time != $1)", "10:20:00.000") + assertClauseSerialize(t, table1ColTime.NOT_EQ(timeVar), "(table1.col_time != $1)", "10:20:00") } func TestTimeExpressionIS_DISTINCT_FROM(t *testing.T) { assertClauseSerialize(t, table1ColTime.IS_DISTINCT_FROM(table2ColTime), "(table1.col_time IS DISTINCT FROM table2.col_time)") - assertClauseSerialize(t, table1ColTime.IS_DISTINCT_FROM(timeVar), "(table1.col_time IS DISTINCT FROM $1)", "10:20:00.000") + assertClauseSerialize(t, table1ColTime.IS_DISTINCT_FROM(timeVar), "(table1.col_time IS DISTINCT FROM $1)", "10:20:00") } func TestTimeExpressionIS_NOT_DISTINCT_FROM(t *testing.T) { assertClauseSerialize(t, table1ColTime.IS_NOT_DISTINCT_FROM(table2ColTime), "(table1.col_time IS NOT DISTINCT FROM table2.col_time)") - assertClauseSerialize(t, table1ColTime.IS_NOT_DISTINCT_FROM(timeVar), "(table1.col_time IS NOT DISTINCT FROM $1)", "10:20:00.000") + assertClauseSerialize(t, table1ColTime.IS_NOT_DISTINCT_FROM(timeVar), "(table1.col_time IS NOT DISTINCT FROM $1)", "10:20:00") } func TestTimeExpressionLT(t *testing.T) { assertClauseSerialize(t, table1ColTime.LT(table2ColTime), "(table1.col_time < table2.col_time)") - assertClauseSerialize(t, table1ColTime.LT(timeVar), "(table1.col_time < $1)", "10:20:00.000") + assertClauseSerialize(t, table1ColTime.LT(timeVar), "(table1.col_time < $1)", "10:20:00") } func TestTimeExpressionLT_EQ(t *testing.T) { assertClauseSerialize(t, table1ColTime.LT_EQ(table2ColTime), "(table1.col_time <= table2.col_time)") - assertClauseSerialize(t, table1ColTime.LT_EQ(timeVar), "(table1.col_time <= $1)", "10:20:00.000") + assertClauseSerialize(t, table1ColTime.LT_EQ(timeVar), "(table1.col_time <= $1)", "10:20:00") } func TestTimeExpressionGT(t *testing.T) { assertClauseSerialize(t, table1ColTime.GT(table2ColTime), "(table1.col_time > table2.col_time)") - assertClauseSerialize(t, table1ColTime.GT(timeVar), "(table1.col_time > $1)", "10:20:00.000") + assertClauseSerialize(t, table1ColTime.GT(timeVar), "(table1.col_time > $1)", "10:20:00") } func TestTimeExpressionGT_EQ(t *testing.T) { assertClauseSerialize(t, table1ColTime.GT_EQ(table2ColTime), "(table1.col_time >= table2.col_time)") - assertClauseSerialize(t, table1ColTime.GT_EQ(timeVar), "(table1.col_time >= $1)", "10:20:00.000") + assertClauseSerialize(t, table1ColTime.GT_EQ(timeVar), "(table1.col_time >= $1)", "10:20:00") } func TestTimeExp(t *testing.T) { assertClauseSerialize(t, TimeExp(table1ColFloat), "table1.col_float") - assertClauseSerialize(t, TimeExp(table1ColFloat).LT(Time(1, 1, 1, 1)), + assertClauseSerialize(t, TimeExp(table1ColFloat).LT(Time(1, 1, 1, 1*time.Millisecond)), "(table1.col_float < $1)", string("01:01:01.001")) } diff --git a/internal/jet/timestamp_expression_test.go b/internal/jet/timestamp_expression_test.go index b49fc9f..9a9ceb4 100644 --- a/internal/jet/timestamp_expression_test.go +++ b/internal/jet/timestamp_expression_test.go @@ -1,52 +1,55 @@ package jet -import "testing" +import ( + "testing" + "time" +) -var timestamp = Timestamp(2000, 1, 31, 10, 20, 0, 0) +var timestamp = Timestamp(2000, 1, 31, 10, 20, 0, 3*time.Millisecond) func TestTimestampExpressionEQ(t *testing.T) { assertClauseSerialize(t, table1ColTimestamp.EQ(table2ColTimestamp), "(table1.col_timestamp = table2.col_timestamp)") assertClauseSerialize(t, table1ColTimestamp.EQ(timestamp), - "(table1.col_timestamp = $1)", "2000-01-31 10:20:00.000") + "(table1.col_timestamp = $1)", "2000-01-31 10:20:00.003") } func TestTimestampExpressionNOT_EQ(t *testing.T) { assertClauseSerialize(t, table1ColTimestamp.NOT_EQ(table2ColTimestamp), "(table1.col_timestamp != table2.col_timestamp)") - assertClauseSerialize(t, table1ColTimestamp.NOT_EQ(timestamp), "(table1.col_timestamp != $1)", "2000-01-31 10:20:00.000") + assertClauseSerialize(t, table1ColTimestamp.NOT_EQ(timestamp), "(table1.col_timestamp != $1)", "2000-01-31 10:20:00.003") } func TestTimestampExpressionIS_DISTINCT_FROM(t *testing.T) { assertClauseSerialize(t, table1ColTimestamp.IS_DISTINCT_FROM(table2ColTimestamp), "(table1.col_timestamp IS DISTINCT FROM table2.col_timestamp)") - assertClauseSerialize(t, table1ColTimestamp.IS_DISTINCT_FROM(timestamp), "(table1.col_timestamp IS DISTINCT FROM $1)", "2000-01-31 10:20:00.000") + assertClauseSerialize(t, table1ColTimestamp.IS_DISTINCT_FROM(timestamp), "(table1.col_timestamp IS DISTINCT FROM $1)", "2000-01-31 10:20:00.003") } func TestTimestampExpressionIS_NOT_DISTINCT_FROM(t *testing.T) { assertClauseSerialize(t, table1ColTimestamp.IS_NOT_DISTINCT_FROM(table2ColTimestamp), "(table1.col_timestamp IS NOT DISTINCT FROM table2.col_timestamp)") - assertClauseSerialize(t, table1ColTimestamp.IS_NOT_DISTINCT_FROM(timestamp), "(table1.col_timestamp IS NOT DISTINCT FROM $1)", "2000-01-31 10:20:00.000") + assertClauseSerialize(t, table1ColTimestamp.IS_NOT_DISTINCT_FROM(timestamp), "(table1.col_timestamp IS NOT DISTINCT FROM $1)", "2000-01-31 10:20:00.003") } func TestTimestampExpressionLT(t *testing.T) { assertClauseSerialize(t, table1ColTimestamp.LT(table2ColTimestamp), "(table1.col_timestamp < table2.col_timestamp)") - assertClauseSerialize(t, table1ColTimestamp.LT(timestamp), "(table1.col_timestamp < $1)", "2000-01-31 10:20:00.000") + assertClauseSerialize(t, table1ColTimestamp.LT(timestamp), "(table1.col_timestamp < $1)", "2000-01-31 10:20:00.003") } func TestTimestampExpressionLT_EQ(t *testing.T) { assertClauseSerialize(t, table1ColTimestamp.LT_EQ(table2ColTimestamp), "(table1.col_timestamp <= table2.col_timestamp)") - assertClauseSerialize(t, table1ColTimestamp.LT_EQ(timestamp), "(table1.col_timestamp <= $1)", "2000-01-31 10:20:00.000") + assertClauseSerialize(t, table1ColTimestamp.LT_EQ(timestamp), "(table1.col_timestamp <= $1)", "2000-01-31 10:20:00.003") } func TestTimestampExpressionGT(t *testing.T) { assertClauseSerialize(t, table1ColTimestamp.GT(table2ColTimestamp), "(table1.col_timestamp > table2.col_timestamp)") - assertClauseSerialize(t, table1ColTimestamp.GT(timestamp), "(table1.col_timestamp > $1)", "2000-01-31 10:20:00.000") + assertClauseSerialize(t, table1ColTimestamp.GT(timestamp), "(table1.col_timestamp > $1)", "2000-01-31 10:20:00.003") } func TestTimestampExpressionGT_EQ(t *testing.T) { assertClauseSerialize(t, table1ColTimestamp.GT_EQ(table2ColTimestamp), "(table1.col_timestamp >= table2.col_timestamp)") - assertClauseSerialize(t, table1ColTimestamp.GT_EQ(timestamp), "(table1.col_timestamp >= $1)", "2000-01-31 10:20:00.000") + assertClauseSerialize(t, table1ColTimestamp.GT_EQ(timestamp), "(table1.col_timestamp >= $1)", "2000-01-31 10:20:00.003") } func TestTimestampExp(t *testing.T) { assertClauseSerialize(t, TimestampExp(table1ColFloat), "table1.col_float") assertClauseSerialize(t, TimestampExp(table1ColFloat).LT(timestamp), - "(table1.col_float < $1)", "2000-01-31 10:20:00.000") + "(table1.col_float < $1)", "2000-01-31 10:20:00.003") } diff --git a/internal/jet/timestampz_expression_test.go b/internal/jet/timestampz_expression_test.go index 8ec30d3..6880c93 100644 --- a/internal/jet/timestampz_expression_test.go +++ b/internal/jet/timestampz_expression_test.go @@ -1,52 +1,55 @@ package jet -import "testing" +import ( + "testing" + "time" +) -var timestampz = Timestampz(2000, 1, 31, 10, 20, 0, 0, 2) +var timestampz = Timestampz(2000, 1, 31, 10, 20, 5, 23*time.Microsecond, "+200") func TestTimestampzExpressionEQ(t *testing.T) { assertClauseSerialize(t, table1ColTimestampz.EQ(table2ColTimestampz), "(table1.col_timestampz = table2.col_timestampz)") assertClauseSerialize(t, table1ColTimestampz.EQ(timestampz), - "(table1.col_timestampz = $1)", "2000-01-31 10:20:00.000 +002") + "(table1.col_timestampz = $1)", "2000-01-31 10:20:05.000023 +200") } func TestTimestampzExpressionNOT_EQ(t *testing.T) { assertClauseSerialize(t, table1ColTimestampz.NOT_EQ(table2ColTimestampz), "(table1.col_timestampz != table2.col_timestampz)") - assertClauseSerialize(t, table1ColTimestampz.NOT_EQ(timestampz), "(table1.col_timestampz != $1)", "2000-01-31 10:20:00.000 +002") + assertClauseSerialize(t, table1ColTimestampz.NOT_EQ(timestampz), "(table1.col_timestampz != $1)", "2000-01-31 10:20:05.000023 +200") } func TestTimestampzExpressionIS_DISTINCT_FROM(t *testing.T) { assertClauseSerialize(t, table1ColTimestampz.IS_DISTINCT_FROM(table2ColTimestampz), "(table1.col_timestampz IS DISTINCT FROM table2.col_timestampz)") - assertClauseSerialize(t, table1ColTimestampz.IS_DISTINCT_FROM(timestampz), "(table1.col_timestampz IS DISTINCT FROM $1)", "2000-01-31 10:20:00.000 +002") + assertClauseSerialize(t, table1ColTimestampz.IS_DISTINCT_FROM(timestampz), "(table1.col_timestampz IS DISTINCT FROM $1)", "2000-01-31 10:20:05.000023 +200") } func TestTimestampzExpressionIS_NOT_DISTINCT_FROM(t *testing.T) { assertClauseSerialize(t, table1ColTimestampz.IS_NOT_DISTINCT_FROM(table2ColTimestampz), "(table1.col_timestampz IS NOT DISTINCT FROM table2.col_timestampz)") - assertClauseSerialize(t, table1ColTimestampz.IS_NOT_DISTINCT_FROM(timestampz), "(table1.col_timestampz IS NOT DISTINCT FROM $1)", "2000-01-31 10:20:00.000 +002") + assertClauseSerialize(t, table1ColTimestampz.IS_NOT_DISTINCT_FROM(timestampz), "(table1.col_timestampz IS NOT DISTINCT FROM $1)", "2000-01-31 10:20:05.000023 +200") } func TestTimestampzExpressionLT(t *testing.T) { assertClauseSerialize(t, table1ColTimestampz.LT(table2ColTimestampz), "(table1.col_timestampz < table2.col_timestampz)") - assertClauseSerialize(t, table1ColTimestampz.LT(timestampz), "(table1.col_timestampz < $1)", "2000-01-31 10:20:00.000 +002") + assertClauseSerialize(t, table1ColTimestampz.LT(timestampz), "(table1.col_timestampz < $1)", "2000-01-31 10:20:05.000023 +200") } func TestTimestampzExpressionLT_EQ(t *testing.T) { assertClauseSerialize(t, table1ColTimestampz.LT_EQ(table2ColTimestampz), "(table1.col_timestampz <= table2.col_timestampz)") - assertClauseSerialize(t, table1ColTimestampz.LT_EQ(timestampz), "(table1.col_timestampz <= $1)", "2000-01-31 10:20:00.000 +002") + assertClauseSerialize(t, table1ColTimestampz.LT_EQ(timestampz), "(table1.col_timestampz <= $1)", "2000-01-31 10:20:05.000023 +200") } func TestTimestampzExpressionGT(t *testing.T) { assertClauseSerialize(t, table1ColTimestampz.GT(table2ColTimestampz), "(table1.col_timestampz > table2.col_timestampz)") - assertClauseSerialize(t, table1ColTimestampz.GT(timestampz), "(table1.col_timestampz > $1)", "2000-01-31 10:20:00.000 +002") + assertClauseSerialize(t, table1ColTimestampz.GT(timestampz), "(table1.col_timestampz > $1)", "2000-01-31 10:20:05.000023 +200") } func TestTimestampzExpressionGT_EQ(t *testing.T) { assertClauseSerialize(t, table1ColTimestampz.GT_EQ(table2ColTimestampz), "(table1.col_timestampz >= table2.col_timestampz)") - assertClauseSerialize(t, table1ColTimestampz.GT_EQ(timestampz), "(table1.col_timestampz >= $1)", "2000-01-31 10:20:00.000 +002") + assertClauseSerialize(t, table1ColTimestampz.GT_EQ(timestampz), "(table1.col_timestampz >= $1)", "2000-01-31 10:20:05.000023 +200") } func TestTimestampzExp(t *testing.T) { assertClauseSerialize(t, TimestampzExp(table1ColFloat), "table1.col_float") assertClauseSerialize(t, TimestampzExp(table1ColFloat).LT(timestampz), - "(table1.col_float < $1)", "2000-01-31 10:20:00.000 +002") + "(table1.col_float < $1)", "2000-01-31 10:20:05.000023 +200") } diff --git a/internal/jet/timez_expression_test.go b/internal/jet/timez_expression_test.go index 23aef52..2a0312a 100644 --- a/internal/jet/timez_expression_test.go +++ b/internal/jet/timez_expression_test.go @@ -2,50 +2,50 @@ package jet import "testing" -var timezVar = Timez(10, 20, 0, 0, 4) +var timezVar = Timez(10, 20, 0, 0, "+4:00") func TestTimezExpressionEQ(t *testing.T) { assertClauseSerialize(t, table1ColTimez.EQ(table2ColTimez), "(table1.col_timez = table2.col_timez)") - assertClauseSerialize(t, table1ColTimez.EQ(timezVar), "(table1.col_timez = $1)", "10:20:00.000 +04") + assertClauseSerialize(t, table1ColTimez.EQ(timezVar), "(table1.col_timez = $1)", "10:20:00 +4:00") } func TestTimezExpressionNOT_EQ(t *testing.T) { assertClauseSerialize(t, table1ColTimez.NOT_EQ(table2ColTimez), "(table1.col_timez != table2.col_timez)") - assertClauseSerialize(t, table1ColTimez.NOT_EQ(timezVar), "(table1.col_timez != $1)", "10:20:00.000 +04") + assertClauseSerialize(t, table1ColTimez.NOT_EQ(timezVar), "(table1.col_timez != $1)", "10:20:00 +4:00") } func TestTimezExpressionIS_DISTINCT_FROM(t *testing.T) { assertClauseSerialize(t, table1ColTimez.IS_DISTINCT_FROM(table2ColTimez), "(table1.col_timez IS DISTINCT FROM table2.col_timez)") - assertClauseSerialize(t, table1ColTimez.IS_DISTINCT_FROM(timezVar), "(table1.col_timez IS DISTINCT FROM $1)", "10:20:00.000 +04") + assertClauseSerialize(t, table1ColTimez.IS_DISTINCT_FROM(timezVar), "(table1.col_timez IS DISTINCT FROM $1)", "10:20:00 +4:00") } func TestTimezExpressionIS_NOT_DISTINCT_FROM(t *testing.T) { assertClauseSerialize(t, table1ColTimez.IS_NOT_DISTINCT_FROM(table2ColTimez), "(table1.col_timez IS NOT DISTINCT FROM table2.col_timez)") - assertClauseSerialize(t, table1ColTimez.IS_NOT_DISTINCT_FROM(timezVar), "(table1.col_timez IS NOT DISTINCT FROM $1)", "10:20:00.000 +04") + assertClauseSerialize(t, table1ColTimez.IS_NOT_DISTINCT_FROM(timezVar), "(table1.col_timez IS NOT DISTINCT FROM $1)", "10:20:00 +4:00") } func TestTimezExpressionLT(t *testing.T) { assertClauseSerialize(t, table1ColTimez.LT(table2ColTimez), "(table1.col_timez < table2.col_timez)") - assertClauseSerialize(t, table1ColTimez.LT(timezVar), "(table1.col_timez < $1)", "10:20:00.000 +04") + assertClauseSerialize(t, table1ColTimez.LT(timezVar), "(table1.col_timez < $1)", "10:20:00 +4:00") } func TestTimezExpressionLT_EQ(t *testing.T) { assertClauseSerialize(t, table1ColTimez.LT_EQ(table2ColTimez), "(table1.col_timez <= table2.col_timez)") - assertClauseSerialize(t, table1ColTimez.LT_EQ(timezVar), "(table1.col_timez <= $1)", "10:20:00.000 +04") + assertClauseSerialize(t, table1ColTimez.LT_EQ(timezVar), "(table1.col_timez <= $1)", "10:20:00 +4:00") } func TestTimezExpressionGT(t *testing.T) { assertClauseSerialize(t, table1ColTimez.GT(table2ColTimez), "(table1.col_timez > table2.col_timez)") - assertClauseSerialize(t, table1ColTimez.GT(timezVar), "(table1.col_timez > $1)", "10:20:00.000 +04") + assertClauseSerialize(t, table1ColTimez.GT(timezVar), "(table1.col_timez > $1)", "10:20:00 +4:00") } func TestTimezExpressionGT_EQ(t *testing.T) { assertClauseSerialize(t, table1ColTimez.GT_EQ(table2ColTimez), "(table1.col_timez >= table2.col_timez)") - assertClauseSerialize(t, table1ColTimez.GT_EQ(timezVar), "(table1.col_timez >= $1)", "10:20:00.000 +04") + assertClauseSerialize(t, table1ColTimez.GT_EQ(timezVar), "(table1.col_timez >= $1)", "10:20:00 +4:00") } func TestTimezExp(t *testing.T) { assertClauseSerialize(t, TimezExp(table1ColFloat), "table1.col_float") - assertClauseSerialize(t, TimezExp(table1ColFloat).LT(Timez(1, 1, 1, 1, 4)), - "(table1.col_float < $1)", string("01:01:01.001 +04")) + assertClauseSerialize(t, TimezExp(table1ColFloat).LT(Timez(1, 1, 1, 1, "+4:00")), + "(table1.col_float < $1)", string("01:01:01.000000001 +4:00")) } diff --git a/internal/testutils/test_utils.go b/internal/testutils/test_utils.go index fcf7655..59ee748 100644 --- a/internal/testutils/test_utils.go +++ b/internal/testutils/test_utils.go @@ -119,7 +119,11 @@ func AssertClauseSerialize(t *testing.T, dialect jet.Dialect, clause jet.Seriali //fmt.Println(out.Buff.String()) assert.DeepEqual(t, out.Buff.String(), query) - assert.DeepEqual(t, out.Args, args) + + if len(args) > 0 { + assert.DeepEqual(t, out.Args, args) + } + } func AssertClauseSerializeErr(t *testing.T, dialect jet.Dialect, clause jet.Serializer, errString string) { diff --git a/mysql/cast.go b/mysql/cast.go index c53005e..c4c056c 100644 --- a/mysql/cast.go +++ b/mysql/cast.go @@ -2,14 +2,27 @@ package mysql import ( "github.com/go-jet/jet/internal/jet" + "strconv" ) type cast interface { - jet.Cast - + // Cast expressions as castType type + AS(castType string) Expression + // Cast expression as char with optional length + AS_CHAR(lenght ...int) StringExpression + // Cast expression AS date type + AS_DATE() DateExpression + // Cast expression AS numeric type, using precision and optionally scale + AS_DECIMAL() FloatExpression + // Cast expression AS time type + AS_TIME() TimeExpression + // Cast expression as datetime type AS_DATETIME() DateTimeExpression + // Cast expressions as signed integer type AS_SIGNED() IntegerExpression + // Cast expression as unsigned integer type AS_UNSIGNED() IntegerExpression + // Cast expression as binary type AS_BINARY() StringExpression } @@ -25,6 +38,10 @@ func CAST(expr jet.Expression) cast { return castImpl } +func (c *castImpl) AS(castType string) Expression { + return c.Cast.AS(castType) +} + func (c *castImpl) AS_DATETIME() DateTimeExpression { return DateTimeExp(c.AS("DATETIME")) } @@ -37,6 +54,29 @@ func (c *castImpl) AS_UNSIGNED() IntegerExpression { return IntExp(c.AS("UNSIGNED")) } +func (b *castImpl) AS_CHAR(lenght ...int) StringExpression { + if len(lenght) > 0 { + return StringExp(b.AS("CHAR(" + strconv.Itoa(lenght[0]) + ")")) + } + + return StringExp(b.AS("CHAR")) +} + +// Cast expression AS date type +func (b *castImpl) AS_DATE() DateExpression { + return DateExp(b.AS("DATE")) +} + +// Cast expression AS date type +func (b *castImpl) AS_DECIMAL() FloatExpression { + return FloatExp(b.AS("DECIMAL")) +} + +// Cast expression AS date type +func (b *castImpl) AS_TIME() TimeExpression { + return TimeExp(b.AS("TIME")) +} + func (c *castImpl) AS_BINARY() StringExpression { return StringExp(c.AS("BINARY")) } diff --git a/mysql/cast_test.go b/mysql/cast_test.go index 26b6426..cc1a809 100644 --- a/mysql/cast_test.go +++ b/mysql/cast_test.go @@ -4,6 +4,15 @@ import ( "testing" ) -func TestCAST_AS_DATE(t *testing.T) { - assertClauseSerialize(t, CAST(Int(22)).AS_DATE(), `CAST(? AS DATE)`, int64(22)) +func TestCAST(t *testing.T) { + assertClauseSerialize(t, CAST(Float(11.22)).AS("bigint"), `CAST(? AS bigint)`) + assertClauseSerialize(t, CAST(Int(22)).AS_CHAR(), `CAST(? AS CHAR)`) + assertClauseSerialize(t, CAST(Int(22)).AS_CHAR(10), `CAST(? AS CHAR(10))`) + assertClauseSerialize(t, CAST(Int(22)).AS_DATE(), `CAST(? AS DATE)`) + assertClauseSerialize(t, CAST(Int(22)).AS_DECIMAL(), `CAST(? AS DECIMAL)`) + assertClauseSerialize(t, CAST(Int(22)).AS_TIME(), `CAST(? AS TIME)`) + assertClauseSerialize(t, CAST(Int(22)).AS_DATETIME(), `CAST(? AS DATETIME)`) + assertClauseSerialize(t, CAST(Int(22)).AS_SIGNED(), `CAST(? AS SIGNED)`) + assertClauseSerialize(t, CAST(Int(22)).AS_UNSIGNED(), `CAST(? AS UNSIGNED)`) + assertClauseSerialize(t, CAST(Int(22)).AS_BINARY(), `CAST(? AS BINARY)`) } diff --git a/mysql/literal.go b/mysql/literal.go index a2b98db..9cf7592 100644 --- a/mysql/literal.go +++ b/mysql/literal.go @@ -17,26 +17,23 @@ var String = jet.String var Date = func(year int, month time.Month, day int) DateExpression { return CAST(jet.Date(year, month, day)).AS_DATE() } - var DateT = func(t time.Time) DateExpression { return CAST(jet.DateT(t)).AS_DATE() } -var Time = func(hour, minute, second int, milliseconds ...int) TimeExpression { - return CAST(jet.Time(hour, minute, second, milliseconds...)).AS_TIME() +var Time = func(hour, minute, second int, nanoseconds ...time.Duration) TimeExpression { + return CAST(jet.Time(hour, minute, second, nanoseconds...)).AS_TIME() } - var TimeT = func(t time.Time) TimeExpression { return CAST(jet.TimeT(t)).AS_TIME() } -var DateTime = func(year int, month time.Month, day, hour, minute, second int, milliseconds ...int) DateTimeExpression { - return CAST(jet.Timestamp(year, month, day, hour, minute, second, milliseconds...)).AS_DATETIME() +var DateTime = func(year int, month time.Month, day, hour, minute, second int, nanoseconds ...time.Duration) DateTimeExpression { + return CAST(jet.Timestamp(year, month, day, hour, minute, second, nanoseconds...)).AS_DATETIME() } - var DateTimeT = func(t time.Time) DateTimeExpression { return CAST(jet.TimestampT(t)).AS_DATETIME() } -var Timestamp = func(year int, month time.Month, day, hour, minute, second int, milliseconds ...int) TimestampExpression { - return TIMESTAMP(StringExp(jet.Timestamp(year, month, day, hour, minute, second, milliseconds...))) +var Timestamp = func(year int, month time.Month, day, hour, minute, second int, nanoseconds ...time.Duration) TimestampExpression { + return TIMESTAMP(StringExp(jet.Timestamp(year, month, day, hour, minute, second, nanoseconds...))) } var TimestampT = func(t time.Time) TimestampExpression { return TIMESTAMP(StringExp(jet.TimestampT(t))) diff --git a/mysql/literal_test.go b/mysql/literal_test.go new file mode 100644 index 0000000..f677d11 --- /dev/null +++ b/mysql/literal_test.go @@ -0,0 +1,42 @@ +package mysql + +import ( + "testing" + "time" +) + +func TestBool(t *testing.T) { + assertClauseSerialize(t, Bool(false), `?`, false) +} + +func TestInt(t *testing.T) { + assertClauseSerialize(t, Int(11), `?`, int64(11)) +} + +func TestFloat(t *testing.T) { + assertClauseSerialize(t, Float(12.34), `?`, float64(12.34)) +} + +func TestString(t *testing.T) { + assertClauseSerialize(t, String("Some text"), `?`, "Some text") +} + +func TestDate(t *testing.T) { + assertClauseSerialize(t, Date(2014, time.January, 2), `CAST(? AS DATE)`, "2014-01-02") + assertClauseSerialize(t, DateT(time.Now()), `CAST(? AS DATE)`) +} + +func TestTime(t *testing.T) { + assertClauseSerialize(t, Time(10, 15, 30), `CAST(? AS TIME)`, "10:15:30") + assertClauseSerialize(t, TimeT(time.Now()), `CAST(? AS TIME)`) +} + +func TestDateTime(t *testing.T) { + assertClauseSerialize(t, DateTime(2010, time.March, 30, 10, 15, 30), `CAST(? AS DATETIME)`, "2010-03-30 10:15:30") + assertClauseSerialize(t, DateTimeT(time.Now()), `CAST(? AS DATETIME)`) +} + +func TestTimestamp(t *testing.T) { + assertClauseSerialize(t, Timestamp(2010, time.March, 30, 10, 15, 30), `TIMESTAMP(?)`, "2010-03-30 10:15:30") + assertClauseSerialize(t, TimestampT(time.Now()), `TIMESTAMP(?)`) +} diff --git a/postgres/cast.go b/postgres/cast.go index 747dd97..7c91cc1 100644 --- a/postgres/cast.go +++ b/postgres/cast.go @@ -3,13 +3,13 @@ package postgres import ( "fmt" "github.com/go-jet/jet/internal/jet" + "strconv" ) type cast interface { - jet.Cast + AS(castType string) Expression // Cast expression AS bool type AS_BOOL() BoolExpression - // Cast expression AS smallint type AS_SMALLINT() IntegerExpression // Cast expression AS integer type @@ -18,16 +18,22 @@ type cast interface { AS_BIGINT() IntegerExpression // Cast expression AS numeric type, using precision and optionally scale AS_NUMERIC(precisionAndScale ...int) FloatExpression - // Cast expression AS real type AS_REAL() FloatExpression // Cast expression AS double precision type AS_DOUBLE() FloatExpression + // Cast expression AS char with optional length + AS_CHAR(length ...int) StringExpression + // Cast expression AS date type + AS_DATE() DateExpression + // Cast expression AS numeric type, using precision and optionally scale + AS_DECIMAL() FloatExpression + // Cast expression AS time type + AS_TIME() TimeExpression // Cast expression AS text type AS_TEXT() StringExpression AS_BYTEA() StringExpression - // Cast expression AS time with time timezone type AS_TIMEZ() TimezExpression // Cast expression AS timestamp type @@ -48,22 +54,26 @@ func CAST(expr Expression) cast { return castImpl } +func (b *castImpl) AS(castType string) Expression { + return b.Cast.AS(castType) +} + func (b *castImpl) AS_BOOL() BoolExpression { - return jet.BoolExp(b.AS("boolean")) + return BoolExp(b.AS("boolean")) } func (b *castImpl) AS_SMALLINT() IntegerExpression { - return jet.IntExp(b.AS("smallint")) + return IntExp(b.AS("smallint")) } // Cast expression AS integer type func (b *castImpl) AS_INTEGER() IntegerExpression { - return jet.IntExp(b.AS("integer")) + return IntExp(b.AS("integer")) } // Cast expression AS bigint type func (b *castImpl) AS_BIGINT() IntegerExpression { - return jet.IntExp(b.AS("bigint")) + return IntExp(b.AS("bigint")) } // Cast expression AS numeric type, using precision and optionally scale @@ -77,45 +87,63 @@ func (b *castImpl) AS_NUMERIC(precisionAndScale ...int) FloatExpression { castArgs = fmt.Sprintf("(%d)", precisionAndScale[0]) } - return jet.FloatExp(b.AS("numeric" + castArgs)) + return FloatExp(b.AS("numeric" + castArgs)) } // Cast expression AS real type func (b *castImpl) AS_REAL() FloatExpression { - return jet.FloatExp(b.AS("real")) + return FloatExp(b.AS("real")) } // Cast expression AS double precision type func (b *castImpl) AS_DOUBLE() FloatExpression { - return jet.FloatExp(b.AS("double precision")) + return FloatExp(b.AS("double precision")) } // Cast expression AS text type func (b *castImpl) AS_TEXT() StringExpression { - return jet.StringExp(b.AS("text")) + return StringExp(b.AS("text")) +} + +func (b *castImpl) AS_CHAR(lenght ...int) StringExpression { + if len(lenght) > 0 { + return StringExp(b.AS("char(" + strconv.Itoa(lenght[0]) + ")")) + } + + return StringExp(b.AS("char")) +} + +// Cast expression AS date type +func (b *castImpl) AS_DATE() DateExpression { + return DateExp(b.AS("date")) +} + +// Cast expression AS date type +func (b *castImpl) AS_DECIMAL() FloatExpression { + return FloatExp(b.AS("decimal")) } // Cast expression AS text type func (b *castImpl) AS_BYTEA() StringExpression { - return jet.StringExp(b.AS("bytea")) + return StringExp(b.AS("bytea")) } // Cast expression AS date type -func (b *castImpl) AS_TIME() jet.TimeExpression { +func (b *castImpl) AS_TIME() TimeExpression { return TimeExp(b.AS("time without time zone")) } // Cast expression AS time with time timezone type func (b *castImpl) AS_TIMEZ() TimezExpression { - return jet.TimezExp(b.AS("time with time zone")) + return TimezExp(b.AS("time with time zone")) } // Cast expression AS timestamp type func (b *castImpl) AS_TIMESTAMP() TimestampExpression { - return jet.TimestampExp(b.AS("timestamp without time zone")) + return TimestampExp(b.AS("timestamp without time zone")) } // Cast expression AS timestamp with timezone type func (b *castImpl) AS_TIMESTAMPZ() TimestampzExpression { - return jet.TimestampzExp(b.AS("timestamp with time zone")) + return TimestampzExp(b.AS("timestamp with time zone")) } diff --git a/postgres/cast_test.go b/postgres/cast_test.go index 297e327..4537784 100644 --- a/postgres/cast_test.go +++ b/postgres/cast_test.go @@ -44,7 +44,7 @@ func TestExpressionCAST_AS_TEXT(t *testing.T) { } func TestExpressionCAST_AS_DATE(t *testing.T) { - assertClauseSerialize(t, CAST(table2Col3).AS_DATE(), "table2.col3::DATE") + assertClauseSerialize(t, CAST(table2Col3).AS_DATE(), "table2.col3::date") } func TestExpressionCAST_AS_TIME(t *testing.T) { diff --git a/postgres/expressions.go b/postgres/expressions.go index df6916e..0333a0d 100644 --- a/postgres/expressions.go +++ b/postgres/expressions.go @@ -26,6 +26,7 @@ var BoolExp = jet.BoolExp var IntExp = jet.IntExp var FloatExp = jet.FloatExp var TimeExp = jet.TimeExp +var StringExp = jet.StringExp var TimezExp = jet.TimezExp var DateExp = jet.DateExp var TimestampExp = jet.TimestampExp diff --git a/postgres/literal.go b/postgres/literal.go index b64c24c..4defa62 100644 --- a/postgres/literal.go +++ b/postgres/literal.go @@ -13,43 +13,33 @@ var String = jet.String var Bytea = func(value string) StringExpression { return CAST(jet.String(value)).AS_BYTEA() } - var Date = func(year int, month time.Month, day int) DateExpression { return CAST(jet.Date(year, month, day)).AS_DATE() } - var DateT = func(t time.Time) DateExpression { return CAST(jet.DateT(t)).AS_DATE() } - -var Time = func(hour, minute, second int, milliseconds ...int) TimeExpression { - return CAST(jet.Time(hour, minute, second, milliseconds...)).AS_TIME() +var Time = func(hour, minute, second int, nanoseconds ...time.Duration) TimeExpression { + return CAST(jet.Time(hour, minute, second, nanoseconds...)).AS_TIME() } - var TimeT = func(t time.Time) TimeExpression { return CAST(jet.TimeT(t)).AS_TIME() } - -var Timez = func(hour, minute, second, milliseconds int, timezone int) TimezExpression { +var Timez = func(hour, minute, second int, milliseconds time.Duration, timezone string) TimezExpression { return CAST(jet.Timez(hour, minute, second, milliseconds, timezone)).AS_TIMEZ() } - var TimezT = func(t time.Time) TimezExpression { return CAST(jet.TimezT(t)).AS_TIMEZ() } - -var Timestamp = func(year int, month time.Month, day, hour, minute, second, milliseconds int) TimestampExpression { - return CAST(jet.Timestamp(year, month, day, hour, minute, second, milliseconds)).AS_TIMESTAMP() +var Timestamp = func(year int, month time.Month, day, hour, minute, second int, milliseconds ...time.Duration) TimestampExpression { + return CAST(jet.Timestamp(year, month, day, hour, minute, second, milliseconds...)).AS_TIMESTAMP() } - var TimestampT = func(t time.Time) TimestampExpression { return CAST(jet.TimestampzT(t)).AS_TIMESTAMP() } - -var Timestampz = func(year, month, day, hour, minute, second, milliseconds int, timezone int) TimestampzExpression { +var Timestampz = func(year int, month time.Month, day, hour, minute, second int, milliseconds time.Duration, timezone string) TimestampzExpression { return CAST(jet.Timestampz(year, month, day, hour, minute, second, milliseconds, timezone)).AS_TIMESTAMPZ() } - var TimestampzT = func(t time.Time) TimestampzExpression { return CAST(jet.TimestampzT(t)).AS_TIMESTAMPZ() } diff --git a/postgres/literal_test.go b/postgres/literal_test.go index 89f7601..f30ef01 100644 --- a/postgres/literal_test.go +++ b/postgres/literal_test.go @@ -1,7 +1,50 @@ package postgres -import "testing" +import ( + "testing" + "time" +) -func TestDateLiteral(t *testing.T) { - assertClauseSerialize(t, Date(2019, 8, 6), "$1::DATE", "2019-08-06") +func TestBool(t *testing.T) { + assertClauseSerialize(t, Bool(false), `$1`, false) +} + +func TestInt(t *testing.T) { + assertClauseSerialize(t, Int(11), `$1`, int64(11)) +} + +func TestFloat(t *testing.T) { + assertClauseSerialize(t, Float(12.34), `$1`, float64(12.34)) +} + +func TestString(t *testing.T) { + assertClauseSerialize(t, String("Some text"), `$1`, "Some text") +} + +func TestDate(t *testing.T) { + assertClauseSerialize(t, Date(2014, time.January, 2), `$1::date`, "2014-01-02") + assertClauseSerialize(t, DateT(time.Now()), `$1::date`) +} + +func TestTime(t *testing.T) { + assertClauseSerialize(t, Time(10, 15, 30), `$1::time without time zone`, "10:15:30") + assertClauseSerialize(t, TimeT(time.Now()), `$1::time without time zone`) +} + +func TestTimez(t *testing.T) { + assertClauseSerialize(t, Timez(10, 15, 30, 0, "UTC"), + `$1::time with time zone`, "10:15:30 UTC") + assertClauseSerialize(t, TimezT(time.Now()), `$1::time with time zone`) +} + +func TestTimestamp(t *testing.T) { + assertClauseSerialize(t, Timestamp(2010, time.March, 30, 10, 15, 30), + `$1::timestamp without time zone`, "2010-03-30 10:15:30") + assertClauseSerialize(t, TimestampT(time.Now()), `$1::timestamp without time zone`) +} + +func TestTimestampz(t *testing.T) { + assertClauseSerialize(t, Timestampz(2010, time.March, 30, 10, 15, 30, 0, "UTC"), + `$1::timestamp with time zone`, "2010-03-30 10:15:30 UTC") + assertClauseSerialize(t, TimestampzT(time.Now()), `$1::timestamp with time zone`) } diff --git a/tests/mysql/alltypes_test.go b/tests/mysql/alltypes_test.go index c305c26..6e15330 100644 --- a/tests/mysql/alltypes_test.go +++ b/tests/mysql/alltypes_test.go @@ -1,6 +1,7 @@ package mysql import ( + "fmt" "github.com/go-jet/jet/internal/testutils" "github.com/go-jet/jet/tests/.gentestdata/mysql/test_sample/model" . "github.com/go-jet/jet/tests/.gentestdata/mysql/test_sample/table" @@ -494,8 +495,8 @@ func TestTimeExpressions(t *testing.T) { AllTypes.Time.EQ(AllTypes.Time), AllTypes.Time.EQ(Time(23, 6, 6)), - AllTypes.Time.EQ(Time(22, 6, 6, 11)), - AllTypes.Time.EQ(Time(21, 6, 6, 11111)), + AllTypes.Time.EQ(Time(22, 6, 6, 11*time.Millisecond)), + AllTypes.Time.EQ(Time(21, 6, 6, 11111*time.Microsecond)), AllTypes.TimePtr.NOT_EQ(AllTypes.Time), AllTypes.TimePtr.NOT_EQ(Time(20, 16, 6)), @@ -547,7 +548,7 @@ SELECT CAST(? AS TIME), CURRENT_TIME, CURRENT_TIME(3) FROM test_sample.all_types; -`, "20:34:58", "23:06:06", "22:06:06.011", "21:06:06.11111", "20:16:06", +`, "20:34:58", "23:06:06", "22:06:06.011", "21:06:06.011111", "20:16:06", "19:26:06", "18:36:06", "17:46:06", "16:56:56", "15:16:46", "14:26:36") err := query.Query(db, &struct{}{}) @@ -624,7 +625,7 @@ func TestDateTimeExpressions(t *testing.T) { AllTypes.DateTime.EQ(dateTime), AllTypes.DateTimePtr.NOT_EQ(AllTypes.DateTime), - AllTypes.DateTimePtr.NOT_EQ(DateTime(2019, 6, 6, 10, 2, 46, 1000)), + AllTypes.DateTimePtr.NOT_EQ(DateTime(2019, 6, 6, 10, 2, 46, 100*time.Millisecond)), AllTypes.DateTime.IS_DISTINCT_FROM(AllTypes.DateTime), AllTypes.DateTime.IS_DISTINCT_FROM(dateTime), @@ -654,7 +655,7 @@ func TestDateTimeExpressions(t *testing.T) { SELECT all_types.date_time = all_types.date_time, all_types.date_time = CAST('2019-06-06 10:02:46' AS DATETIME), all_types.date_time_ptr != all_types.date_time, - all_types.date_time_ptr != CAST('2019-06-06 10:02:46.1000' AS DATETIME), + all_types.date_time_ptr != CAST('2019-06-06 10:02:46.100' AS DATETIME), NOT(all_types.date_time <=> all_types.date_time), NOT(all_types.date_time <=> CAST('2019-06-06 10:02:46' AS DATETIME)), all_types.date_time <=> all_types.date_time, @@ -686,7 +687,7 @@ func TestTimestampExpressions(t *testing.T) { AllTypes.Timestamp.EQ(timestamp), AllTypes.TimestampPtr.NOT_EQ(AllTypes.Timestamp), - AllTypes.TimestampPtr.NOT_EQ(Timestamp(2019, 6, 6, 10, 2, 46, 1000)), + AllTypes.TimestampPtr.NOT_EQ(Timestamp(2019, 6, 6, 10, 2, 46, 100*time.Millisecond)), AllTypes.Timestamp.IS_DISTINCT_FROM(AllTypes.Timestamp), AllTypes.Timestamp.IS_DISTINCT_FROM(timestamp), @@ -716,7 +717,7 @@ func TestTimestampExpressions(t *testing.T) { SELECT all_types.timestamp = all_types.timestamp, all_types.timestamp = TIMESTAMP('2019-06-06 10:02:46'), all_types.timestamp_ptr != all_types.timestamp, - all_types.timestamp_ptr != TIMESTAMP('2019-06-06 10:02:46.1000'), + all_types.timestamp_ptr != TIMESTAMP('2019-06-06 10:02:46.100'), NOT(all_types.timestamp <=> all_types.timestamp), NOT(all_types.timestamp <=> TIMESTAMP('2019-06-06 10:02:46')), all_types.timestamp <=> all_types.timestamp, @@ -751,11 +752,11 @@ func TestTimeLiterals(t *testing.T) { Time(timeT.Clock()).AS("time"), TimeT(timeT).AS("timeT"), DateTimeT(timeT).AS("datetime"), - Timestamp(2019, 8, 6, 10, 10, 30, 123456).AS("timestamp"), + Timestamp(2019, 8, 6, 10, 10, 30, 123456*time.Millisecond).AS("timestamp"), TimestampT(timeT).AS("timestampT"), ).FROM(AllTypes).LIMIT(1) - //fmt.Println(query.Sql()) + fmt.Println(query.DebugSql()) testutils.AssertStatementSql(t, query, ` SELECT CAST(? AS DATE) AS "date", diff --git a/tests/postgres/alltypes_test.go b/tests/postgres/alltypes_test.go index 5c9111f..e530c31 100644 --- a/tests/postgres/alltypes_test.go +++ b/tests/postgres/alltypes_test.go @@ -384,7 +384,7 @@ SELECT (all_types.numeric = all_types.numeric) AS "eq1", TRUNC(ABS(all_types.decimal), $29) AS "abs", TRUNC(POWER(all_types.decimal, $30), $31) AS "power", TRUNC(SQRT(all_types.decimal), $32) AS "sqrt", - TRUNC(CBRT(all_types.decimal)::DECIMAL, $33) AS "cbrt", + TRUNC(CBRT(all_types.decimal)::decimal, $33) AS "cbrt", CEIL(all_types.real) AS "ceil", FLOOR(all_types.real) AS "floor", ROUND(all_types.decimal) AS "round1", @@ -552,22 +552,22 @@ func TestTimeExpression(t *testing.T) { 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.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.Timestampz.EQ(Timestampz(2010, 10, 21, 15, 30, 12, 444, "PST")), 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.Timez.NOT_EQ(Timez(23, 6, 6, 555, "+4:00")), 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.Timestampz.NOT_EQ(Timestampz(2010, 10, 21, 15, 30, 12, 777, "UTC")), AllTypes.Date.NOT_EQ(AllTypes.DatePtr), AllTypes.Date.NOT_EQ(Date(2010, 12, 3)), @@ -803,7 +803,7 @@ func TestTimeLiterals(t *testing.T) { //fmt.Println(query.Sql()) testutils.AssertStatementSql(t, query, ` -SELECT $1::DATE AS "date", +SELECT $1::date AS "date", $2::time without time zone AS "time", $3::time with time zone AS "timez", $4::timestamp without time zone AS "timestamp", diff --git a/tests/postgres/select_test.go b/tests/postgres/select_test.go index 833a7b5..066f64c 100644 --- a/tests/postgres/select_test.go +++ b/tests/postgres/select_test.go @@ -10,6 +10,7 @@ import ( . "github.com/go-jet/jet/tests/.gentestdata/jetdb/dvds/table" "gotest.tools/assert" "testing" + "time" ) func TestSelect_ScanToStruct(t *testing.T) { @@ -1185,15 +1186,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.000'::timestamp without time zone +WHERE payment.payment_date < '2007-02-14 22:16:01'::timestamp without time zone ORDER BY payment.payment_date ASC; ` query := Payment.SELECT(Payment.AllColumns). - WHERE(Payment.PaymentDate.LT(Timestamp(2007, 02, 14, 22, 16, 01, 0))). + WHERE(Payment.PaymentDate.LT(Timestamp(2007, time.February, 14, 22, 16, 01, 0))). ORDER_BY(Payment.PaymentDate.ASC()) - testutils.AssertDebugStatementSql(t, query, expectedSQL, "2007-02-14 22:16:01.000") + testutils.AssertDebugStatementSql(t, query, expectedSQL, "2007-02-14 22:16:01") payments := []model.Payment{}