Add support for PostgreSQL interval column
This commit is contained in:
parent
641c62098c
commit
3013dc3647
30 changed files with 1038 additions and 255 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
20
postgres/columns_test.go
Normal file
20
postgres/columns_test.go
Normal file
|
|
@ -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"`)
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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)")
|
||||
}
|
||||
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue