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:
go-jet 2024-02-27 10:48:57 +01:00
parent 9d45aaaba5
commit 43fc77ba99
8 changed files with 472 additions and 460 deletions

View file

@ -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,
},
}