Range types implemented
plus and minus infinity keyword tests implemented range table tests added skip cockroach db added select test case added for range fields generator modified to generate correct types generator tests modified to include sample range table model and template generators modified to support range fields returning the T in UPPER and LOWER functions raw ranges implemented bounds set as optional dep modified dependencies modified and issue fixed range expression with templates implemented rangeExpression change to make it more type safe third parameter of constructor function fixed literals removed, functions added tests modified constructor functions used for creating range expressions NumRange converted to a constructor function from literal range_lower and range_upper renamed to lower_bound and upper_bound range literal removed PlusInfinity and MinusInfinity implemented int4 and int8 castings added issues fixed and tests checked number, ts, tstz literal and cast implemented date range literal expression modified and raw function used parent type converted from RangeExpression to Expression range type implemented for postgres range column type, function and literal expression implemented CONTAINS and OVERLAP operations added for range expressions range expressions implemented
This commit is contained in:
parent
a9cbf94d68
commit
893567daca
20 changed files with 1062 additions and 19 deletions
|
|
@ -358,3 +358,44 @@ func DateColumn(name string) ColumnDate {
|
|||
dateColumn.ColumnExpressionImpl = NewColumnImpl(name, "", dateColumn)
|
||||
return dateColumn
|
||||
}
|
||||
|
||||
//------------------------------------------------------//
|
||||
|
||||
// ColumnRange is interface for range columns which can be int range, string range
|
||||
// timestamp range or date range.
|
||||
type ColumnRange[T Expression] interface {
|
||||
Range[T]
|
||||
Column
|
||||
|
||||
From(subQuery SelectTable) ColumnRange[T]
|
||||
SET(rangeExp Range[T]) ColumnAssigment
|
||||
}
|
||||
|
||||
type rangeColumnImpl[T Expression] struct {
|
||||
rangeInterfaceImpl[T]
|
||||
ColumnExpressionImpl
|
||||
}
|
||||
|
||||
func (i *rangeColumnImpl[T]) From(subQuery SelectTable) ColumnRange[T] {
|
||||
newRangeColumn := RangeColumn[T](i.name)
|
||||
newRangeColumn.setTableName(i.tableName)
|
||||
newRangeColumn.setSubQuery(subQuery)
|
||||
|
||||
return newRangeColumn
|
||||
}
|
||||
|
||||
func (i *rangeColumnImpl[T]) SET(rangeExp Range[T]) ColumnAssigment {
|
||||
return columnAssigmentImpl{
|
||||
column: i,
|
||||
expression: rangeExp,
|
||||
}
|
||||
}
|
||||
|
||||
// RangeColumn creates named range column.
|
||||
func RangeColumn[T Expression](name string) ColumnRange[T] {
|
||||
rangeColumn := &rangeColumnImpl[T]{}
|
||||
rangeColumn.rangeInterfaceImpl.parent = rangeColumn
|
||||
rangeColumn.ColumnExpressionImpl = NewColumnImpl(name, "", rangeColumn)
|
||||
|
||||
return rangeColumn
|
||||
}
|
||||
|
|
|
|||
|
|
@ -468,6 +468,33 @@ func REGEXP_LIKE(stringExp StringExpression, pattern StringExpression, matchType
|
|||
return newBoolFunc("REGEXP_LIKE", stringExp, pattern)
|
||||
}
|
||||
|
||||
//----------Range Type Functions ----------------------//
|
||||
|
||||
// LOWER_BOUND returns range expressions lower bound
|
||||
func LOWER_BOUND[T Expression](rangeExpression Range[T]) T {
|
||||
return rangeTypeCaster[T](rangeExpression, NewFunc("LOWER", []Expression{rangeExpression}, nil))
|
||||
}
|
||||
|
||||
// UPPER_BOUND returns range expressions upper bound
|
||||
func UPPER_BOUND[T Expression](rangeExpression Range[T]) T {
|
||||
return rangeTypeCaster[T](rangeExpression, NewFunc("UPPER", []Expression{rangeExpression}, nil))
|
||||
}
|
||||
|
||||
func rangeTypeCaster[T Expression](rangeExpression Range[T], exp Expression) T {
|
||||
var i Expression
|
||||
switch rangeExpression.(type) {
|
||||
case Range[DateExpression]:
|
||||
i = DateExp(exp)
|
||||
case Range[IntegerExpression]:
|
||||
i = IntExp(exp)
|
||||
case Range[TimestampzExpression]:
|
||||
i = TimestampzExp(exp)
|
||||
case Range[TimestampExpression]:
|
||||
i = TimestampExp(exp)
|
||||
}
|
||||
return i.(T)
|
||||
}
|
||||
|
||||
//----------Data Type Formatting Functions ----------------------//
|
||||
|
||||
// TO_CHAR converts expression to string with format
|
||||
|
|
@ -843,3 +870,35 @@ func newTimestampzFunc(name string, expressions ...Expression) *timestampzFunc {
|
|||
func Func(name string, expressions ...Expression) Expression {
|
||||
return NewFunc(name, expressions, nil)
|
||||
}
|
||||
|
||||
func NumRange(lowNum, highNum NumericExpression, bounds ...StringExpression) Range[NumericExpression] {
|
||||
return RangeExp[NumericExpression](NewFunc("numrange", rangeFuncParamCombiner[NumericExpression](lowNum, highNum, bounds...), nil))
|
||||
}
|
||||
|
||||
func Int4Range(lowNum, highNum IntegerExpression, bounds ...StringExpression) Range[IntegerExpression] {
|
||||
return RangeExp[IntegerExpression](NewFunc("int4range", rangeFuncParamCombiner[IntegerExpression](lowNum, highNum, bounds...), nil))
|
||||
}
|
||||
|
||||
func Int8Range(lowNum, highNum IntegerExpression, bounds ...StringExpression) Range[IntegerExpression] {
|
||||
return RangeExp[IntegerExpression](NewFunc("int8range", rangeFuncParamCombiner[IntegerExpression](lowNum, highNum, bounds...), nil))
|
||||
}
|
||||
|
||||
func TimestampRange(lowTs, highTs TimestampExpression, bounds ...StringExpression) Range[TimestampExpression] {
|
||||
return RangeExp[TimestampExpression](NewFunc("tsrange", rangeFuncParamCombiner[TimestampExpression](lowTs, highTs, bounds...), nil))
|
||||
}
|
||||
|
||||
func TimestampzRange(lowTs, highTs TimestampzExpression, bounds ...StringExpression) Range[TimestampzExpression] {
|
||||
return RangeExp[TimestampzExpression](NewFunc("tstzrange", rangeFuncParamCombiner[TimestampzExpression](lowTs, highTs, bounds...), nil))
|
||||
}
|
||||
|
||||
func DateRange(lowTs, highTs DateExpression, bounds ...StringExpression) Range[DateExpression] {
|
||||
return RangeExp[DateExpression](NewFunc("daterange", rangeFuncParamCombiner[DateExpression](lowTs, highTs, bounds...), nil))
|
||||
}
|
||||
|
||||
func rangeFuncParamCombiner[T Expression](low, high T, bounds ...StringExpression) []Expression {
|
||||
exp := []Expression{low, high}
|
||||
if len(bounds) != 0 {
|
||||
exp = append(exp, bounds[0])
|
||||
}
|
||||
return exp
|
||||
}
|
||||
|
|
|
|||
|
|
@ -202,3 +202,14 @@ func TestTO_ASCII(t *testing.T) {
|
|||
func TestFunc(t *testing.T) {
|
||||
assertClauseSerialize(t, Func("FOO", String("test"), NULL, MAX(Int(1))), "FOO($1, NULL, MAX($2))", "test", int64(1))
|
||||
}
|
||||
|
||||
func Test_rangePointCaster(t *testing.T) {
|
||||
mainRange := Int8Range(Int8(10), Int8(12))
|
||||
exp := NewFunc("UPPER", []Expression{mainRange}, nil)
|
||||
|
||||
got := rangeTypeCaster(mainRange, exp)
|
||||
_, ok := got.(IntegerExpression)
|
||||
if !ok {
|
||||
t.Errorf("expecting to get IntegerExpression but got %v", got)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -333,6 +333,10 @@ var (
|
|||
NULL = newNullLiteral()
|
||||
// STAR is jet equivalent of SQL *
|
||||
STAR = newStarLiteral()
|
||||
// PLUS_INFINITY is jet equivalent for sql infinity
|
||||
PLUS_INFINITY = String("infinity")
|
||||
// MINUS_INFINITY is jet equivalent for sql -infinity
|
||||
MINUS_INFINITY = String("-infinity")
|
||||
)
|
||||
|
||||
type nullLiteral struct {
|
||||
|
|
@ -490,6 +494,11 @@ func RawDate(raw string, namedArgs ...map[string]interface{}) DateExpression {
|
|||
return DateExp(Raw(raw, namedArgs...))
|
||||
}
|
||||
|
||||
// RawRange helper that for range expressions
|
||||
func RawRange[T Expression](raw string, namedArgs ...map[string]interface{}) Range[T] {
|
||||
return RangeExp[T](Raw(raw, namedArgs...))
|
||||
}
|
||||
|
||||
// UUID is a helper function to create string literal expression from uuid object
|
||||
// value can be any uuid type with a String method
|
||||
func UUID(value fmt.Stringer) StringExpression {
|
||||
|
|
|
|||
|
|
@ -69,6 +69,16 @@ func GtEq(lhs, rhs Expression) BoolExpression {
|
|||
return newBinaryBoolOperatorExpression(lhs, rhs, ">=")
|
||||
}
|
||||
|
||||
// Contains returns a representation of "a @> b"
|
||||
func Contains(lhs Expression, rhs Expression) BoolExpression {
|
||||
return newBinaryBoolOperatorExpression(lhs, rhs, "@>")
|
||||
}
|
||||
|
||||
// Overlap returns a representation of "a && b"
|
||||
func Overlap(lhs, rhs Expression) BoolExpression {
|
||||
return newBinaryBoolOperatorExpression(lhs, rhs, "&&")
|
||||
}
|
||||
|
||||
// Add notEq returns a representation of "a + b"
|
||||
func Add(lhs, rhs Serializer) Expression {
|
||||
return NewBinaryOperatorExpression(lhs, rhs, "+")
|
||||
|
|
|
|||
95
internal/jet/range_expression.go
Normal file
95
internal/jet/range_expression.go
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
package jet
|
||||
|
||||
// Range Expression is interface for date range types
|
||||
type Range[T Expression] interface {
|
||||
Expression
|
||||
|
||||
EQ(rhs Range[T]) BoolExpression
|
||||
NOT_EQ(rhs Range[T]) BoolExpression
|
||||
|
||||
LT(rhs Range[T]) BoolExpression
|
||||
LT_EQ(rhs Range[T]) BoolExpression
|
||||
GT(rhs Range[T]) BoolExpression
|
||||
GT_EQ(rhs Range[T]) BoolExpression
|
||||
|
||||
CONTAINS(rhs T) BoolExpression
|
||||
CONTAINS_RANGE(rhs Range[T]) BoolExpression
|
||||
OVERLAP(rhs Range[T]) BoolExpression
|
||||
UNION(rhs Range[T]) Range[T]
|
||||
INTERSECTION(rhs Range[T]) Range[T]
|
||||
DIFFERENCE(rhs Range[T]) Range[T]
|
||||
}
|
||||
|
||||
type rangeInterfaceImpl[T Expression] struct {
|
||||
parent Expression
|
||||
}
|
||||
|
||||
func (r *rangeInterfaceImpl[T]) EQ(rhs Range[T]) BoolExpression {
|
||||
return Eq(r.parent, rhs)
|
||||
}
|
||||
|
||||
func (r *rangeInterfaceImpl[T]) NOT_EQ(rhs Range[T]) BoolExpression {
|
||||
return NotEq(r.parent, rhs)
|
||||
}
|
||||
|
||||
func (r *rangeInterfaceImpl[T]) LT(rhs Range[T]) BoolExpression {
|
||||
return Lt(r.parent, rhs)
|
||||
}
|
||||
|
||||
func (r *rangeInterfaceImpl[T]) LT_EQ(rhs Range[T]) BoolExpression {
|
||||
return LtEq(r.parent, rhs)
|
||||
|
||||
}
|
||||
|
||||
func (r *rangeInterfaceImpl[T]) GT(rhs Range[T]) BoolExpression {
|
||||
return Gt(r.parent, rhs)
|
||||
|
||||
}
|
||||
|
||||
func (r *rangeInterfaceImpl[T]) GT_EQ(rhs Range[T]) BoolExpression {
|
||||
return GtEq(r.parent, rhs)
|
||||
}
|
||||
|
||||
func (r *rangeInterfaceImpl[T]) CONTAINS(rhs T) BoolExpression {
|
||||
return Contains(r.parent, rhs)
|
||||
}
|
||||
|
||||
func (r *rangeInterfaceImpl[T]) CONTAINS_RANGE(rhs Range[T]) BoolExpression {
|
||||
return Contains(r.parent, rhs)
|
||||
}
|
||||
|
||||
func (r *rangeInterfaceImpl[T]) OVERLAP(rhs Range[T]) BoolExpression {
|
||||
return Overlap(r.parent, rhs)
|
||||
}
|
||||
|
||||
func (r *rangeInterfaceImpl[T]) UNION(rhs Range[T]) Range[T] {
|
||||
return RangeExp[T](Add(r.parent, rhs))
|
||||
}
|
||||
|
||||
func (r *rangeInterfaceImpl[T]) INTERSECTION(rhs Range[T]) Range[T] {
|
||||
return RangeExp[T](Mul(r.parent, rhs))
|
||||
}
|
||||
|
||||
func (r *rangeInterfaceImpl[T]) DIFFERENCE(rhs Range[T]) Range[T] {
|
||||
return RangeExp[T](Sub(r.parent, rhs))
|
||||
}
|
||||
|
||||
//---------------------------------------------------//
|
||||
|
||||
type rangeExpressionWrapper[T Expression] struct {
|
||||
rangeInterfaceImpl[T]
|
||||
Expression
|
||||
}
|
||||
|
||||
func newRangeExpressionWrap[T Expression](expression Expression) Range[T] {
|
||||
rangeExpressionWrap := rangeExpressionWrapper[T]{Expression: expression}
|
||||
rangeExpressionWrap.rangeInterfaceImpl.parent = &rangeExpressionWrap
|
||||
return &rangeExpressionWrap
|
||||
}
|
||||
|
||||
// RangeExp is range expression wrapper around arbitrary expression.
|
||||
// Allows go compiler to see any expression as range expression.
|
||||
// Does not add sql cast to generated sql builder output.
|
||||
func RangeExp[T Expression](expression Expression) Range[T] {
|
||||
return newRangeExpressionWrap[T](expression)
|
||||
}
|
||||
63
internal/jet/range_expression_test.go
Normal file
63
internal/jet/range_expression_test.go
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
package jet
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestRangeExpressionEQ(t *testing.T) {
|
||||
assertClauseSerialize(t, table1ColRange.EQ(table2ColRange), "(table1.col_range = table2.col_range)")
|
||||
assertClauseSerialize(t, table1ColRange.EQ(Int8Range(Int8(1), Int8(4), String("[)"))), "(table1.col_range = int8range($1, $2, $3))", int8(1), int8(4), "[)")
|
||||
}
|
||||
|
||||
func TestRangeExpressionNOT_EQ(t *testing.T) {
|
||||
assertClauseSerialize(t, table1ColRange.NOT_EQ(table2ColRange), "(table1.col_range != table2.col_range)")
|
||||
assertClauseSerialize(t, table1ColRange.NOT_EQ(Int8Range(Int8(1), Int8(4), String("[)"))), "(table1.col_range != int8range($1, $2, $3))", int8(1), int8(4), "[)")
|
||||
}
|
||||
|
||||
func TestRangeExpressionLT(t *testing.T) {
|
||||
assertClauseSerialize(t, table1ColRange.LT(table2ColRange), "(table1.col_range < table2.col_range)")
|
||||
assertClauseSerialize(t, table1ColRange.LT(Int8Range(Int8(1), Int8(4), String("[)"))), "(table1.col_range < int8range($1, $2, $3))", int8(1), int8(4), "[)")
|
||||
}
|
||||
|
||||
func TestRangeExpressionLT_EQ(t *testing.T) {
|
||||
assertClauseSerialize(t, table1ColRange.LT_EQ(table2ColRange), "(table1.col_range <= table2.col_range)")
|
||||
assertClauseSerialize(t, table1ColRange.LT_EQ(Int8Range(Int8(1), Int8(4), String("[)"))), "(table1.col_range <= int8range($1, $2, $3))", int8(1), int8(4), "[)")
|
||||
}
|
||||
|
||||
func TestRangeExpressionGT(t *testing.T) {
|
||||
assertClauseSerialize(t, table1ColRange.GT(table2ColRange), "(table1.col_range > table2.col_range)")
|
||||
assertClauseSerialize(t, table1ColRange.GT(Int8Range(Int8(1), Int8(4), String("[)"))), "(table1.col_range > int8range($1, $2, $3))", int8(1), int8(4), "[)")
|
||||
}
|
||||
|
||||
func TestRangeExpressionGT_EQ(t *testing.T) {
|
||||
assertClauseSerialize(t, table1ColRange.GT_EQ(table2ColRange), "(table1.col_range >= table2.col_range)")
|
||||
assertClauseSerialize(t, table1ColRange.GT_EQ(Int8Range(Int8(1), Int8(4), String("[)"))), "(table1.col_range >= int8range($1, $2, $3))", int8(1), int8(4), "[)")
|
||||
}
|
||||
|
||||
func TestRangeExpressionCONTAINS_RANGE(t *testing.T) {
|
||||
assertClauseSerialize(t, table1ColRange.CONTAINS_RANGE(table2ColRange), "(table1.col_range @> table2.col_range)")
|
||||
assertClauseSerialize(t, table1ColRange.CONTAINS_RANGE(Int8Range(Int8(1), Int8(4), String("[)"))), "(table1.col_range @> int8range($1, $2, $3))", int8(1), int8(4), "[)")
|
||||
}
|
||||
|
||||
func TestRangeExpressionCONTAINS(t *testing.T) {
|
||||
assertClauseSerialize(t, table1ColRange.CONTAINS(table2Col3), "(table1.col_range @> table2.col3)")
|
||||
assertClauseSerialize(t, table1ColRange.CONTAINS(Int8(1)), "(table1.col_range @> $1)", int8(1))
|
||||
}
|
||||
|
||||
func TestRangeExpressionOVERLAP(t *testing.T) {
|
||||
assertClauseSerialize(t, table1ColRange.OVERLAP(table2ColRange), "(table1.col_range && table2.col_range)")
|
||||
assertClauseSerialize(t, table1ColRange.OVERLAP(Int8Range(Int8(1), Int8(4), String("[)"))), "(table1.col_range && int8range($1, $2, $3))", int8(1), int8(4), "[)")
|
||||
}
|
||||
|
||||
func TestRangeExpressionUNION(t *testing.T) {
|
||||
assertClauseSerialize(t, table1ColRange.UNION(table2ColRange), "(table1.col_range + table2.col_range)")
|
||||
assertClauseSerialize(t, table1ColRange.UNION(Int8Range(Int8(1), Int8(4), String("[)"))), "(table1.col_range + int8range($1, $2, $3))", int8(1), int8(4), "[)")
|
||||
}
|
||||
|
||||
func TestRangeExpressionINTERSECTION(t *testing.T) {
|
||||
assertClauseSerialize(t, table1ColRange.INTERSECTION(table2ColRange), "(table1.col_range * table2.col_range)")
|
||||
assertClauseSerialize(t, table1ColRange.INTERSECTION(Int8Range(Int8(1), Int8(4), String("[)"))), "(table1.col_range * int8range($1, $2, $3))", int8(1), int8(4), "[)")
|
||||
}
|
||||
|
||||
func TestRangeExpressionDIFFERENCE(t *testing.T) {
|
||||
assertClauseSerialize(t, table1ColRange.DIFFERENCE(table2ColRange), "(table1.col_range - table2.col_range)")
|
||||
assertClauseSerialize(t, table1ColRange.DIFFERENCE(Int8Range(Int8(1), Int8(4), String("[)"))), "(table1.col_range - int8range($1, $2, $3))", int8(1), int8(4), "[)")
|
||||
}
|
||||
|
|
@ -25,8 +25,9 @@ var (
|
|||
table1ColTimestampz = TimestampzColumn("col_timestampz")
|
||||
table1ColBool = BoolColumn("col_bool")
|
||||
table1ColDate = DateColumn("col_date")
|
||||
table1ColRange = RangeColumn[IntegerExpression]("col_range")
|
||||
)
|
||||
var table1 = NewTable("db", "table1", "", table1Col1, table1ColInt, table1ColFloat, table1Col3, table1ColTime, table1ColTimez, table1ColBool, table1ColDate, table1ColTimestamp, table1ColTimestampz)
|
||||
var table1 = NewTable("db", "table1", "", table1Col1, table1ColInt, table1ColFloat, table1Col3, table1ColTime, table1ColTimez, table1ColBool, table1ColDate, table1ColRange, table1ColTimestamp, table1ColTimestampz)
|
||||
|
||||
var (
|
||||
table2Col3 = IntegerColumn("col3")
|
||||
|
|
@ -40,8 +41,9 @@ var (
|
|||
table2ColTimestamp = TimestampColumn("col_timestamp")
|
||||
table2ColTimestampz = TimestampzColumn("col_timestampz")
|
||||
table2ColDate = DateColumn("col_date")
|
||||
table2ColRange = RangeColumn[IntegerExpression]("col_range")
|
||||
)
|
||||
var table2 = NewTable("db", "table2", "", table2Col3, table2Col4, table2ColInt, table2ColFloat, table2ColStr, table2ColBool, table2ColTime, table2ColTimez, table2ColDate, table2ColTimestamp, table2ColTimestampz)
|
||||
var table2 = NewTable("db", "table2", "", table2Col3, table2Col4, table2ColInt, table2ColFloat, table2ColStr, table2ColBool, table2ColTime, table2ColTimez, table2ColDate, table2ColRange, table2ColTimestamp, table2ColTimestampz)
|
||||
|
||||
var (
|
||||
table3Col1 = IntegerColumn("col1")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue