jet/tests/postgres/range_test.go
Sarkan 893567daca
Range types implemented
plus and minus infinity keyword tests implemented

range table tests added

skip cockroach db added

select test case added for range fields

generator modified to generate correct types

generator tests modified to include sample range table

model and template generators modified to support range fields

returning the T in UPPER and LOWER functions

raw ranges implemented

bounds set as optional

dep modified

dependencies modified and issue fixed

range expression with templates implemented

rangeExpression change to make it more type safe

third parameter of constructor function fixed

literals removed, functions added

tests modified

constructor functions used for creating range expressions

NumRange converted to a constructor function from literal

range_lower and range_upper renamed to lower_bound and upper_bound

range literal removed

PlusInfinity and MinusInfinity implemented

int4 and int8 castings added

issues fixed and tests checked

number, ts, tstz literal and cast implemented

date range literal expression modified and raw function used

parent type converted from RangeExpression to Expression

range type implemented for postgres

range column type, function and literal expression implemented

CONTAINS and OVERLAP operations added for range expressions

range expressions implemented
2024-02-25 01:20:36 +01:00

514 lines
15 KiB
Go

package postgres
import (
"github.com/go-jet/jet/v2/internal/testutils"
"github.com/go-jet/jet/v2/qrm"
"github.com/google/go-cmp/cmp"
"github.com/jackc/pgtype"
"github.com/stretchr/testify/require"
"math/big"
"testing"
"time"
. "github.com/go-jet/jet/v2/postgres"
"github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/test_sample/model"
. "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/test_sample/table"
)
func TestRangeTable_DateContainsSingle(t *testing.T) {
skipForCockroachDB(t)
expectedSQL := `
SELECT DISTINCT sample_ranges.date_range AS "sample_ranges.date_range",
sample_ranges.timestamp_range AS "sample_ranges.timestamp_range",
sample_ranges.timestampz_range AS "sample_ranges.timestampz_range",
sample_ranges.int4_range AS "sample_ranges.int4_range",
sample_ranges.int8_range AS "sample_ranges.int8_range",
sample_ranges.num_range AS "sample_ranges.num_range"
FROM test_sample.sample_ranges
WHERE sample_ranges.date_range @> '2023-12-12'::date;
`
query := SELECT(SampleRanges.AllColumns).
DISTINCT().
FROM(SampleRanges).
WHERE(SampleRanges.DateRange.CONTAINS(Date(2023, 12, 12)))
testutils.AssertDebugStatementSql(t, query, expectedSQL, "2023-12-12")
sample := model.SampleRanges{}
err := query.Query(db, &sample)
require.NoError(t, err)
expectedRow := model.SampleRanges{
DateRange: pgtype.Daterange{
Lower: pgtype.Date{
Time: time.Date(2023, 9, 25, 0, 0, 0, 0, time.UTC),
Status: pgtype.Present,
},
Upper: pgtype.Date{
Time: time.Date(2024, 2, 10, 0, 0, 0, 0, time.UTC),
Status: pgtype.Present,
},
LowerType: pgtype.Inclusive,
UpperType: pgtype.Exclusive,
Status: pgtype.Present,
},
TimestampRange: pgtype.Tsrange{
Lower: pgtype.Timestamp{
Time: time.Date(2020, 01, 01, 0, 0, 0, 0, time.UTC),
Status: pgtype.Present,
},
Upper: pgtype.Timestamp{
Time: time.Date(2021, 01, 01, 15, 0, 0, 0, time.UTC),
Status: pgtype.Present,
},
LowerType: pgtype.Inclusive,
UpperType: pgtype.Inclusive,
Status: pgtype.Present,
},
TimestampzRange: pgtype.Tstzrange{
Lower: pgtype.Timestamptz{
Time: time.Date(2024, 05, 07, 15, 0, 0, 0, time.FixedZone("", 0)),
Status: pgtype.Present,
},
Upper: pgtype.Timestamptz{
Time: time.Date(2024, 10, 11, 14, 0, 0, 0, time.FixedZone("", 0)),
Status: pgtype.Present,
},
LowerType: pgtype.Inclusive,
UpperType: pgtype.Exclusive,
Status: pgtype.Present,
},
Int4Range: pgtype.Int4range{
Lower: pgtype.Int4{
Int: 11,
Status: pgtype.Present,
},
Upper: pgtype.Int4{
Int: 20,
Status: pgtype.Present,
},
LowerType: pgtype.Inclusive,
UpperType: pgtype.Exclusive,
Status: pgtype.Present,
},
Int8Range: pgtype.Int8range{
Lower: pgtype.Int8{
Int: 200,
Status: pgtype.Present,
},
Upper: pgtype.Int8{
Int: 2450,
Status: pgtype.Present,
},
LowerType: pgtype.Inclusive,
UpperType: pgtype.Exclusive,
Status: pgtype.Present,
},
NumRange: pgtype.Numrange{
Lower: pgtype.Numeric{
Int: big.NewInt(2),
Exp: 3,
Status: pgtype.Present,
},
Upper: pgtype.Numeric{
Int: big.NewInt(5),
Status: pgtype.Present,
Exp: 3,
},
LowerType: pgtype.Inclusive,
UpperType: pgtype.Exclusive,
Status: pgtype.Present,
},
}
testutils.AssertDeepEqual(t, sample, expectedRow, cmp.AllowUnexported(big.Int{}))
requireLogged(t, query)
}
func TestRangeTable_IntContainsRange(t *testing.T) {
skipForCockroachDB(t)
expectedSQL := `
SELECT DISTINCT sample_ranges.date_range AS "sample_ranges.date_range",
sample_ranges.timestamp_range AS "sample_ranges.timestamp_range",
sample_ranges.timestampz_range AS "sample_ranges.timestampz_range",
sample_ranges.int4_range AS "sample_ranges.int4_range",
sample_ranges.int8_range AS "sample_ranges.int8_range",
sample_ranges.num_range AS "sample_ranges.num_range"
FROM test_sample.sample_ranges
WHERE sample_ranges.int4_range @> int4range(12, 18, '[)'::text);
`
query := SELECT(SampleRanges.AllColumns).
DISTINCT().
FROM(SampleRanges).
WHERE(SampleRanges.Int4Range.CONTAINS_RANGE(INT4_RANGE(Int(12), Int(18), String("[)"))))
testutils.AssertDebugStatementSql(t, query, expectedSQL, int64(12), int64(18), "[)")
sample := model.SampleRanges{}
err := query.Query(db, &sample)
require.NoError(t, err)
expectedRow := model.SampleRanges{
DateRange: pgtype.Daterange{
Lower: pgtype.Date{
Time: time.Date(2023, 9, 25, 0, 0, 0, 0, time.UTC),
Status: pgtype.Present,
},
Upper: pgtype.Date{
Time: time.Date(2024, 2, 10, 0, 0, 0, 0, time.UTC),
Status: pgtype.Present,
},
LowerType: pgtype.Inclusive,
UpperType: pgtype.Exclusive,
Status: pgtype.Present,
},
TimestampRange: pgtype.Tsrange{
Lower: pgtype.Timestamp{
Time: time.Date(2020, 01, 01, 0, 0, 0, 0, time.UTC),
Status: pgtype.Present,
},
Upper: pgtype.Timestamp{
Time: time.Date(2021, 01, 01, 15, 0, 0, 0, time.UTC),
Status: pgtype.Present,
},
LowerType: pgtype.Inclusive,
UpperType: pgtype.Inclusive,
Status: pgtype.Present,
},
TimestampzRange: pgtype.Tstzrange{
Lower: pgtype.Timestamptz{
Time: time.Date(2024, 05, 07, 15, 0, 0, 0, time.FixedZone("", 0)),
Status: pgtype.Present,
},
Upper: pgtype.Timestamptz{
Time: time.Date(2024, 10, 11, 14, 0, 0, 0, time.FixedZone("", 0)),
Status: pgtype.Present,
},
LowerType: pgtype.Inclusive,
UpperType: pgtype.Exclusive,
Status: pgtype.Present,
},
Int4Range: pgtype.Int4range{
Lower: pgtype.Int4{
Int: 11,
Status: pgtype.Present,
},
Upper: pgtype.Int4{
Int: 20,
Status: pgtype.Present,
},
LowerType: pgtype.Inclusive,
UpperType: pgtype.Exclusive,
Status: pgtype.Present,
},
Int8Range: pgtype.Int8range{
Lower: pgtype.Int8{
Int: 200,
Status: pgtype.Present,
},
Upper: pgtype.Int8{
Int: 2450,
Status: pgtype.Present,
},
LowerType: pgtype.Inclusive,
UpperType: pgtype.Exclusive,
Status: pgtype.Present,
},
NumRange: pgtype.Numrange{
Lower: pgtype.Numeric{
Int: big.NewInt(2),
Exp: 3,
Status: pgtype.Present,
},
Upper: pgtype.Numeric{
Int: big.NewInt(5),
Status: pgtype.Present,
Exp: 3,
},
LowerType: pgtype.Inclusive,
UpperType: pgtype.Exclusive,
Status: pgtype.Present,
},
}
testutils.AssertDeepEqual(t, sample, expectedRow, cmp.AllowUnexported(big.Int{}))
requireLogged(t, query)
}
func TestRangeTable_TimestampContainsRange(t *testing.T) {
skipForCockroachDB(t)
expectedSQL := `
SELECT DISTINCT sample_ranges.date_range AS "sample_ranges.date_range",
sample_ranges.timestamp_range AS "sample_ranges.timestamp_range",
sample_ranges.timestampz_range AS "sample_ranges.timestampz_range",
sample_ranges.int4_range AS "sample_ranges.int4_range",
sample_ranges.int8_range AS "sample_ranges.int8_range",
sample_ranges.num_range AS "sample_ranges.num_range"
FROM test_sample.sample_ranges
WHERE sample_ranges.timestamp_range @> tsrange('2020-02-01 00:00:00'::timestamp without time zone, '2020-10-01 00:00:00'::timestamp without time zone, '[)'::text);
`
query := SELECT(SampleRanges.AllColumns).
DISTINCT().
FROM(SampleRanges).
WHERE(SampleRanges.TimestampRange.CONTAINS_RANGE(TIMESTAMP_RANGE(Timestamp(2020, 02, 01, 0, 0, 0), Timestamp(2020, 10, 01, 0, 0, 0), String("[)"))))
testutils.AssertDebugStatementSql(t, query, expectedSQL, "2020-02-01 00:00:00", "2020-10-01 00:00:00", "[)")
sample := model.SampleRanges{}
err := query.Query(db, &sample)
require.NoError(t, err)
expectedRow := model.SampleRanges{
DateRange: pgtype.Daterange{
Lower: pgtype.Date{
Time: time.Date(2023, 9, 25, 0, 0, 0, 0, time.UTC),
Status: pgtype.Present,
},
Upper: pgtype.Date{
Time: time.Date(2024, 2, 10, 0, 0, 0, 0, time.UTC),
Status: pgtype.Present,
},
LowerType: pgtype.Inclusive,
UpperType: pgtype.Exclusive,
Status: pgtype.Present,
},
TimestampRange: pgtype.Tsrange{
Lower: pgtype.Timestamp{
Time: time.Date(2020, 01, 01, 0, 0, 0, 0, time.UTC),
Status: pgtype.Present,
},
Upper: pgtype.Timestamp{
Time: time.Date(2021, 01, 01, 15, 0, 0, 0, time.UTC),
Status: pgtype.Present,
},
LowerType: pgtype.Inclusive,
UpperType: pgtype.Inclusive,
Status: pgtype.Present,
},
TimestampzRange: pgtype.Tstzrange{
Lower: pgtype.Timestamptz{
Time: time.Date(2024, 05, 07, 15, 0, 0, 0, time.FixedZone("", 0)),
Status: pgtype.Present,
},
Upper: pgtype.Timestamptz{
Time: time.Date(2024, 10, 11, 14, 0, 0, 0, time.FixedZone("", 0)),
Status: pgtype.Present,
},
LowerType: pgtype.Inclusive,
UpperType: pgtype.Exclusive,
Status: pgtype.Present,
},
Int4Range: pgtype.Int4range{
Lower: pgtype.Int4{
Int: 11,
Status: pgtype.Present,
},
Upper: pgtype.Int4{
Int: 20,
Status: pgtype.Present,
},
LowerType: pgtype.Inclusive,
UpperType: pgtype.Exclusive,
Status: pgtype.Present,
},
Int8Range: pgtype.Int8range{
Lower: pgtype.Int8{
Int: 200,
Status: pgtype.Present,
},
Upper: pgtype.Int8{
Int: 2450,
Status: pgtype.Present,
},
LowerType: pgtype.Inclusive,
UpperType: pgtype.Exclusive,
Status: pgtype.Present,
},
NumRange: pgtype.Numrange{
Lower: pgtype.Numeric{
Int: big.NewInt(2),
Exp: 3,
Status: pgtype.Present,
},
Upper: pgtype.Numeric{
Int: big.NewInt(5),
Status: pgtype.Present,
Exp: 3,
},
LowerType: pgtype.Inclusive,
UpperType: pgtype.Exclusive,
Status: pgtype.Present,
},
}
testutils.AssertDeepEqual(t, sample, expectedRow, cmp.AllowUnexported(big.Int{}))
requireLogged(t, query)
}
func TestRangeTable_ContainsOutOfRange(t *testing.T) {
skipForCockroachDB(t)
expectedSQL := `
SELECT DISTINCT sample_ranges.date_range AS "sample_ranges.date_range",
sample_ranges.timestamp_range AS "sample_ranges.timestamp_range",
sample_ranges.timestampz_range AS "sample_ranges.timestampz_range",
sample_ranges.int4_range AS "sample_ranges.int4_range",
sample_ranges.int8_range AS "sample_ranges.int8_range",
sample_ranges.num_range AS "sample_ranges.num_range"
FROM test_sample.sample_ranges
WHERE sample_ranges.int4_range @> int4range(12, 30, '[)'::text);
`
query := SELECT(SampleRanges.AllColumns).
DISTINCT().
FROM(SampleRanges).
WHERE(SampleRanges.Int4Range.CONTAINS_RANGE(INT4_RANGE(Int(12), Int(30), String("[)"))))
testutils.AssertDebugStatementSql(t, query, expectedSQL, int64(12), int64(30), "[)")
sample := model.SampleRanges{}
err := query.Query(db, &sample)
require.ErrorIs(t, err, qrm.ErrNoRows)
requireLogged(t, query)
}
func TestRangeTable_InsertColumn(t *testing.T) {
skipForCockroachDB(t)
insertQuery := SampleRanges.INSERT(SampleRanges.AllColumns).
VALUES(
DATE_RANGE(
Date(2010, 01, 01),
Date(2014, 01, 01),
String("[)"),
),
DEFAULT,
TIMESTAMPTZ_RANGE(
TimestampzT(time.Date(2010, 01, 01, 23, 0, 0, 0, time.UTC)),
TimestampzT(time.Date(2014, 01, 01, 15, 0, 0, 0, time.UTC)),
String("[)"),
),
INT4_RANGE(Int(64), Int(128), String("[]")),
INT8_RANGE(Int(1024), Int(2048), String("[]")),
DEFAULT,
).
RETURNING(SampleRanges.AllColumns)
expectedQuery := `
INSERT INTO test_sample.sample_ranges (date_range, timestamp_range, timestampz_range, int4_range, int8_range, num_range)
VALUES (daterange('2010-01-01'::date, '2014-01-01'::date, '[)'::text), DEFAULT, tstzrange('2010-01-01 23:00:00Z'::timestamp with time zone, '2014-01-01 15:00:00Z'::timestamp with time zone, '[)'::text), int4range(64, 128, '[]'::text), int8range(1024, 2048, '[]'::text), DEFAULT)
RETURNING sample_ranges.date_range AS "sample_ranges.date_range",
sample_ranges.timestamp_range AS "sample_ranges.timestamp_range",
sample_ranges.timestampz_range AS "sample_ranges.timestampz_range",
sample_ranges.int4_range AS "sample_ranges.int4_range",
sample_ranges.int8_range AS "sample_ranges.int8_range",
sample_ranges.num_range AS "sample_ranges.num_range";
`
testutils.AssertDebugStatementSql(t, insertQuery, expectedQuery,
"2010-01-01", "2014-01-01", "[)",
time.Date(2010, 01, 01, 23, 0, 0, 0, time.UTC), time.Date(2014, 01, 01, 15, 0, 0, 0, time.UTC), "[)",
int64(64), int64(128), "[]",
int64(1024), int64(2048), "[]",
)
}
func TestRangeTable_UpperBound(t *testing.T) {
skipForCockroachDB(t)
expectedSQL := `
SELECT UPPER(sample_ranges.date_range)
FROM test_sample.sample_ranges
WHERE sample_ranges.date_range @> '2023-12-12'::date;
`
query := SELECT(UPPER_BOUND[DateExpression](SampleRanges.DateRange)).
FROM(SampleRanges).
WHERE(SampleRanges.DateRange.CONTAINS(Date(2023, 12, 12)))
testutils.AssertDebugStatementSql(t, query, expectedSQL, "2023-12-12")
var date time.Time
err := query.Query(db, &date)
require.NoError(t, err)
expectedYear := 2024
expectedMonth := time.February
expectedDay := 10
if expectedYear != date.Year() || expectedMonth != date.Month() || expectedDay != date.Day() {
t.Errorf("expected: 2024-02-10 got: %s", date.Format("2006-01-02"))
}
}
func TestRangeTable_LowerBound(t *testing.T) {
skipForCockroachDB(t)
expectedSQL := `
SELECT LOWER(sample_ranges.date_range)
FROM test_sample.sample_ranges
WHERE sample_ranges.date_range @> '2023-12-12'::date;
`
query := SELECT(LOWER_BOUND[DateExpression](SampleRanges.DateRange)).
FROM(SampleRanges).
WHERE(SampleRanges.DateRange.CONTAINS(Date(2023, 12, 12)))
testutils.AssertDebugStatementSql(t, query, expectedSQL, "2023-12-12")
var date time.Time
err := query.Query(db, &date)
require.NoError(t, err)
expectedYear := 2023
expectedMonth := time.September
expectedDay := 25
if expectedYear != date.Year() || expectedMonth != date.Month() || expectedDay != date.Day() {
t.Errorf("expected: 2023-09-25 got: %s", date.Format("2006-01-02"))
}
}
func TestRangeTable_InsertInfinite(t *testing.T) {
skipForCockroachDB(t)
insertQuery := SampleRanges.INSERT(SampleRanges.AllColumns).
VALUES(
DATE_RANGE(
Date(2010, 01, 01),
DateExp(PLUS_INFINITY),
String("[)"),
),
DEFAULT,
TIMESTAMPTZ_RANGE(
TimestampzExp(MINUS_INFINITY),
TimestampzT(time.Date(2014, 01, 01, 15, 0, 0, 0, time.UTC)),
String("[)"),
),
INT4_RANGE(Int(64), Int(128), String("[]")),
INT8_RANGE(Int(1024), Int(2048), String("[]")),
DEFAULT,
).
RETURNING(SampleRanges.AllColumns)
expectedQuery := `
INSERT INTO test_sample.sample_ranges (date_range, timestamp_range, timestampz_range, int4_range, int8_range, num_range)
VALUES (daterange('2010-01-01'::date, 'infinity', '[)'::text), DEFAULT, tstzrange('-infinity', '2014-01-01 15:00:00Z'::timestamp with time zone, '[)'::text), int4range(64, 128, '[]'::text), int8range(1024, 2048, '[]'::text), DEFAULT)
RETURNING sample_ranges.date_range AS "sample_ranges.date_range",
sample_ranges.timestamp_range AS "sample_ranges.timestamp_range",
sample_ranges.timestampz_range AS "sample_ranges.timestampz_range",
sample_ranges.int4_range AS "sample_ranges.int4_range",
sample_ranges.int8_range AS "sample_ranges.int8_range",
sample_ranges.num_range AS "sample_ranges.num_range";
`
testutils.AssertDebugStatementSql(t, insertQuery, expectedQuery,
"2010-01-01", "infinity", "[)",
"-infinity", time.Date(2014, 01, 01, 15, 0, 0, 0, time.UTC), "[)",
int64(64), int64(128), "[]",
int64(1024), int64(2048), "[]",
)
}