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
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"github.com/go-jet/jet/v2/generator/metadata"
|
"github.com/go-jet/jet/v2/generator/metadata"
|
||||||
"github.com/go-jet/jet/v2/internal/utils/dbidentifier"
|
"github.com/go-jet/jet/v2/internal/utils/dbidentifier"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"github.com/jackc/pgtype"
|
||||||
"path"
|
"path"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -320,6 +321,18 @@ func toGoType(column metadata.Column) interface{} {
|
||||||
return float64(0.0)
|
return float64(0.0)
|
||||||
case "uuid":
|
case "uuid":
|
||||||
return uuid.UUID{}
|
return uuid.UUID{}
|
||||||
|
case "daterange":
|
||||||
|
return pgtype.Daterange{}
|
||||||
|
case "tsrange":
|
||||||
|
return pgtype.Tsrange{}
|
||||||
|
case "tstzrange":
|
||||||
|
return pgtype.Tstzrange{}
|
||||||
|
case "int4range":
|
||||||
|
return pgtype.Int4range{}
|
||||||
|
case "int8range":
|
||||||
|
return pgtype.Int8range{}
|
||||||
|
case "numrange":
|
||||||
|
return pgtype.Numrange{}
|
||||||
default:
|
default:
|
||||||
fmt.Println("- [Model ] Unsupported sql column '" + column.Name + " " + column.DataType.Name + "', using string instead.")
|
fmt.Println("- [Model ] Unsupported sql column '" + column.Name + " " + column.DataType.Name + "', using string instead.")
|
||||||
return ""
|
return ""
|
||||||
|
|
|
||||||
|
|
@ -177,6 +177,18 @@ func getSqlBuilderColumnType(columnMetaData metadata.Column) string {
|
||||||
case "real", "numeric", "decimal", "double precision", "float", "float4", "float8",
|
case "real", "numeric", "decimal", "double precision", "float", "float4", "float8",
|
||||||
"double": // MySQL
|
"double": // MySQL
|
||||||
return "Float"
|
return "Float"
|
||||||
|
case "daterange":
|
||||||
|
return "DateRange"
|
||||||
|
case "tsrange":
|
||||||
|
return "TimestampRange"
|
||||||
|
case "tstzrange":
|
||||||
|
return "TimestampzRange"
|
||||||
|
case "int4range":
|
||||||
|
return "Int4Range"
|
||||||
|
case "int8range":
|
||||||
|
return "Int8Range"
|
||||||
|
case "numrange":
|
||||||
|
return "NumericRange"
|
||||||
default:
|
default:
|
||||||
fmt.Println("- [SQL Builder] Unsupported sql column '" + columnMetaData.Name + " " + columnMetaData.DataType.Name + "', using StringColumn instead.")
|
fmt.Println("- [SQL Builder] Unsupported sql column '" + columnMetaData.Name + " " + columnMetaData.DataType.Name + "', using StringColumn instead.")
|
||||||
return "String"
|
return "String"
|
||||||
|
|
|
||||||
25
go.mod
25
go.mod
|
|
@ -1,6 +1,6 @@
|
||||||
module github.com/go-jet/jet/v2
|
module github.com/go-jet/jet/v2
|
||||||
|
|
||||||
go 1.11
|
go 1.21.6
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/go-sql-driver/mysql v1.7.1
|
github.com/go-sql-driver/mysql v1.7.1
|
||||||
|
|
@ -10,7 +10,6 @@ require (
|
||||||
github.com/mattn/go-sqlite3 v1.14.17
|
github.com/mattn/go-sqlite3 v1.14.17
|
||||||
)
|
)
|
||||||
|
|
||||||
// test dependencies
|
|
||||||
require (
|
require (
|
||||||
github.com/google/go-cmp v0.5.9
|
github.com/google/go-cmp v0.5.9
|
||||||
github.com/jackc/pgx/v4 v4.18.1
|
github.com/jackc/pgx/v4 v4.18.1
|
||||||
|
|
@ -21,3 +20,25 @@ require (
|
||||||
golang.org/x/sync v0.3.0
|
golang.org/x/sync v0.3.0
|
||||||
gopkg.in/guregu/null.v4 v4.0.0
|
gopkg.in/guregu/null.v4 v4.0.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/felixge/fgprof v0.9.3 // indirect
|
||||||
|
github.com/friendsofgo/errors v0.9.2 // indirect
|
||||||
|
github.com/gofrs/uuid v4.0.0+incompatible // indirect
|
||||||
|
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd // indirect
|
||||||
|
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||||
|
github.com/jackc/pgio v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgproto3/v2 v2.3.2 // indirect
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||||
|
github.com/jackc/pgtype v1.14.0 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/volatiletech/inflect v0.0.1 // indirect
|
||||||
|
github.com/volatiletech/randomize v0.0.1 // indirect
|
||||||
|
github.com/volatiletech/strmangle v0.0.1 // indirect
|
||||||
|
golang.org/x/crypto v0.6.0 // indirect
|
||||||
|
golang.org/x/text v0.7.0 // indirect
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
|
|
|
||||||
2
go.sum
2
go.sum
|
|
@ -32,7 +32,6 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
||||||
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
|
|
||||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||||
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||||
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||||
|
|
@ -54,7 +53,6 @@ github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5W
|
||||||
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
|
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
|
||||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
|
|
||||||
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
||||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
||||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
||||||
|
|
|
||||||
|
|
@ -358,3 +358,44 @@ func DateColumn(name string) ColumnDate {
|
||||||
dateColumn.ColumnExpressionImpl = NewColumnImpl(name, "", dateColumn)
|
dateColumn.ColumnExpressionImpl = NewColumnImpl(name, "", dateColumn)
|
||||||
return 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)
|
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 ----------------------//
|
//----------Data Type Formatting Functions ----------------------//
|
||||||
|
|
||||||
// TO_CHAR converts expression to string with format
|
// 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 {
|
func Func(name string, expressions ...Expression) Expression {
|
||||||
return NewFunc(name, expressions, nil)
|
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) {
|
func TestFunc(t *testing.T) {
|
||||||
assertClauseSerialize(t, Func("FOO", String("test"), NULL, MAX(Int(1))), "FOO($1, NULL, MAX($2))", "test", int64(1))
|
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()
|
NULL = newNullLiteral()
|
||||||
// STAR is jet equivalent of SQL *
|
// STAR is jet equivalent of SQL *
|
||||||
STAR = newStarLiteral()
|
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 {
|
type nullLiteral struct {
|
||||||
|
|
@ -490,6 +494,11 @@ func RawDate(raw string, namedArgs ...map[string]interface{}) DateExpression {
|
||||||
return DateExp(Raw(raw, namedArgs...))
|
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
|
// UUID is a helper function to create string literal expression from uuid object
|
||||||
// value can be any uuid type with a String method
|
// value can be any uuid type with a String method
|
||||||
func UUID(value fmt.Stringer) StringExpression {
|
func UUID(value fmt.Stringer) StringExpression {
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,16 @@ func GtEq(lhs, rhs Expression) BoolExpression {
|
||||||
return newBinaryBoolOperatorExpression(lhs, rhs, ">=")
|
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"
|
// Add notEq returns a representation of "a + b"
|
||||||
func Add(lhs, rhs Serializer) Expression {
|
func Add(lhs, rhs Serializer) Expression {
|
||||||
return NewBinaryOperatorExpression(lhs, rhs, "+")
|
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")
|
table1ColTimestampz = TimestampzColumn("col_timestampz")
|
||||||
table1ColBool = BoolColumn("col_bool")
|
table1ColBool = BoolColumn("col_bool")
|
||||||
table1ColDate = DateColumn("col_date")
|
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 (
|
var (
|
||||||
table2Col3 = IntegerColumn("col3")
|
table2Col3 = IntegerColumn("col3")
|
||||||
|
|
@ -40,8 +41,9 @@ var (
|
||||||
table2ColTimestamp = TimestampColumn("col_timestamp")
|
table2ColTimestamp = TimestampColumn("col_timestamp")
|
||||||
table2ColTimestampz = TimestampzColumn("col_timestampz")
|
table2ColTimestampz = TimestampzColumn("col_timestampz")
|
||||||
table2ColDate = DateColumn("col_date")
|
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 (
|
var (
|
||||||
table3Col1 = IntegerColumn("col1")
|
table3Col1 = IntegerColumn("col1")
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,42 @@ type ColumnTimestampz = jet.ColumnTimestampz
|
||||||
// TimestampzColumn creates named timestamp with time zone column.
|
// TimestampzColumn creates named timestamp with time zone column.
|
||||||
var TimestampzColumn = jet.TimestampzColumn
|
var TimestampzColumn = jet.TimestampzColumn
|
||||||
|
|
||||||
|
// ColumnDateRange is interface of SQL date range column
|
||||||
|
type ColumnDateRange = jet.ColumnRange[DateExpression]
|
||||||
|
|
||||||
|
// DateRangeColumn creates named range with range column
|
||||||
|
var DateRangeColumn = jet.RangeColumn[DateExpression]
|
||||||
|
|
||||||
|
// ColumnNumericRange is interface of SQL numeric range column
|
||||||
|
type ColumnNumericRange = jet.ColumnRange[NumericExpression]
|
||||||
|
|
||||||
|
// NumericRangeColumn creates named range with range column
|
||||||
|
var NumericRangeColumn = jet.RangeColumn[NumericExpression]
|
||||||
|
|
||||||
|
// ColumnTimestampRange is interface of SQL timestamp range column
|
||||||
|
type ColumnTimestampRange = jet.ColumnRange[TimestampExpression]
|
||||||
|
|
||||||
|
// TimestampRangeColumn creates named range with range column
|
||||||
|
var TimestampRangeColumn = jet.RangeColumn[TimestampExpression]
|
||||||
|
|
||||||
|
// ColumnTimestampzRange is interface of SQL timestamp range column
|
||||||
|
type ColumnTimestampzRange = jet.ColumnRange[TimestampzExpression]
|
||||||
|
|
||||||
|
// TimestampzRangeColumn creates named range with range column
|
||||||
|
var TimestampzRangeColumn = jet.RangeColumn[TimestampzExpression]
|
||||||
|
|
||||||
|
// ColumnInt4Range is interface of SQL int range column
|
||||||
|
type ColumnInt4Range = jet.ColumnRange[IntegerExpression]
|
||||||
|
|
||||||
|
// Int4RangeColumn creates named range with range column
|
||||||
|
var Int4RangeColumn = jet.RangeColumn[IntegerExpression]
|
||||||
|
|
||||||
|
// ColumnInt8Range is interface of SQL int range column
|
||||||
|
type ColumnInt8Range = jet.ColumnRange[IntegerExpression]
|
||||||
|
|
||||||
|
// Int8RangeColumn creates named range with range column
|
||||||
|
var Int8RangeColumn = jet.RangeColumn[IntegerExpression]
|
||||||
|
|
||||||
//------------------------------------------------------//
|
//------------------------------------------------------//
|
||||||
|
|
||||||
// ColumnInterval is interface of PostgreSQL interval columns.
|
// ColumnInterval is interface of PostgreSQL interval columns.
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,24 @@ type TimestampExpression = jet.TimestampExpression
|
||||||
// TimestampzExpression interface
|
// TimestampzExpression interface
|
||||||
type TimestampzExpression = jet.TimestampzExpression
|
type TimestampzExpression = jet.TimestampzExpression
|
||||||
|
|
||||||
|
// DateRange Expression interface
|
||||||
|
type DateRange = jet.Range[DateExpression]
|
||||||
|
|
||||||
|
// TimestampRange Expression interface
|
||||||
|
type TimestampRange = jet.Range[TimestampExpression]
|
||||||
|
|
||||||
|
// TimestampzRange Expression interface
|
||||||
|
type TimestampzRange = jet.Range[TimestampzExpression]
|
||||||
|
|
||||||
|
// NumericRange Expression interface
|
||||||
|
type NumericRange = jet.Range[NumericExpression]
|
||||||
|
|
||||||
|
// Int4Range Expression interface
|
||||||
|
type Int4Range = jet.Range[IntegerExpression]
|
||||||
|
|
||||||
|
// Int8Range Expression interface
|
||||||
|
type Int8Range = jet.Range[IntegerExpression]
|
||||||
|
|
||||||
// BoolExp is bool expression wrapper around arbitrary expression.
|
// BoolExp is bool expression wrapper around arbitrary expression.
|
||||||
// Allows go compiler to see any expression as bool expression.
|
// Allows go compiler to see any expression as bool expression.
|
||||||
// Does not add sql cast to generated sql builder output.
|
// Does not add sql cast to generated sql builder output.
|
||||||
|
|
@ -81,6 +99,13 @@ var TimestampExp = jet.TimestampExp
|
||||||
// Does not add sql cast to generated sql builder output.
|
// Does not add sql cast to generated sql builder output.
|
||||||
var TimestampzExp = jet.TimestampzExp
|
var TimestampzExp = jet.TimestampzExp
|
||||||
|
|
||||||
|
// 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 T) jet.Range[T] {
|
||||||
|
return jet.RangeExp[T](expression)
|
||||||
|
}
|
||||||
|
|
||||||
// RawArgs is type used to pass optional arguments to Raw method
|
// RawArgs is type used to pass optional arguments to Raw method
|
||||||
type RawArgs = map[string]interface{}
|
type RawArgs = map[string]interface{}
|
||||||
|
|
||||||
|
|
@ -90,15 +115,21 @@ type RawArgs = map[string]interface{}
|
||||||
var (
|
var (
|
||||||
Raw = jet.Raw
|
Raw = jet.Raw
|
||||||
|
|
||||||
RawBool = jet.RawBool
|
RawBool = jet.RawBool
|
||||||
RawInt = jet.RawInt
|
RawInt = jet.RawInt
|
||||||
RawFloat = jet.RawFloat
|
RawFloat = jet.RawFloat
|
||||||
RawString = jet.RawString
|
RawString = jet.RawString
|
||||||
RawTime = jet.RawTime
|
RawTime = jet.RawTime
|
||||||
RawTimez = jet.RawTimez
|
RawTimez = jet.RawTimez
|
||||||
RawTimestamp = jet.RawTimestamp
|
RawTimestamp = jet.RawTimestamp
|
||||||
RawTimestampz = jet.RawTimestampz
|
RawTimestampz = jet.RawTimestampz
|
||||||
RawDate = jet.RawDate
|
RawDate = jet.RawDate
|
||||||
|
RawNumRange = jet.RawRange[jet.NumericExpression]
|
||||||
|
RawInt4Range = jet.RawRange[jet.IntegerExpression]
|
||||||
|
RawInt8Range = jet.RawRange[jet.IntegerExpression]
|
||||||
|
RawTimestampRange = jet.RawRange[jet.TimestampExpression]
|
||||||
|
RawTimestampzRange = jet.RawRange[jet.TimestampzExpression]
|
||||||
|
RawDateRange = jet.RawRange[jet.DateExpression]
|
||||||
)
|
)
|
||||||
|
|
||||||
// Func can be used to call custom or unsupported database functions.
|
// Func can be used to call custom or unsupported database functions.
|
||||||
|
|
|
||||||
|
|
@ -267,6 +267,18 @@ var TO_HEX = jet.TO_HEX
|
||||||
|
|
||||||
//----------Data Type Formatting Functions ----------------------//
|
//----------Data Type Formatting Functions ----------------------//
|
||||||
|
|
||||||
|
// LOWER_BOUND returns range expressions lower bound
|
||||||
|
func LOWER_BOUND[T Expression](expression jet.Range[T]) T {
|
||||||
|
return jet.LOWER_BOUND[T](expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UPPER_BOUND returns range expressions upper bound
|
||||||
|
func UPPER_BOUND[T Expression](expression jet.Range[T]) T {
|
||||||
|
return jet.UPPER_BOUND[T](expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------Data Type Formatting Functions ----------------------//
|
||||||
|
|
||||||
// TO_CHAR converts expression to string with format
|
// TO_CHAR converts expression to string with format
|
||||||
var TO_CHAR = jet.TO_CHAR
|
var TO_CHAR = jet.TO_CHAR
|
||||||
|
|
||||||
|
|
@ -421,3 +433,18 @@ var CUBE = jet.CUBE
|
||||||
// It can be also used with multiple parameters to check if a set of columns is included in the current grouping set. The result
|
// It can be also used with multiple parameters to check if a set of columns is included in the current grouping set. The result
|
||||||
// of the GROUPING function would then be an integer bit mask having 1’s for the arguments which have GROUPING(argument) as 1.
|
// of the GROUPING function would then be an integer bit mask having 1’s for the arguments which have GROUPING(argument) as 1.
|
||||||
var GROUPING = jet.GROUPING
|
var GROUPING = jet.GROUPING
|
||||||
|
|
||||||
|
var (
|
||||||
|
// DATE_RANGE constructor function to create a date range
|
||||||
|
DATE_RANGE = jet.DateRange
|
||||||
|
// NUM_Range constructor function to create a numeric range
|
||||||
|
NUM_Range = jet.NumRange
|
||||||
|
// TIMESTAMP_RANGE constructor function to create a timestamp range
|
||||||
|
TIMESTAMP_RANGE = jet.TimestampRange
|
||||||
|
// TIMESTAMPTZ_RANGE constructor function to create a timestampz range
|
||||||
|
TIMESTAMPTZ_RANGE = jet.TimestampzRange
|
||||||
|
// INT4_RANGE constructor function to create a int4 range
|
||||||
|
INT4_RANGE = jet.Int4Range
|
||||||
|
// INT8_RANGE constructor function to create a int8 range
|
||||||
|
INT8_RANGE = jet.Int8Range
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -12,4 +12,8 @@ var (
|
||||||
NULL = jet.NULL
|
NULL = jet.NULL
|
||||||
// STAR is jet equivalent of SQL *
|
// STAR is jet equivalent of SQL *
|
||||||
STAR = jet.STAR
|
STAR = jet.STAR
|
||||||
|
// PLUS_INFINITY is jet equivalent for sql infinity
|
||||||
|
PLUS_INFINITY = jet.PLUS_INFINITY
|
||||||
|
// MINUS_INFINITY is jet equivalent for sql -infinity
|
||||||
|
MINUS_INFINITY = jet.MINUS_INFINITY
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ var table1ColTimestampz = TimestampzColumn("col_timestampz")
|
||||||
var table1ColBool = BoolColumn("col_bool")
|
var table1ColBool = BoolColumn("col_bool")
|
||||||
var table1ColDate = DateColumn("col_date")
|
var table1ColDate = DateColumn("col_date")
|
||||||
var table1ColInterval = IntervalColumn("col_interval")
|
var table1ColInterval = IntervalColumn("col_interval")
|
||||||
|
var table1ColRange = Int8RangeColumn("col_range")
|
||||||
|
|
||||||
var table1 = NewTable(
|
var table1 = NewTable(
|
||||||
"db",
|
"db",
|
||||||
|
|
@ -32,6 +33,7 @@ var table1 = NewTable(
|
||||||
table1ColTimestamp,
|
table1ColTimestamp,
|
||||||
table1ColTimestampz,
|
table1ColTimestampz,
|
||||||
table1ColInterval,
|
table1ColInterval,
|
||||||
|
table1ColRange,
|
||||||
)
|
)
|
||||||
|
|
||||||
var table2Col3 = IntegerColumn("col3")
|
var table2Col3 = IntegerColumn("col3")
|
||||||
|
|
@ -46,8 +48,9 @@ var table2ColTimestamp = TimestampColumn("col_timestamp")
|
||||||
var table2ColTimestampz = TimestampzColumn("col_timestampz")
|
var table2ColTimestampz = TimestampzColumn("col_timestampz")
|
||||||
var table2ColDate = DateColumn("col_date")
|
var table2ColDate = DateColumn("col_date")
|
||||||
var table2ColInterval = IntervalColumn("col_interval")
|
var table2ColInterval = IntervalColumn("col_interval")
|
||||||
|
var table2ColRange = Int8RangeColumn("col_range")
|
||||||
|
|
||||||
var table2 = NewTable("db", "table2", "", table2Col3, table2Col4, table2ColInt, table2ColFloat, table2ColStr, table2ColBool, table2ColTime, table2ColTimez, table2ColDate, table2ColTimestamp, table2ColTimestampz, table2ColInterval)
|
var table2 = NewTable("db", "table2", "", table2Col3, table2Col4, table2ColInt, table2ColFloat, table2ColStr, table2ColBool, table2ColTime, table2ColTimez, table2ColDate, table2ColTimestamp, table2ColTimestampz, table2ColInterval, table2ColRange)
|
||||||
|
|
||||||
var table3Col1 = IntegerColumn("col1")
|
var table3Col1 = IntegerColumn("col1")
|
||||||
var table3ColInt = IntegerColumn("col_int")
|
var table3ColInt = IntegerColumn("col_int")
|
||||||
|
|
|
||||||
|
|
@ -561,13 +561,14 @@ func TestGeneratedAllTypesSQLBuilderFiles(t *testing.T) {
|
||||||
|
|
||||||
testutils.AssertFileNamesEqual(t, modelDir, "all_types.go", "all_types_view.go", "employee.go", "link.go",
|
testutils.AssertFileNamesEqual(t, modelDir, "all_types.go", "all_types_view.go", "employee.go", "link.go",
|
||||||
"mood.go", "person.go", "person_phone.go", "weird_names_table.go", "level.go", "user.go", "floats.go", "people.go",
|
"mood.go", "person.go", "person_phone.go", "weird_names_table.go", "level.go", "user.go", "floats.go", "people.go",
|
||||||
"components.go", "vulnerabilities.go", "all_types_materialized_view.go")
|
"components.go", "vulnerabilities.go", "all_types_materialized_view.go", "sample_ranges.go")
|
||||||
testutils.AssertFileContent(t, modelDir+"/all_types.go", allTypesModelContent)
|
testutils.AssertFileContent(t, modelDir+"/all_types.go", allTypesModelContent)
|
||||||
|
|
||||||
testutils.AssertFileNamesEqual(t, tableDir, "all_types.go", "employee.go", "link.go",
|
testutils.AssertFileNamesEqual(t, tableDir, "all_types.go", "employee.go", "link.go",
|
||||||
"person.go", "person_phone.go", "weird_names_table.go", "user.go", "floats.go", "people.go", "table_use_schema.go",
|
"person.go", "person_phone.go", "weird_names_table.go", "user.go", "floats.go", "people.go", "table_use_schema.go",
|
||||||
"components.go", "vulnerabilities.go")
|
"components.go", "vulnerabilities.go", "sample_ranges.go")
|
||||||
testutils.AssertFileContent(t, tableDir+"/all_types.go", allTypesTableContent)
|
testutils.AssertFileContent(t, tableDir+"/all_types.go", allTypesTableContent)
|
||||||
|
testutils.AssertFileContent(t, tableDir+"/sample_ranges.go", sampleRangeTableContent)
|
||||||
|
|
||||||
testutils.AssertFileNamesEqual(t, viewDir, "all_types_materialized_view.go", "all_types_view.go",
|
testutils.AssertFileNamesEqual(t, viewDir, "all_types_materialized_view.go", "all_types_view.go",
|
||||||
"view_use_schema.go")
|
"view_use_schema.go")
|
||||||
|
|
@ -968,3 +969,96 @@ func newAllTypesTableImpl(schemaName, tableName, alias string) allTypesTable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
var sampleRangeTableContent = `
|
||||||
|
//
|
||||||
|
// Code generated by go-jet DO NOT EDIT.
|
||||||
|
//
|
||||||
|
// WARNING: Changes to this file may cause incorrect behavior
|
||||||
|
// and will be lost if the code is regenerated
|
||||||
|
//
|
||||||
|
|
||||||
|
package table
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/go-jet/jet/v2/postgres"
|
||||||
|
)
|
||||||
|
|
||||||
|
var SampleRanges = newSampleRangesTable("test_sample", "sample_ranges", "")
|
||||||
|
|
||||||
|
type sampleRangesTable struct {
|
||||||
|
postgres.Table
|
||||||
|
|
||||||
|
// Columns
|
||||||
|
DateRange postgres.ColumnDateRange
|
||||||
|
TimestampRange postgres.ColumnTimestampRange
|
||||||
|
TimestampzRange postgres.ColumnTimestampzRange
|
||||||
|
Int4Range postgres.ColumnInt4Range
|
||||||
|
Int8Range postgres.ColumnInt8Range
|
||||||
|
NumRange postgres.ColumnNumericRange
|
||||||
|
|
||||||
|
AllColumns postgres.ColumnList
|
||||||
|
MutableColumns postgres.ColumnList
|
||||||
|
}
|
||||||
|
|
||||||
|
type SampleRangesTable struct {
|
||||||
|
sampleRangesTable
|
||||||
|
|
||||||
|
EXCLUDED sampleRangesTable
|
||||||
|
}
|
||||||
|
|
||||||
|
// AS creates new SampleRangesTable with assigned alias
|
||||||
|
func (a SampleRangesTable) AS(alias string) *SampleRangesTable {
|
||||||
|
return newSampleRangesTable(a.SchemaName(), a.TableName(), alias)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema creates new SampleRangesTable with assigned schema name
|
||||||
|
func (a SampleRangesTable) FromSchema(schemaName string) *SampleRangesTable {
|
||||||
|
return newSampleRangesTable(schemaName, a.TableName(), a.Alias())
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPrefix creates new SampleRangesTable with assigned table prefix
|
||||||
|
func (a SampleRangesTable) WithPrefix(prefix string) *SampleRangesTable {
|
||||||
|
return newSampleRangesTable(a.SchemaName(), prefix+a.TableName(), a.TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSuffix creates new SampleRangesTable with assigned table suffix
|
||||||
|
func (a SampleRangesTable) WithSuffix(suffix string) *SampleRangesTable {
|
||||||
|
return newSampleRangesTable(a.SchemaName(), a.TableName()+suffix, a.TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSampleRangesTable(schemaName, tableName, alias string) *SampleRangesTable {
|
||||||
|
return &SampleRangesTable{
|
||||||
|
sampleRangesTable: newSampleRangesTableImpl(schemaName, tableName, alias),
|
||||||
|
EXCLUDED: newSampleRangesTableImpl("", "excluded", ""),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSampleRangesTableImpl(schemaName, tableName, alias string) sampleRangesTable {
|
||||||
|
var (
|
||||||
|
DateRangeColumn = postgres.DateRangeColumn("date_range")
|
||||||
|
TimestampRangeColumn = postgres.TimestampRangeColumn("timestamp_range")
|
||||||
|
TimestampzRangeColumn = postgres.TimestampzRangeColumn("timestampz_range")
|
||||||
|
Int4RangeColumn = postgres.Int4RangeColumn("int4_range")
|
||||||
|
Int8RangeColumn = postgres.Int8RangeColumn("int8_range")
|
||||||
|
NumRangeColumn = postgres.NumericRangeColumn("num_range")
|
||||||
|
allColumns = postgres.ColumnList{DateRangeColumn, TimestampRangeColumn, TimestampzRangeColumn, Int4RangeColumn, Int8RangeColumn, NumRangeColumn}
|
||||||
|
mutableColumns = postgres.ColumnList{DateRangeColumn, TimestampRangeColumn, TimestampzRangeColumn, Int4RangeColumn, Int8RangeColumn, NumRangeColumn}
|
||||||
|
)
|
||||||
|
|
||||||
|
return sampleRangesTable{
|
||||||
|
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
|
||||||
|
|
||||||
|
//Columns
|
||||||
|
DateRange: DateRangeColumn,
|
||||||
|
TimestampRange: TimestampRangeColumn,
|
||||||
|
TimestampzRange: TimestampzRangeColumn,
|
||||||
|
Int4Range: Int4RangeColumn,
|
||||||
|
Int8Range: Int8RangeColumn,
|
||||||
|
NumRange: NumRangeColumn,
|
||||||
|
|
||||||
|
AllColumns: allColumns,
|
||||||
|
MutableColumns: mutableColumns,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
|
||||||
514
tests/postgres/range_test.go
Normal file
514
tests/postgres/range_test.go
Normal file
|
|
@ -0,0 +1,514 @@
|
||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/go-jet/jet/v2/internal/testutils"
|
||||||
|
"github.com/go-jet/jet/v2/qrm"
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/jackc/pgtype"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
. "github.com/go-jet/jet/v2/postgres"
|
||||||
|
"github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/test_sample/model"
|
||||||
|
. "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/test_sample/table"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRangeTable_DateContainsSingle(t *testing.T) {
|
||||||
|
skipForCockroachDB(t)
|
||||||
|
expectedSQL := `
|
||||||
|
SELECT DISTINCT sample_ranges.date_range AS "sample_ranges.date_range",
|
||||||
|
sample_ranges.timestamp_range AS "sample_ranges.timestamp_range",
|
||||||
|
sample_ranges.timestampz_range AS "sample_ranges.timestampz_range",
|
||||||
|
sample_ranges.int4_range AS "sample_ranges.int4_range",
|
||||||
|
sample_ranges.int8_range AS "sample_ranges.int8_range",
|
||||||
|
sample_ranges.num_range AS "sample_ranges.num_range"
|
||||||
|
FROM test_sample.sample_ranges
|
||||||
|
WHERE sample_ranges.date_range @> '2023-12-12'::date;
|
||||||
|
`
|
||||||
|
|
||||||
|
query := SELECT(SampleRanges.AllColumns).
|
||||||
|
DISTINCT().
|
||||||
|
FROM(SampleRanges).
|
||||||
|
WHERE(SampleRanges.DateRange.CONTAINS(Date(2023, 12, 12)))
|
||||||
|
|
||||||
|
testutils.AssertDebugStatementSql(t, query, expectedSQL, "2023-12-12")
|
||||||
|
|
||||||
|
sample := model.SampleRanges{}
|
||||||
|
err := query.Query(db, &sample)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expectedRow := model.SampleRanges{
|
||||||
|
DateRange: pgtype.Daterange{
|
||||||
|
Lower: pgtype.Date{
|
||||||
|
Time: time.Date(2023, 9, 25, 0, 0, 0, 0, time.UTC),
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
Upper: pgtype.Date{
|
||||||
|
Time: time.Date(2024, 2, 10, 0, 0, 0, 0, time.UTC),
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
LowerType: pgtype.Inclusive,
|
||||||
|
UpperType: pgtype.Exclusive,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
TimestampRange: pgtype.Tsrange{
|
||||||
|
Lower: pgtype.Timestamp{
|
||||||
|
Time: time.Date(2020, 01, 01, 0, 0, 0, 0, time.UTC),
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
Upper: pgtype.Timestamp{
|
||||||
|
Time: time.Date(2021, 01, 01, 15, 0, 0, 0, time.UTC),
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
LowerType: pgtype.Inclusive,
|
||||||
|
UpperType: pgtype.Inclusive,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
TimestampzRange: pgtype.Tstzrange{
|
||||||
|
Lower: pgtype.Timestamptz{
|
||||||
|
Time: time.Date(2024, 05, 07, 15, 0, 0, 0, time.FixedZone("", 0)),
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
Upper: pgtype.Timestamptz{
|
||||||
|
Time: time.Date(2024, 10, 11, 14, 0, 0, 0, time.FixedZone("", 0)),
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
LowerType: pgtype.Inclusive,
|
||||||
|
UpperType: pgtype.Exclusive,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
Int4Range: pgtype.Int4range{
|
||||||
|
Lower: pgtype.Int4{
|
||||||
|
Int: 11,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
Upper: pgtype.Int4{
|
||||||
|
Int: 20,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
LowerType: pgtype.Inclusive,
|
||||||
|
UpperType: pgtype.Exclusive,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
Int8Range: pgtype.Int8range{
|
||||||
|
Lower: pgtype.Int8{
|
||||||
|
Int: 200,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
Upper: pgtype.Int8{
|
||||||
|
Int: 2450,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
LowerType: pgtype.Inclusive,
|
||||||
|
UpperType: pgtype.Exclusive,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
NumRange: pgtype.Numrange{
|
||||||
|
Lower: pgtype.Numeric{
|
||||||
|
Int: big.NewInt(2),
|
||||||
|
Exp: 3,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
Upper: pgtype.Numeric{
|
||||||
|
Int: big.NewInt(5),
|
||||||
|
Status: pgtype.Present,
|
||||||
|
Exp: 3,
|
||||||
|
},
|
||||||
|
LowerType: pgtype.Inclusive,
|
||||||
|
UpperType: pgtype.Exclusive,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testutils.AssertDeepEqual(t, sample, expectedRow, cmp.AllowUnexported(big.Int{}))
|
||||||
|
requireLogged(t, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRangeTable_IntContainsRange(t *testing.T) {
|
||||||
|
skipForCockroachDB(t)
|
||||||
|
expectedSQL := `
|
||||||
|
SELECT DISTINCT sample_ranges.date_range AS "sample_ranges.date_range",
|
||||||
|
sample_ranges.timestamp_range AS "sample_ranges.timestamp_range",
|
||||||
|
sample_ranges.timestampz_range AS "sample_ranges.timestampz_range",
|
||||||
|
sample_ranges.int4_range AS "sample_ranges.int4_range",
|
||||||
|
sample_ranges.int8_range AS "sample_ranges.int8_range",
|
||||||
|
sample_ranges.num_range AS "sample_ranges.num_range"
|
||||||
|
FROM test_sample.sample_ranges
|
||||||
|
WHERE sample_ranges.int4_range @> int4range(12, 18, '[)'::text);
|
||||||
|
`
|
||||||
|
|
||||||
|
query := SELECT(SampleRanges.AllColumns).
|
||||||
|
DISTINCT().
|
||||||
|
FROM(SampleRanges).
|
||||||
|
WHERE(SampleRanges.Int4Range.CONTAINS_RANGE(INT4_RANGE(Int(12), Int(18), String("[)"))))
|
||||||
|
|
||||||
|
testutils.AssertDebugStatementSql(t, query, expectedSQL, int64(12), int64(18), "[)")
|
||||||
|
|
||||||
|
sample := model.SampleRanges{}
|
||||||
|
err := query.Query(db, &sample)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expectedRow := model.SampleRanges{
|
||||||
|
DateRange: pgtype.Daterange{
|
||||||
|
Lower: pgtype.Date{
|
||||||
|
Time: time.Date(2023, 9, 25, 0, 0, 0, 0, time.UTC),
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
Upper: pgtype.Date{
|
||||||
|
Time: time.Date(2024, 2, 10, 0, 0, 0, 0, time.UTC),
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
LowerType: pgtype.Inclusive,
|
||||||
|
UpperType: pgtype.Exclusive,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
TimestampRange: pgtype.Tsrange{
|
||||||
|
Lower: pgtype.Timestamp{
|
||||||
|
Time: time.Date(2020, 01, 01, 0, 0, 0, 0, time.UTC),
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
Upper: pgtype.Timestamp{
|
||||||
|
Time: time.Date(2021, 01, 01, 15, 0, 0, 0, time.UTC),
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
LowerType: pgtype.Inclusive,
|
||||||
|
UpperType: pgtype.Inclusive,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
TimestampzRange: pgtype.Tstzrange{
|
||||||
|
Lower: pgtype.Timestamptz{
|
||||||
|
Time: time.Date(2024, 05, 07, 15, 0, 0, 0, time.FixedZone("", 0)),
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
Upper: pgtype.Timestamptz{
|
||||||
|
Time: time.Date(2024, 10, 11, 14, 0, 0, 0, time.FixedZone("", 0)),
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
LowerType: pgtype.Inclusive,
|
||||||
|
UpperType: pgtype.Exclusive,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
Int4Range: pgtype.Int4range{
|
||||||
|
Lower: pgtype.Int4{
|
||||||
|
Int: 11,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
Upper: pgtype.Int4{
|
||||||
|
Int: 20,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
LowerType: pgtype.Inclusive,
|
||||||
|
UpperType: pgtype.Exclusive,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
Int8Range: pgtype.Int8range{
|
||||||
|
Lower: pgtype.Int8{
|
||||||
|
Int: 200,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
Upper: pgtype.Int8{
|
||||||
|
Int: 2450,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
LowerType: pgtype.Inclusive,
|
||||||
|
UpperType: pgtype.Exclusive,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
NumRange: pgtype.Numrange{
|
||||||
|
Lower: pgtype.Numeric{
|
||||||
|
Int: big.NewInt(2),
|
||||||
|
Exp: 3,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
Upper: pgtype.Numeric{
|
||||||
|
Int: big.NewInt(5),
|
||||||
|
Status: pgtype.Present,
|
||||||
|
Exp: 3,
|
||||||
|
},
|
||||||
|
LowerType: pgtype.Inclusive,
|
||||||
|
UpperType: pgtype.Exclusive,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testutils.AssertDeepEqual(t, sample, expectedRow, cmp.AllowUnexported(big.Int{}))
|
||||||
|
requireLogged(t, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRangeTable_TimestampContainsRange(t *testing.T) {
|
||||||
|
skipForCockroachDB(t)
|
||||||
|
expectedSQL := `
|
||||||
|
SELECT DISTINCT sample_ranges.date_range AS "sample_ranges.date_range",
|
||||||
|
sample_ranges.timestamp_range AS "sample_ranges.timestamp_range",
|
||||||
|
sample_ranges.timestampz_range AS "sample_ranges.timestampz_range",
|
||||||
|
sample_ranges.int4_range AS "sample_ranges.int4_range",
|
||||||
|
sample_ranges.int8_range AS "sample_ranges.int8_range",
|
||||||
|
sample_ranges.num_range AS "sample_ranges.num_range"
|
||||||
|
FROM test_sample.sample_ranges
|
||||||
|
WHERE sample_ranges.timestamp_range @> tsrange('2020-02-01 00:00:00'::timestamp without time zone, '2020-10-01 00:00:00'::timestamp without time zone, '[)'::text);
|
||||||
|
`
|
||||||
|
|
||||||
|
query := SELECT(SampleRanges.AllColumns).
|
||||||
|
DISTINCT().
|
||||||
|
FROM(SampleRanges).
|
||||||
|
WHERE(SampleRanges.TimestampRange.CONTAINS_RANGE(TIMESTAMP_RANGE(Timestamp(2020, 02, 01, 0, 0, 0), Timestamp(2020, 10, 01, 0, 0, 0), String("[)"))))
|
||||||
|
|
||||||
|
testutils.AssertDebugStatementSql(t, query, expectedSQL, "2020-02-01 00:00:00", "2020-10-01 00:00:00", "[)")
|
||||||
|
|
||||||
|
sample := model.SampleRanges{}
|
||||||
|
err := query.Query(db, &sample)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expectedRow := model.SampleRanges{
|
||||||
|
DateRange: pgtype.Daterange{
|
||||||
|
Lower: pgtype.Date{
|
||||||
|
Time: time.Date(2023, 9, 25, 0, 0, 0, 0, time.UTC),
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
Upper: pgtype.Date{
|
||||||
|
Time: time.Date(2024, 2, 10, 0, 0, 0, 0, time.UTC),
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
LowerType: pgtype.Inclusive,
|
||||||
|
UpperType: pgtype.Exclusive,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
TimestampRange: pgtype.Tsrange{
|
||||||
|
Lower: pgtype.Timestamp{
|
||||||
|
Time: time.Date(2020, 01, 01, 0, 0, 0, 0, time.UTC),
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
Upper: pgtype.Timestamp{
|
||||||
|
Time: time.Date(2021, 01, 01, 15, 0, 0, 0, time.UTC),
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
LowerType: pgtype.Inclusive,
|
||||||
|
UpperType: pgtype.Inclusive,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
TimestampzRange: pgtype.Tstzrange{
|
||||||
|
Lower: pgtype.Timestamptz{
|
||||||
|
Time: time.Date(2024, 05, 07, 15, 0, 0, 0, time.FixedZone("", 0)),
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
Upper: pgtype.Timestamptz{
|
||||||
|
Time: time.Date(2024, 10, 11, 14, 0, 0, 0, time.FixedZone("", 0)),
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
LowerType: pgtype.Inclusive,
|
||||||
|
UpperType: pgtype.Exclusive,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
Int4Range: pgtype.Int4range{
|
||||||
|
Lower: pgtype.Int4{
|
||||||
|
Int: 11,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
Upper: pgtype.Int4{
|
||||||
|
Int: 20,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
LowerType: pgtype.Inclusive,
|
||||||
|
UpperType: pgtype.Exclusive,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
Int8Range: pgtype.Int8range{
|
||||||
|
Lower: pgtype.Int8{
|
||||||
|
Int: 200,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
Upper: pgtype.Int8{
|
||||||
|
Int: 2450,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
LowerType: pgtype.Inclusive,
|
||||||
|
UpperType: pgtype.Exclusive,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
NumRange: pgtype.Numrange{
|
||||||
|
Lower: pgtype.Numeric{
|
||||||
|
Int: big.NewInt(2),
|
||||||
|
Exp: 3,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
Upper: pgtype.Numeric{
|
||||||
|
Int: big.NewInt(5),
|
||||||
|
Status: pgtype.Present,
|
||||||
|
Exp: 3,
|
||||||
|
},
|
||||||
|
LowerType: pgtype.Inclusive,
|
||||||
|
UpperType: pgtype.Exclusive,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testutils.AssertDeepEqual(t, sample, expectedRow, cmp.AllowUnexported(big.Int{}))
|
||||||
|
requireLogged(t, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRangeTable_ContainsOutOfRange(t *testing.T) {
|
||||||
|
skipForCockroachDB(t)
|
||||||
|
expectedSQL := `
|
||||||
|
SELECT DISTINCT sample_ranges.date_range AS "sample_ranges.date_range",
|
||||||
|
sample_ranges.timestamp_range AS "sample_ranges.timestamp_range",
|
||||||
|
sample_ranges.timestampz_range AS "sample_ranges.timestampz_range",
|
||||||
|
sample_ranges.int4_range AS "sample_ranges.int4_range",
|
||||||
|
sample_ranges.int8_range AS "sample_ranges.int8_range",
|
||||||
|
sample_ranges.num_range AS "sample_ranges.num_range"
|
||||||
|
FROM test_sample.sample_ranges
|
||||||
|
WHERE sample_ranges.int4_range @> int4range(12, 30, '[)'::text);
|
||||||
|
`
|
||||||
|
|
||||||
|
query := SELECT(SampleRanges.AllColumns).
|
||||||
|
DISTINCT().
|
||||||
|
FROM(SampleRanges).
|
||||||
|
WHERE(SampleRanges.Int4Range.CONTAINS_RANGE(INT4_RANGE(Int(12), Int(30), String("[)"))))
|
||||||
|
|
||||||
|
testutils.AssertDebugStatementSql(t, query, expectedSQL, int64(12), int64(30), "[)")
|
||||||
|
|
||||||
|
sample := model.SampleRanges{}
|
||||||
|
err := query.Query(db, &sample)
|
||||||
|
|
||||||
|
require.ErrorIs(t, err, qrm.ErrNoRows)
|
||||||
|
requireLogged(t, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRangeTable_InsertColumn(t *testing.T) {
|
||||||
|
skipForCockroachDB(t)
|
||||||
|
|
||||||
|
insertQuery := SampleRanges.INSERT(SampleRanges.AllColumns).
|
||||||
|
VALUES(
|
||||||
|
DATE_RANGE(
|
||||||
|
Date(2010, 01, 01),
|
||||||
|
Date(2014, 01, 01),
|
||||||
|
String("[)"),
|
||||||
|
),
|
||||||
|
DEFAULT,
|
||||||
|
TIMESTAMPTZ_RANGE(
|
||||||
|
TimestampzT(time.Date(2010, 01, 01, 23, 0, 0, 0, time.UTC)),
|
||||||
|
TimestampzT(time.Date(2014, 01, 01, 15, 0, 0, 0, time.UTC)),
|
||||||
|
String("[)"),
|
||||||
|
),
|
||||||
|
INT4_RANGE(Int(64), Int(128), String("[]")),
|
||||||
|
INT8_RANGE(Int(1024), Int(2048), String("[]")),
|
||||||
|
DEFAULT,
|
||||||
|
).
|
||||||
|
RETURNING(SampleRanges.AllColumns)
|
||||||
|
|
||||||
|
expectedQuery := `
|
||||||
|
INSERT INTO test_sample.sample_ranges (date_range, timestamp_range, timestampz_range, int4_range, int8_range, num_range)
|
||||||
|
VALUES (daterange('2010-01-01'::date, '2014-01-01'::date, '[)'::text), DEFAULT, tstzrange('2010-01-01 23:00:00Z'::timestamp with time zone, '2014-01-01 15:00:00Z'::timestamp with time zone, '[)'::text), int4range(64, 128, '[]'::text), int8range(1024, 2048, '[]'::text), DEFAULT)
|
||||||
|
RETURNING sample_ranges.date_range AS "sample_ranges.date_range",
|
||||||
|
sample_ranges.timestamp_range AS "sample_ranges.timestamp_range",
|
||||||
|
sample_ranges.timestampz_range AS "sample_ranges.timestampz_range",
|
||||||
|
sample_ranges.int4_range AS "sample_ranges.int4_range",
|
||||||
|
sample_ranges.int8_range AS "sample_ranges.int8_range",
|
||||||
|
sample_ranges.num_range AS "sample_ranges.num_range";
|
||||||
|
`
|
||||||
|
testutils.AssertDebugStatementSql(t, insertQuery, expectedQuery,
|
||||||
|
"2010-01-01", "2014-01-01", "[)",
|
||||||
|
time.Date(2010, 01, 01, 23, 0, 0, 0, time.UTC), time.Date(2014, 01, 01, 15, 0, 0, 0, time.UTC), "[)",
|
||||||
|
int64(64), int64(128), "[]",
|
||||||
|
int64(1024), int64(2048), "[]",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRangeTable_UpperBound(t *testing.T) {
|
||||||
|
skipForCockroachDB(t)
|
||||||
|
|
||||||
|
expectedSQL := `
|
||||||
|
SELECT UPPER(sample_ranges.date_range)
|
||||||
|
FROM test_sample.sample_ranges
|
||||||
|
WHERE sample_ranges.date_range @> '2023-12-12'::date;
|
||||||
|
`
|
||||||
|
|
||||||
|
query := SELECT(UPPER_BOUND[DateExpression](SampleRanges.DateRange)).
|
||||||
|
FROM(SampleRanges).
|
||||||
|
WHERE(SampleRanges.DateRange.CONTAINS(Date(2023, 12, 12)))
|
||||||
|
|
||||||
|
testutils.AssertDebugStatementSql(t, query, expectedSQL, "2023-12-12")
|
||||||
|
|
||||||
|
var date time.Time
|
||||||
|
err := query.Query(db, &date)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expectedYear := 2024
|
||||||
|
expectedMonth := time.February
|
||||||
|
expectedDay := 10
|
||||||
|
if expectedYear != date.Year() || expectedMonth != date.Month() || expectedDay != date.Day() {
|
||||||
|
t.Errorf("expected: 2024-02-10 got: %s", date.Format("2006-01-02"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRangeTable_LowerBound(t *testing.T) {
|
||||||
|
skipForCockroachDB(t)
|
||||||
|
|
||||||
|
expectedSQL := `
|
||||||
|
SELECT LOWER(sample_ranges.date_range)
|
||||||
|
FROM test_sample.sample_ranges
|
||||||
|
WHERE sample_ranges.date_range @> '2023-12-12'::date;
|
||||||
|
`
|
||||||
|
|
||||||
|
query := SELECT(LOWER_BOUND[DateExpression](SampleRanges.DateRange)).
|
||||||
|
FROM(SampleRanges).
|
||||||
|
WHERE(SampleRanges.DateRange.CONTAINS(Date(2023, 12, 12)))
|
||||||
|
|
||||||
|
testutils.AssertDebugStatementSql(t, query, expectedSQL, "2023-12-12")
|
||||||
|
|
||||||
|
var date time.Time
|
||||||
|
err := query.Query(db, &date)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expectedYear := 2023
|
||||||
|
expectedMonth := time.September
|
||||||
|
expectedDay := 25
|
||||||
|
if expectedYear != date.Year() || expectedMonth != date.Month() || expectedDay != date.Day() {
|
||||||
|
t.Errorf("expected: 2023-09-25 got: %s", date.Format("2006-01-02"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRangeTable_InsertInfinite(t *testing.T) {
|
||||||
|
skipForCockroachDB(t)
|
||||||
|
|
||||||
|
insertQuery := SampleRanges.INSERT(SampleRanges.AllColumns).
|
||||||
|
VALUES(
|
||||||
|
DATE_RANGE(
|
||||||
|
Date(2010, 01, 01),
|
||||||
|
DateExp(PLUS_INFINITY),
|
||||||
|
String("[)"),
|
||||||
|
),
|
||||||
|
DEFAULT,
|
||||||
|
TIMESTAMPTZ_RANGE(
|
||||||
|
TimestampzExp(MINUS_INFINITY),
|
||||||
|
TimestampzT(time.Date(2014, 01, 01, 15, 0, 0, 0, time.UTC)),
|
||||||
|
String("[)"),
|
||||||
|
),
|
||||||
|
INT4_RANGE(Int(64), Int(128), String("[]")),
|
||||||
|
INT8_RANGE(Int(1024), Int(2048), String("[]")),
|
||||||
|
DEFAULT,
|
||||||
|
).
|
||||||
|
RETURNING(SampleRanges.AllColumns)
|
||||||
|
|
||||||
|
expectedQuery := `
|
||||||
|
INSERT INTO test_sample.sample_ranges (date_range, timestamp_range, timestampz_range, int4_range, int8_range, num_range)
|
||||||
|
VALUES (daterange('2010-01-01'::date, 'infinity', '[)'::text), DEFAULT, tstzrange('-infinity', '2014-01-01 15:00:00Z'::timestamp with time zone, '[)'::text), int4range(64, 128, '[]'::text), int8range(1024, 2048, '[]'::text), DEFAULT)
|
||||||
|
RETURNING sample_ranges.date_range AS "sample_ranges.date_range",
|
||||||
|
sample_ranges.timestamp_range AS "sample_ranges.timestamp_range",
|
||||||
|
sample_ranges.timestampz_range AS "sample_ranges.timestampz_range",
|
||||||
|
sample_ranges.int4_range AS "sample_ranges.int4_range",
|
||||||
|
sample_ranges.int8_range AS "sample_ranges.int8_range",
|
||||||
|
sample_ranges.num_range AS "sample_ranges.num_range";
|
||||||
|
`
|
||||||
|
|
||||||
|
testutils.AssertDebugStatementSql(t, insertQuery, expectedQuery,
|
||||||
|
"2010-01-01", "infinity", "[)",
|
||||||
|
"-infinity", time.Date(2014, 01, 01, 15, 0, 0, 0, time.UTC), "[)",
|
||||||
|
int64(64), int64(128), "[]",
|
||||||
|
int64(1024), int64(2048), "[]",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 08bcfcbb2e1eadfca54c4522802fc65f1fee865c
|
Subproject commit 915bdc16b723d89becc577c780949baef861a6ae
|
||||||
Loading…
Add table
Add a link
Reference in a new issue