Range expression update
* Add Int4 and Int8 integer expression to distinguish int4range and int8range types * Add range functions to range expressions to avoid go template appearing in sql * Compact range integration tests and add range update tests
This commit is contained in:
parent
9d45aaaba5
commit
43fc77ba99
8 changed files with 472 additions and 460 deletions
|
|
@ -470,12 +470,12 @@ func REGEXP_LIKE(stringExp StringExpression, pattern StringExpression, matchType
|
|||
|
||||
//----------Range Type Functions ----------------------//
|
||||
|
||||
// LOWER_BOUND returns range expressions lower bound
|
||||
// LOWER_BOUND returns range expressions lower bound. Returns null if range is empty or the requested bound is infinite.
|
||||
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
|
||||
// UPPER_BOUND returns range expressions upper bound. Returns null if range is empty or the requested bound is infinite.
|
||||
func UPPER_BOUND[T Expression](rangeExpression Range[T]) T {
|
||||
return rangeTypeCaster[T](rangeExpression, NewFunc("UPPER", []Expression{rangeExpression}, nil))
|
||||
}
|
||||
|
|
@ -483,18 +483,45 @@ func UPPER_BOUND[T Expression](rangeExpression Range[T]) T {
|
|||
func rangeTypeCaster[T Expression](rangeExpression Range[T], exp Expression) T {
|
||||
var i Expression
|
||||
switch rangeExpression.(type) {
|
||||
case Range[Int4Expression], Range[Int8Expression]:
|
||||
i = IntExp(exp)
|
||||
case Range[NumericExpression]:
|
||||
i = FloatExp(exp)
|
||||
case Range[DateExpression]:
|
||||
i = DateExp(exp)
|
||||
case Range[IntegerExpression]:
|
||||
i = IntExp(exp)
|
||||
case Range[TimestampzExpression]:
|
||||
i = TimestampzExp(exp)
|
||||
case Range[TimestampExpression]:
|
||||
i = TimestampExp(exp)
|
||||
case Range[TimestampzExpression]:
|
||||
i = TimestampzExp(exp)
|
||||
}
|
||||
return i.(T)
|
||||
}
|
||||
|
||||
// IS_EMPTY returns true if range is empty
|
||||
func IS_EMPTY[T Expression](rangeExpression Range[T]) BoolExpression {
|
||||
return newBoolFunc("ISEMPTY", rangeExpression)
|
||||
}
|
||||
|
||||
// LOWER_INC returns true if lower bound is inclusive. Returns false for empty range.
|
||||
func LOWER_INC[T Expression](rangeExpression Range[T]) BoolExpression {
|
||||
return newBoolFunc("LOWER_INC", rangeExpression)
|
||||
}
|
||||
|
||||
// UPPER_INC returns true if upper bound is inclusive. Returns false for empty range.
|
||||
func UPPER_INC[T Expression](rangeExpression Range[T]) BoolExpression {
|
||||
return newBoolFunc("UPPER_INC", rangeExpression)
|
||||
}
|
||||
|
||||
// LOWER_INF returns true if upper bound is infinite. Returns false for empty range.
|
||||
func LOWER_INF[T Expression](rangeExpression Range[T]) BoolExpression {
|
||||
return newBoolFunc("LOWER_INF", rangeExpression)
|
||||
}
|
||||
|
||||
// UPPER_INF returns true if lower bound is infinite. Returns false for empty range.
|
||||
func UPPER_INF[T Expression](rangeExpression Range[T]) BoolExpression {
|
||||
return newBoolFunc("UPPER_INF", rangeExpression)
|
||||
}
|
||||
|
||||
//----------Data Type Formatting Functions ----------------------//
|
||||
|
||||
// TO_CHAR converts expression to string with format
|
||||
|
|
@ -872,30 +899,30 @@ func Func(name string, expressions ...Expression) Expression {
|
|||
}
|
||||
|
||||
func NumRange(lowNum, highNum NumericExpression, bounds ...StringExpression) Range[NumericExpression] {
|
||||
return RangeExp[NumericExpression](NewFunc("numrange", rangeFuncParamCombiner[NumericExpression](lowNum, highNum, bounds...), nil))
|
||||
return NumRangeExp(NewFunc("numrange", rangeFuncParamCombiner(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 Int4Range(lowNum, highNum IntegerExpression, bounds ...StringExpression) Range[Int4Expression] {
|
||||
return Int4RangeExp(NewFunc("int4range", rangeFuncParamCombiner(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 Int8Range(lowNum, highNum Int8Expression, bounds ...StringExpression) Range[Int8Expression] {
|
||||
return Int8RangeExp(NewFunc("int8range", rangeFuncParamCombiner(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 TsRange(lowTs, highTs TimestampExpression, bounds ...StringExpression) Range[TimestampExpression] {
|
||||
return TsRangeExp(NewFunc("tsrange", rangeFuncParamCombiner(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 TstzRange(lowTs, highTs TimestampzExpression, bounds ...StringExpression) Range[TimestampzExpression] {
|
||||
return TstzRangeExp(NewFunc("tstzrange", rangeFuncParamCombiner(lowTs, highTs, bounds...), nil))
|
||||
}
|
||||
|
||||
func DateRange(lowTs, highTs DateExpression, bounds ...StringExpression) Range[DateExpression] {
|
||||
return RangeExp[DateExpression](NewFunc("daterange", rangeFuncParamCombiner[DateExpression](lowTs, highTs, bounds...), nil))
|
||||
return DateRangeExp(NewFunc("daterange", rangeFuncParamCombiner(lowTs, highTs, bounds...), nil))
|
||||
}
|
||||
|
||||
func rangeFuncParamCombiner[T Expression](low, high T, bounds ...StringExpression) []Expression {
|
||||
func rangeFuncParamCombiner(low, high Expression, bounds ...StringExpression) []Expression {
|
||||
exp := []Expression{low, high}
|
||||
if len(bounds) != 0 {
|
||||
exp = append(exp, bounds[0])
|
||||
|
|
|
|||
|
|
@ -31,6 +31,12 @@ type IntegerExpression interface {
|
|||
BIT_SHIFT_RIGHT(shift IntegerExpression) IntegerExpression
|
||||
}
|
||||
|
||||
// additional integer expression subtypes, used in range expressions.
|
||||
type (
|
||||
Int4Expression IntegerExpression
|
||||
Int8Expression IntegerExpression
|
||||
)
|
||||
|
||||
type integerInterfaceImpl struct {
|
||||
numericExpressionImpl
|
||||
parent IntegerExpression
|
||||
|
|
|
|||
|
|
@ -18,10 +18,18 @@ type Range[T Expression] interface {
|
|||
UNION(rhs Range[T]) Range[T]
|
||||
INTERSECTION(rhs Range[T]) Range[T]
|
||||
DIFFERENCE(rhs Range[T]) Range[T]
|
||||
|
||||
UPPER_BOUND() T
|
||||
LOWER_BOUND() T
|
||||
IS_EMPTY() BoolExpression
|
||||
LOWER_INC() BoolExpression
|
||||
UPPER_INC() BoolExpression
|
||||
LOWER_INF() BoolExpression
|
||||
UPPER_INF() BoolExpression
|
||||
}
|
||||
|
||||
type rangeInterfaceImpl[T Expression] struct {
|
||||
parent Expression
|
||||
parent Range[T]
|
||||
}
|
||||
|
||||
func (r *rangeInterfaceImpl[T]) EQ(rhs Range[T]) BoolExpression {
|
||||
|
|
@ -74,6 +82,34 @@ func (r *rangeInterfaceImpl[T]) DIFFERENCE(rhs Range[T]) Range[T] {
|
|||
return RangeExp[T](Sub(r.parent, rhs))
|
||||
}
|
||||
|
||||
func (r *rangeInterfaceImpl[T]) UPPER_BOUND() T {
|
||||
return UPPER_BOUND(r.parent)
|
||||
}
|
||||
|
||||
func (r *rangeInterfaceImpl[T]) LOWER_BOUND() T {
|
||||
return LOWER_BOUND(r.parent)
|
||||
}
|
||||
|
||||
func (r *rangeInterfaceImpl[T]) IS_EMPTY() BoolExpression {
|
||||
return IS_EMPTY(r.parent)
|
||||
}
|
||||
|
||||
func (r *rangeInterfaceImpl[T]) LOWER_INC() BoolExpression {
|
||||
return LOWER_INC(r.parent)
|
||||
}
|
||||
|
||||
func (r *rangeInterfaceImpl[T]) UPPER_INC() BoolExpression {
|
||||
return UPPER_INC(r.parent)
|
||||
}
|
||||
|
||||
func (r *rangeInterfaceImpl[T]) LOWER_INF() BoolExpression {
|
||||
return LOWER_INF(r.parent)
|
||||
}
|
||||
|
||||
func (r *rangeInterfaceImpl[T]) UPPER_INF() BoolExpression {
|
||||
return UPPER_INF(r.parent)
|
||||
}
|
||||
|
||||
//---------------------------------------------------//
|
||||
|
||||
type rangeExpressionWrapper[T Expression] struct {
|
||||
|
|
@ -93,3 +129,13 @@ func newRangeExpressionWrap[T Expression](expression Expression) Range[T] {
|
|||
func RangeExp[T Expression](expression Expression) Range[T] {
|
||||
return newRangeExpressionWrap[T](expression)
|
||||
}
|
||||
|
||||
// different range expression wrappers
|
||||
var (
|
||||
Int4RangeExp = RangeExp[Int4Expression]
|
||||
Int8RangeExp = RangeExp[Int8Expression]
|
||||
NumRangeExp = RangeExp[NumericExpression]
|
||||
DateRangeExp = RangeExp[DateExpression]
|
||||
TsRangeExp = RangeExp[TimestampExpression]
|
||||
TstzRangeExp = RangeExp[TimestampzExpression]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ var (
|
|||
table1ColTimestampz = TimestampzColumn("col_timestampz")
|
||||
table1ColBool = BoolColumn("col_bool")
|
||||
table1ColDate = DateColumn("col_date")
|
||||
table1ColRange = RangeColumn[IntegerExpression]("col_range")
|
||||
table1ColRange = RangeColumn[Int8Expression]("col_range")
|
||||
)
|
||||
var table1 = NewTable("db", "table1", "", table1Col1, table1ColInt, table1ColFloat, table1Col3, table1ColTime, table1ColTimez, table1ColBool, table1ColDate, table1ColRange, table1ColTimestamp, table1ColTimestampz)
|
||||
|
||||
|
|
@ -41,7 +41,7 @@ var (
|
|||
table2ColTimestamp = TimestampColumn("col_timestamp")
|
||||
table2ColTimestampz = TimestampzColumn("col_timestampz")
|
||||
table2ColDate = DateColumn("col_date")
|
||||
table2ColRange = RangeColumn[IntegerExpression]("col_range")
|
||||
table2ColRange = RangeColumn[Int8Expression]("col_range")
|
||||
)
|
||||
var table2 = NewTable("db", "table2", "", table2Col3, table2Col4, table2ColInt, table2ColFloat, table2ColStr, table2ColBool, table2ColTime, table2ColTimez, table2ColDate, table2ColRange, table2ColTimestamp, table2ColTimestampz)
|
||||
|
||||
|
|
|
|||
|
|
@ -89,17 +89,17 @@ 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]
|
||||
// ColumnInt4Range is interface of SQL int4 range column
|
||||
type ColumnInt4Range jet.ColumnRange[jet.Int4Expression]
|
||||
|
||||
// Int4RangeColumn creates named range with range column
|
||||
var Int4RangeColumn = jet.RangeColumn[IntegerExpression]
|
||||
var Int4RangeColumn = jet.RangeColumn[jet.Int4Expression]
|
||||
|
||||
// ColumnInt8Range is interface of SQL int range column
|
||||
type ColumnInt8Range = jet.ColumnRange[IntegerExpression]
|
||||
// ColumnInt8Range is interface of SQL int8 range column
|
||||
type ColumnInt8Range jet.ColumnRange[jet.Int8Expression]
|
||||
|
||||
// Int8RangeColumn creates named range with range column
|
||||
var Int8RangeColumn = jet.RangeColumn[IntegerExpression]
|
||||
var Int8RangeColumn = jet.RangeColumn[jet.Int8Expression]
|
||||
|
||||
//------------------------------------------------------//
|
||||
|
||||
|
|
|
|||
|
|
@ -102,9 +102,14 @@ 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)
|
||||
}
|
||||
var (
|
||||
Int4RangeExp = jet.Int4RangeExp
|
||||
Int8RangeExp = jet.Int8RangeExp
|
||||
NumRangeExp = jet.NumRangeExp
|
||||
DateRangeExp = jet.DateRangeExp
|
||||
TsRangeExp = jet.TsRangeExp
|
||||
TstzRangeExp = jet.TstzRangeExp
|
||||
)
|
||||
|
||||
// RawArgs is type used to pass optional arguments to Raw method
|
||||
type RawArgs = map[string]interface{}
|
||||
|
|
@ -125,8 +130,8 @@ var (
|
|||
RawTimestampz = jet.RawTimestampz
|
||||
RawDate = jet.RawDate
|
||||
RawNumRange = jet.RawRange[jet.NumericExpression]
|
||||
RawInt4Range = jet.RawRange[jet.IntegerExpression]
|
||||
RawInt8Range = jet.RawRange[jet.IntegerExpression]
|
||||
RawInt4Range = jet.RawRange[jet.Int4Expression]
|
||||
RawInt8Range = jet.RawRange[jet.Int8Expression]
|
||||
RawTimestampRange = jet.RawRange[jet.TimestampExpression]
|
||||
RawTimestampzRange = jet.RawRange[jet.TimestampzExpression]
|
||||
RawDateRange = jet.RawRange[jet.DateExpression]
|
||||
|
|
|
|||
|
|
@ -434,15 +434,16 @@ var CUBE = jet.CUBE
|
|||
// 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
|
||||
|
||||
// range constructor functions
|
||||
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
|
||||
// NUM_RANGE constructor function to create a numeric range
|
||||
NUM_RANGE = jet.NumRange
|
||||
// TS_RANGE constructor function to create a timestamp range
|
||||
TS_RANGE = jet.TsRange
|
||||
// TSTZ_RANGE constructor function to create a timestampz range
|
||||
TSTZ_RANGE = jet.TstzRange
|
||||
// INT4_RANGE constructor function to create a int4 range
|
||||
INT4_RANGE = jet.Int4Range
|
||||
// INT8_RANGE constructor function to create a int8 range
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package postgres
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"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"
|
||||
|
|
@ -15,59 +15,168 @@ import (
|
|||
. "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/test_sample/table"
|
||||
)
|
||||
|
||||
func TestRangeTable_DateContainsSingle(t *testing.T) {
|
||||
func TestRangeTableSelect(t *testing.T) {
|
||||
skipForCockroachDB(t)
|
||||
expectedSQL := `
|
||||
SELECT DISTINCT sample_ranges.date_range AS "sample_ranges.date_range",
|
||||
|
||||
int4Range := INT4_RANGE(Int(11), Int(20))
|
||||
int8Range := INT8_RANGE(Int(100), Int(250), String("[)"))
|
||||
numRange := NUM_RANGE(Int(11), Float(22.22))
|
||||
dateRange := DATE_RANGE(Date(2010, 10, 1), DateExp(PLUS_INFINITY))
|
||||
tsRange := TS_RANGE(
|
||||
Timestamp(2020, 02, 01, 0, 0, 0),
|
||||
Timestamp(2020, 10, 01, 0, 0, 0),
|
||||
)
|
||||
tstzRange := TSTZ_RANGE(
|
||||
TimestampzExp(MINUS_INFINITY),
|
||||
TimestampzT(time.Date(2014, 01, 01, 15, 0, 0, 0, time.UTC)),
|
||||
)
|
||||
|
||||
query := SELECT(
|
||||
SampleRanges.AllColumns,
|
||||
|
||||
SampleRanges.Int4Range.EQ(SampleRanges.Int4Range).AS("sample.int4eq"),
|
||||
SampleRanges.Int8Range.EQ(int8Range).AS("sample.int8eq"),
|
||||
SampleRanges.Int4Range.NOT_EQ(int4Range).AS("sample.int4neq"),
|
||||
SampleRanges.NumRange.LT(numRange).IS_TRUE().AS("sample.num_lt"),
|
||||
SampleRanges.DateRange.LT_EQ(dateRange).IS_FALSE().AS("sample.date_lteq"),
|
||||
SampleRanges.TimestampRange.GT(tsRange).AS("sample.ts_gt"),
|
||||
SampleRanges.TimestampzRange.GT_EQ(tstzRange).AS("sample.tstz_gteq"),
|
||||
SampleRanges.Int4Range.CONTAINS(Int32(22)).AS("sample.int4cont"),
|
||||
SampleRanges.Int8Range.CONTAINS(Int64(75364)).AS("sample.int8cont"),
|
||||
SampleRanges.Int8Range.CONTAINS_RANGE(int8Range).AS("sample.int8cont_range"),
|
||||
SampleRanges.NumRange.OVERLAP(numRange).AS("sample.num_overlap"),
|
||||
SampleRanges.DateRange.UNION(dateRange).AS("sample.date_union"),
|
||||
SampleRanges.TimestampRange.INTERSECTION(tsRange).AS("sample.ts_ints"),
|
||||
SampleRanges.TimestampzRange.DIFFERENCE(tstzRange).AS("sample.tstz_diff"),
|
||||
SampleRanges.Int4Range.UPPER_BOUND().ADD(Int(5)).AS("sample.int4_upper"),
|
||||
SampleRanges.Int8Range.LOWER_BOUND().SUB(Int(12)).AS("sample.int8_lower"),
|
||||
LOWER_BOUND[DateExpression](SampleRanges.DateRange),
|
||||
UPPER_BOUND[NumericExpression](SampleRanges.NumRange).AS("sample.num_upper"),
|
||||
SampleRanges.TimestampRange.UPPER_BOUND(),
|
||||
SampleRanges.DateRange.IS_EMPTY().AS("sample.date_empty"),
|
||||
SampleRanges.TimestampRange.LOWER_INC().AS("sample.ts_low_inc"),
|
||||
SampleRanges.TimestampzRange.UPPER_INC().AS("sample.tstz_up_inc"),
|
||||
SampleRanges.TimestampRange.LOWER_INF().AS("sample.ts_low_inf"),
|
||||
SampleRanges.TimestampzRange.UPPER_INF().AS("sample.tstz_up_inf"),
|
||||
|
||||
RawInt4Range("'[1,2]'").EQ(int4Range),
|
||||
RawInt8Range("int8range(15, 25)").EQ(int8Range),
|
||||
RawNumRange("numrange(20.0, 30.0)").NOT_EQ(numRange),
|
||||
).FROM(
|
||||
SampleRanges,
|
||||
).WHERE(
|
||||
SampleRanges.DateRange.CONTAINS(Date(2023, 12, 12)),
|
||||
)
|
||||
|
||||
testutils.AssertStatementSql(t, query, `
|
||||
SELECT 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"
|
||||
sample_ranges.num_range AS "sample_ranges.num_range",
|
||||
(sample_ranges.int4_range = sample_ranges.int4_range) AS "sample.int4eq",
|
||||
(sample_ranges.int8_range = int8range($1, $2, $3::text)) AS "sample.int8eq",
|
||||
(sample_ranges.int4_range != int4range($4, $5)) AS "sample.int4neq",
|
||||
(sample_ranges.num_range < numrange($6, $7)) IS TRUE AS "sample.num_lt",
|
||||
(sample_ranges.date_range <= daterange($8::date, $9)) IS FALSE AS "sample.date_lteq",
|
||||
(sample_ranges.timestamp_range > tsrange($10::timestamp without time zone, $11::timestamp without time zone)) AS "sample.ts_gt",
|
||||
(sample_ranges.timestampz_range >= tstzrange($12, $13::timestamp with time zone)) AS "sample.tstz_gteq",
|
||||
(sample_ranges.int4_range @> $14::integer) AS "sample.int4cont",
|
||||
(sample_ranges.int8_range @> $15::bigint) AS "sample.int8cont",
|
||||
(sample_ranges.int8_range @> int8range($16, $17, $18::text)) AS "sample.int8cont_range",
|
||||
(sample_ranges.num_range && numrange($19, $20)) AS "sample.num_overlap",
|
||||
(sample_ranges.date_range + daterange($21::date, $22)) AS "sample.date_union",
|
||||
(sample_ranges.timestamp_range * tsrange($23::timestamp without time zone, $24::timestamp without time zone)) AS "sample.ts_ints",
|
||||
(sample_ranges.timestampz_range - tstzrange($25, $26::timestamp with time zone)) AS "sample.tstz_diff",
|
||||
(UPPER(sample_ranges.int4_range) + $27) AS "sample.int4_upper",
|
||||
(LOWER(sample_ranges.int8_range) - $28) AS "sample.int8_lower",
|
||||
LOWER(sample_ranges.date_range),
|
||||
UPPER(sample_ranges.num_range) AS "sample.num_upper",
|
||||
UPPER(sample_ranges.timestamp_range),
|
||||
ISEMPTY(sample_ranges.date_range) AS "sample.date_empty",
|
||||
LOWER_INC(sample_ranges.timestamp_range) AS "sample.ts_low_inc",
|
||||
UPPER_INC(sample_ranges.timestampz_range) AS "sample.tstz_up_inc",
|
||||
LOWER_INF(sample_ranges.timestamp_range) AS "sample.ts_low_inf",
|
||||
UPPER_INF(sample_ranges.timestampz_range) AS "sample.tstz_up_inf",
|
||||
('[1,2]') = int4range($29, $30),
|
||||
(int8range(15, 25)) = int8range($31, $32, $33::text),
|
||||
(numrange(20.0, 30.0)) != numrange($34, $35)
|
||||
FROM test_sample.sample_ranges
|
||||
WHERE sample_ranges.date_range @> '2023-12-12'::date;
|
||||
`
|
||||
WHERE sample_ranges.date_range @> $36::date;
|
||||
`)
|
||||
|
||||
query := SELECT(SampleRanges.AllColumns).
|
||||
DISTINCT().
|
||||
FROM(SampleRanges).
|
||||
WHERE(SampleRanges.DateRange.CONTAINS(Date(2023, 12, 12)))
|
||||
type sample struct {
|
||||
model.SampleRanges
|
||||
|
||||
testutils.AssertDebugStatementSql(t, query, expectedSQL, "2023-12-12")
|
||||
|
||||
sample := model.SampleRanges{}
|
||||
err := query.Query(db, &sample)
|
||||
Int4Eq bool
|
||||
Int8Eq bool
|
||||
Int4Neq bool
|
||||
NumLt bool
|
||||
DateLtEq bool
|
||||
TsGt bool
|
||||
TsTzGtEq bool
|
||||
Int4Cont bool
|
||||
Int8Cont bool
|
||||
Int8ContRange bool
|
||||
NumOverlap bool
|
||||
DateUnion pgtype.Daterange
|
||||
TsInts pgtype.Tsrange
|
||||
TsTzDiff pgtype.Tstzrange
|
||||
Int4Upper int32
|
||||
Int8Lower int64
|
||||
NumUpper float64
|
||||
DateEmpty bool
|
||||
TsLowInc bool
|
||||
TsTzUpInc bool
|
||||
TsLowInf bool
|
||||
TsTzUpInf bool
|
||||
}
|
||||
|
||||
var dest sample
|
||||
err := query.Query(db, &dest)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedRow := model.SampleRanges{
|
||||
DateRange: pgtype.Daterange{
|
||||
expectedRow := sample{
|
||||
SampleRanges: sampleRangeRow,
|
||||
Int4Eq: true,
|
||||
Int8Eq: false,
|
||||
Int4Neq: false,
|
||||
NumLt: false,
|
||||
DateLtEq: true,
|
||||
TsGt: false,
|
||||
TsTzGtEq: true,
|
||||
Int4Cont: false,
|
||||
Int8Cont: false,
|
||||
Int8ContRange: false,
|
||||
NumOverlap: false,
|
||||
DateUnion: pgtype.Daterange{
|
||||
Lower: pgtype.Date{
|
||||
Time: time.Date(2023, 9, 25, 0, 0, 0, 0, time.UTC),
|
||||
Time: time.Date(2010, 10, 1, 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,
|
||||
InfinityModifier: pgtype.Infinity,
|
||||
},
|
||||
LowerType: pgtype.Inclusive,
|
||||
UpperType: pgtype.Exclusive,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
TsInts: pgtype.Tsrange{
|
||||
Lower: pgtype.Timestamp{
|
||||
Time: time.Date(2020, 02, 01, 0, 0, 0, 0, time.UTC),
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
Upper: pgtype.Timestamp{
|
||||
Time: time.Date(2020, 10, 01, 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{
|
||||
TsTzDiff: pgtype.Tstzrange{
|
||||
Lower: pgtype.Timestamptz{
|
||||
Time: time.Date(2024, 05, 07, 15, 0, 0, 0, time.FixedZone("", 0)),
|
||||
Status: pgtype.Present,
|
||||
|
|
@ -80,410 +189,85 @@ WHERE sample_ranges.date_range @> '2023-12-12'::date;
|
|||
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,
|
||||
},
|
||||
Int4Upper: 25,
|
||||
Int8Lower: 188,
|
||||
NumUpper: 5000,
|
||||
DateEmpty: false,
|
||||
TsLowInc: true,
|
||||
TsTzUpInc: false,
|
||||
TsLowInf: false,
|
||||
TsTzUpInf: false,
|
||||
}
|
||||
|
||||
testutils.AssertDeepEqual(t, sample, expectedRow, cmp.AllowUnexported(big.Int{}))
|
||||
testutils.AssertDeepEqual(t, dest, 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",
|
||||
func TestRangeSelectColumnsFromSubQuery(t *testing.T) {
|
||||
|
||||
subQuery := SELECT(
|
||||
SampleRanges.AllColumns,
|
||||
SampleRanges.Int4Range.AS("range4"),
|
||||
).FROM(
|
||||
SampleRanges,
|
||||
).AsTable("sub_query")
|
||||
|
||||
int4Range := Int4RangeColumn("range4").From(subQuery)
|
||||
|
||||
stmt := SELECT(
|
||||
subQuery.AllColumns(),
|
||||
int4Range,
|
||||
).FROM(
|
||||
subQuery,
|
||||
)
|
||||
|
||||
testutils.AssertDebugStatementSql(t, stmt, `
|
||||
SELECT sub_query."sample_ranges.date_range" AS "sample_ranges.date_range",
|
||||
sub_query."sample_ranges.timestamp_range" AS "sample_ranges.timestamp_range",
|
||||
sub_query."sample_ranges.timestampz_range" AS "sample_ranges.timestampz_range",
|
||||
sub_query."sample_ranges.int4_range" AS "sample_ranges.int4_range",
|
||||
sub_query."sample_ranges.int8_range" AS "sample_ranges.int8_range",
|
||||
sub_query."sample_ranges.num_range" AS "sample_ranges.num_range",
|
||||
sub_query.range4 AS "range4",
|
||||
sub_query.range4 AS "range4"
|
||||
FROM (
|
||||
SELECT 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);
|
||||
`
|
||||
sample_ranges.num_range AS "sample_ranges.num_range",
|
||||
sample_ranges.int4_range AS "range4"
|
||||
FROM test_sample.sample_ranges
|
||||
) AS sub_query;
|
||||
`)
|
||||
|
||||
query := SELECT(SampleRanges.AllColumns).
|
||||
DISTINCT().
|
||||
FROM(SampleRanges).
|
||||
WHERE(SampleRanges.Int4Range.CONTAINS_RANGE(INT4_RANGE(Int(12), Int(18), String("[)"))))
|
||||
var dest struct {
|
||||
model.SampleRanges
|
||||
|
||||
testutils.AssertDebugStatementSql(t, query, expectedSQL, int64(12), int64(18), "[)")
|
||||
Range4 pgtype.Int4range
|
||||
}
|
||||
|
||||
sample := model.SampleRanges{}
|
||||
err := query.Query(db, &sample)
|
||||
err := stmt.Query(db, &dest)
|
||||
|
||||
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)
|
||||
testutils.AssertDeepEqual(t, dest.SampleRanges.Int4Range, sampleRangeRow.Int4Range)
|
||||
testutils.AssertDeepEqual(t, dest.SampleRanges.Int8Range, sampleRangeRow.Int8Range)
|
||||
testutils.AssertDeepEqual(t, dest.Range4, sampleRangeRow.Int4Range)
|
||||
}
|
||||
|
||||
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("[)"),
|
||||
String("()"),
|
||||
),
|
||||
DEFAULT,
|
||||
TIMESTAMPTZ_RANGE(
|
||||
TSTZ_RANGE(
|
||||
TimestampzExp(MINUS_INFINITY),
|
||||
TimestampzT(time.Date(2014, 01, 01, 15, 0, 0, 0, time.UTC)),
|
||||
String("[)"),
|
||||
|
|
@ -492,11 +276,14 @@ func TestRangeTable_InsertInfinite(t *testing.T) {
|
|||
INT8_RANGE(Int(1024), Int(2048), String("[]")),
|
||||
DEFAULT,
|
||||
).
|
||||
RETURNING(SampleRanges.AllColumns)
|
||||
MODEL(
|
||||
sampleRangeRow,
|
||||
).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)
|
||||
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),
|
||||
('[2023-09-25,2024-02-10)', '[2020-01-01 00:00:00,2021-01-01 15:00:00]', '[2024-05-07 15:00:00Z,2024-10-11 14:00:00Z)', '[11,20)', '[200,2450)', '[2e3,5e3)')
|
||||
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",
|
||||
|
|
@ -504,11 +291,151 @@ RETURNING sample_ranges.date_range AS "sample_ranges.date_range",
|
|||
sample_ranges.int8_range AS "sample_ranges.int8_range",
|
||||
sample_ranges.num_range AS "sample_ranges.num_range";
|
||||
`
|
||||
testutils.AssertDebugStatementSql(t, insertQuery, expectedQuery)
|
||||
|
||||
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), "[]",
|
||||
)
|
||||
testutils.ExecuteInTxAndRollback(t, db, func(tx *sql.Tx) {
|
||||
var dest []model.SampleRanges
|
||||
err := insertQuery.Query(tx, &dest)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, dest, 2)
|
||||
testutils.AssertDeepEqual(t, sampleRangeRow, dest[1], cmp.AllowUnexported(big.Int{}))
|
||||
})
|
||||
}
|
||||
|
||||
func TestRangeTableUpdate(t *testing.T) {
|
||||
skipForCockroachDB(t)
|
||||
|
||||
t.Run("using model", func(t *testing.T) {
|
||||
stmt := SampleRanges.UPDATE(SampleRanges.AllColumns).
|
||||
MODEL(sampleRangeRow).
|
||||
WHERE(SampleRanges.TimestampRange.LOWER_INF().IS_FALSE()).
|
||||
RETURNING(SampleRanges.AllColumns)
|
||||
|
||||
testutils.AssertStatementSql(t, stmt, `
|
||||
UPDATE test_sample.sample_ranges
|
||||
SET (date_range, timestamp_range, timestampz_range, int4_range, int8_range, num_range) = ($1, $2, $3, $4, $5, $6)
|
||||
WHERE LOWER_INF(sample_ranges.timestamp_range) IS FALSE
|
||||
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.ExecuteInTxAndRollback(t, db, func(tx *sql.Tx) {
|
||||
var dest []model.SampleRanges
|
||||
|
||||
err := stmt.Query(tx, &dest)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, dest, 1)
|
||||
testutils.AssertDeepEqual(t, sampleRangeRow, dest[0], cmp.AllowUnexported(big.Int{}))
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("update using SET", func(t *testing.T) {
|
||||
stmt := SampleRanges.UPDATE().
|
||||
SET(
|
||||
SampleRanges.Int4Range.SET(INT4_RANGE(Int32(-12), Int32(78))),
|
||||
SampleRanges.Int8Range.SET(INT8_RANGE(Int64(-1200), Int64(7800))),
|
||||
).
|
||||
WHERE(
|
||||
SampleRanges.TimestampzRange.LOWER_BOUND().GT(NOW()),
|
||||
)
|
||||
|
||||
testutils.AssertDebugStatementSql(t, stmt, `
|
||||
UPDATE test_sample.sample_ranges
|
||||
SET int4_range = int4range(-12::integer, 78::integer),
|
||||
int8_range = int8range(-1200::bigint, 7800::bigint)
|
||||
WHERE LOWER(sample_ranges.timestampz_range) > NOW();
|
||||
`)
|
||||
|
||||
testutils.ExecuteInTxAndRollback(t, db, func(tx *sql.Tx) {
|
||||
testutils.AssertExec(t, stmt, tx, 1)
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
var sampleRangeRow = 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,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue