From 43fc77ba992d234090001400a624827b41f8874a Mon Sep 17 00:00:00 2001 From: go-jet Date: Tue, 27 Feb 2024 10:48:57 +0100 Subject: [PATCH] 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 --- internal/jet/func_expression.go | 61 ++- internal/jet/integer_expression.go | 6 + internal/jet/range_expression.go | 48 +- internal/jet/testutils.go | 4 +- postgres/columns.go | 12 +- postgres/expressions.go | 15 +- postgres/functions.go | 13 +- tests/postgres/range_test.go | 773 +++++++++++++---------------- 8 files changed, 472 insertions(+), 460 deletions(-) diff --git a/internal/jet/func_expression.go b/internal/jet/func_expression.go index c56053f..6036b8d 100644 --- a/internal/jet/func_expression.go +++ b/internal/jet/func_expression.go @@ -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]) diff --git a/internal/jet/integer_expression.go b/internal/jet/integer_expression.go index 0bce229..f5451a0 100644 --- a/internal/jet/integer_expression.go +++ b/internal/jet/integer_expression.go @@ -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 diff --git a/internal/jet/range_expression.go b/internal/jet/range_expression.go index f05fdea..9e15c2b 100644 --- a/internal/jet/range_expression.go +++ b/internal/jet/range_expression.go @@ -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] +) diff --git a/internal/jet/testutils.go b/internal/jet/testutils.go index d91e30f..70b21c7 100644 --- a/internal/jet/testutils.go +++ b/internal/jet/testutils.go @@ -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) diff --git a/postgres/columns.go b/postgres/columns.go index aee2896..819da38 100644 --- a/postgres/columns.go +++ b/postgres/columns.go @@ -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] //------------------------------------------------------// diff --git a/postgres/expressions.go b/postgres/expressions.go index a903860..b964534 100644 --- a/postgres/expressions.go +++ b/postgres/expressions.go @@ -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] diff --git a/postgres/functions.go b/postgres/functions.go index eddc930..7b6d1e1 100644 --- a/postgres/functions.go +++ b/postgres/functions.go @@ -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 diff --git a/tests/postgres/range_test.go b/tests/postgres/range_test.go index 63498eb..88928f2 100644 --- a/tests/postgres/range_test.go +++ b/tests/postgres/range_test.go @@ -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", - 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); -` +func TestRangeSelectColumnsFromSubQuery(t *testing.T) { - query := SELECT(SampleRanges.AllColumns). - DISTINCT(). - FROM(SampleRanges). - WHERE(SampleRanges.Int4Range.CONTAINS_RANGE(INT4_RANGE(Int(12), Int(18), String("[)")))) + subQuery := SELECT( + SampleRanges.AllColumns, + SampleRanges.Int4Range.AS("range4"), + ).FROM( + SampleRanges, + ).AsTable("sub_query") - testutils.AssertDebugStatementSql(t, query, expectedSQL, int64(12), int64(18), "[)") + int4Range := Int4RangeColumn("range4").From(subQuery) - sample := model.SampleRanges{} - err := query.Query(db, &sample) + 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", + sample_ranges.int4_range AS "range4" + FROM test_sample.sample_ranges + ) AS sub_query; +`) + + var dest struct { + model.SampleRanges + + Range4 pgtype.Int4range + } + + 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, + }, }