From 7d4aa4cdabad3bcf2fb4e855680592776c8d4229 Mon Sep 17 00:00:00 2001 From: go-jet Date: Fri, 16 Jan 2026 14:11:22 +0100 Subject: [PATCH 1/6] Fix for SELECT_JSON_ARR generates incorrect order by, limit and offset clauses for mariadb. --- internal/jet/clause.go | 12 +++ internal/testutils/test_utils.go | 14 ++++ mysql/select_json.go | 59 +++++++++++--- tests/mysql/select_json_test.go | 131 ++++++++++++++++++++++--------- 4 files changed, 168 insertions(+), 48 deletions(-) diff --git a/internal/jet/clause.go b/internal/jet/clause.go index dab2f81..5604209 100644 --- a/internal/jet/clause.go +++ b/internal/jet/clause.go @@ -190,6 +190,10 @@ type ClauseOrderBy struct { SkipNewLine bool } +func (o *ClauseOrderBy) serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) { + o.Serialize(statementType, out, options...) +} + // Serialize serializes clause into SQLBuilder func (o *ClauseOrderBy) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) { if o.List == nil { @@ -219,6 +223,10 @@ type ClauseLimit struct { Count int64 } +func (o *ClauseLimit) serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) { + o.Serialize(statementType, out, options...) +} + // Serialize serializes clause into SQLBuilder func (l *ClauseLimit) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) { if l.Count >= 0 { @@ -233,6 +241,10 @@ type ClauseOffset struct { Count IntegerExpression } +func (o *ClauseOffset) serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) { + o.Serialize(statementType, out, options...) +} + // Serialize serializes clause into SQLBuilder func (o *ClauseOffset) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) { if is.Nil(o.Count) { diff --git a/internal/testutils/test_utils.go b/internal/testutils/test_utils.go index 88f00ab..f2a2c8d 100644 --- a/internal/testutils/test_utils.go +++ b/internal/testutils/test_utils.go @@ -136,6 +136,20 @@ func SaveJSONFile(v interface{}, testRelativePath string) { throw.OnError(err) } +func ReadJSONFile(t require.TestingT, testRelativePath string, dest any) { + if _, ok := t.(*testing.B); ok { + return // skip assert for benchmarks + } + + filePath := getFullPath(testRelativePath) + fileJSONData, err := os.ReadFile(filePath) // #nosec G304 + require.NoError(t, err) + + err = json.Unmarshal(fileJSONData, dest) + + require.NoError(t, err) +} + // AssertJSONFile check if data json representation is the same as json at testRelativePath func AssertJSONFile(t require.TestingT, data interface{}, testRelativePath string) { if _, ok := t.(*testing.B); ok { diff --git a/mysql/select_json.go b/mysql/select_json.go index cfeb30f..914f885 100644 --- a/mysql/select_json.go +++ b/mysql/select_json.go @@ -30,26 +30,46 @@ func SELECT_JSON_OBJ(projections ...Projection) SelectJsonStatement { type selectJsonStatement struct { *selectStatementImpl + + projections []Projection + statementType jet.StatementType + + // SELECT_JSON_ARR internal clauses + arrOrderBy *jet.ClauseOrderBy + arrLimit *jet.ClauseLimit + arrOffset *jet.ClauseOffset } func newSelectStatementJson(projections []Projection, statementType jet.StatementType) SelectJsonStatement { - newSelect := &selectJsonStatement{ + newSelectJson := &selectJsonStatement{ selectStatementImpl: newSelectStatement(statementType, nil, nil), + + projections: projections, + statementType: statementType, + + arrOrderBy: &jet.ClauseOrderBy{}, + arrLimit: &jet.ClauseLimit{Count: -1}, + arrOffset: &jet.ClauseOffset{}, } - newSelect.Select.ProjectionList = ProjectionList{constructJsonFunc(projections, statementType).AS("json")} + newSelectJson.constructProjectionList() - return newSelect + return newSelectJson } -func constructJsonFunc(projections []Projection, statementType jet.StatementType) Expression { - jsonObj := Func("JSON_OBJECT", CustomExpression(jet.JsonObjProjectionList(projections))) +func (s *selectJsonStatement) constructProjectionList() { + jsonProjection := Func("JSON_OBJECT", CustomExpression(jet.JsonObjProjectionList(s.projections))) - if statementType == jet.SelectJsonArrStatementType { - return Func("JSON_ARRAYAGG", jsonObj) + if s.statementType == jet.SelectJsonArrStatementType { + jsonProjection = Func("JSON_ARRAYAGG", CustomExpression( + jsonProjection, + s.arrOrderBy, + s.arrLimit, + s.arrOffset, + )) } - return jsonObj + s.Select.ProjectionList = ProjectionList{jsonProjection.AS("json")} } func (s *selectJsonStatement) FROM(table ReadableTable) SelectJsonStatement { @@ -63,17 +83,32 @@ func (s *selectJsonStatement) WHERE(condition BoolExpression) SelectJsonStatemen return s } -func (s *selectJsonStatement) ORDER_BY(orderByClauses ...OrderByClause) SelectJsonStatement { - s.OrderBy.List = orderByClauses +func (s *selectJsonStatement) ORDER_BY(orderBy ...OrderByClause) SelectJsonStatement { + if s.statementType == jet.SelectJsonArrStatementType { + s.arrOrderBy.List = orderBy + } else { + s.OrderBy.List = orderBy + } + return s } func (s *selectJsonStatement) LIMIT(limit int64) SelectJsonStatement { - s.Limit.Count = limit + if s.statementType == jet.SelectJsonArrStatementType { + s.arrLimit.Count = limit + } else { + s.Limit.Count = limit + } + return s } func (s *selectJsonStatement) OFFSET(offset int64) SelectJsonStatement { - s.Offset.Count = Int(offset) + if s.statementType == jet.SelectJsonArrStatementType { + s.arrOffset.Count = Int(offset) + } else { + s.Offset.Count = Int(offset) + } + return s } diff --git a/tests/mysql/select_json_test.go b/tests/mysql/select_json_test.go index a50afb4..390bfe3 100644 --- a/tests/mysql/select_json_test.go +++ b/tests/mysql/select_json_test.go @@ -2,8 +2,8 @@ package mysql import ( "context" - "fmt" "github.com/go-jet/jet/v2/qrm" + "slices" "strings" "testing" @@ -127,32 +127,91 @@ WHERE actor.actor_id = ?; } func TestSelectJsonArr(t *testing.T) { - stmt := SELECT_JSON_ARR(Actor.AllColumns). - FROM(Actor). - ORDER_BY(Actor.ActorID) + onlyMariaDB(t) - testutils.AssertDebugStatementSql(t, stmt, ` + var savedActors []model.Actor + testutils.ReadJSONFile(t, "./testdata/results/mysql/all_actors.json", &savedActors) + + t.Run("order by", func(t *testing.T) { + stmt := SELECT_JSON_ARR(Actor.AllColumns). + FROM(Actor). + ORDER_BY( + Actor.LastName.DESC(), + Actor.FirstName.ASC(), + Actor.ActorID.ASC(), + ) + + testutils.AssertDebugStatementSql(t, stmt, ` SELECT JSON_ARRAYAGG(JSON_OBJECT( 'actorID', actor.actor_id, 'firstName', actor.first_name, 'lastName', actor.last_name, 'lastUpdate', DATE_FORMAT(actor.last_update,'%Y-%m-%dT%H:%i:%s.%fZ') - )) AS "json" -FROM dvds.actor -ORDER BY actor.actor_id; + ) + ORDER BY actor.last_name DESC, actor.first_name ASC, actor.actor_id ASC) AS "json" +FROM dvds.actor; `) - var dest []model.Actor + var dest []model.Actor - err := stmt.Query(db, &dest) - require.Nil(t, err) + err := stmt.Query(db, &dest) + require.Nil(t, err) + + // sort by actor.LastName desc + slices.SortFunc(savedActors, func(a, b model.Actor) int { + if l := strings.Compare(b.LastName, a.LastName); l != 0 { + return l + } + + if f := strings.Compare(a.FirstName, b.FirstName); f != 0 { + return f + } + + return int(a.ActorID) - int(b.ActorID) + }) + + require.Equal(t, dest, savedActors) + requireLogged(t, stmt) + requireQueryLogged(t, stmt, 1) + }) + + t.Run("order by, limit, offset", func(t *testing.T) { + stmt := SELECT_JSON_ARR(Actor.AllColumns). + FROM(Actor). + ORDER_BY(Actor.ActorID.DESC()). + LIMIT(5). + OFFSET(10) + + testutils.AssertStatementSql(t, stmt, ` +SELECT JSON_ARRAYAGG(JSON_OBJECT( + 'actorID', actor.actor_id, + 'firstName', actor.first_name, + 'lastName', actor.last_name, + 'lastUpdate', DATE_FORMAT(actor.last_update,'%Y-%m-%dT%H:%i:%s.%fZ') + ) + ORDER BY actor.actor_id DESC + LIMIT ? + OFFSET ?) AS "json" +FROM dvds.actor; +`) + + var dest []model.Actor + + err := stmt.Query(db, &dest) + require.Nil(t, err) + + slices.SortFunc(savedActors, func(a, b model.Actor) int { + return int(b.ActorID) - int(a.ActorID) + }) + + require.Equal(t, dest, savedActors[10:15]) + }) - testutils.AssertJSONFile(t, dest, "./testdata/results/mysql/all_actors.json") - requireLogged(t, stmt) - requireQueryLogged(t, stmt, 1) } func TestSelectJsonArr_NestedArr(t *testing.T) { + onlyMariaDB(t) + stmt := SELECT_JSON_ARR( Actor.AllColumns, @@ -198,16 +257,16 @@ SELECT JSON_ARRAYAGG(JSON_OBJECT( 'rating', film.rating, 'specialFeatures', film.special_features, 'lastUpdate', DATE_FORMAT(film.last_update,'%Y-%m-%dT%H:%i:%s.%fZ') - )) AS "json" + ) + ORDER BY film.length DESC) AS "json" FROM dvds.film_actor INNER JOIN dvds.film ON ((film.film_id = film_actor.film_id) AND (actor.actor_id = film_actor.actor_id)) WHERE (film.film_id % 17) = 0 - ORDER BY film.length DESC ) - )) AS "json" + ) + ORDER BY actor.actor_id) AS "json" FROM dvds.actor -WHERE actor.actor_id BETWEEN 1 AND 3 -ORDER BY actor.actor_id; +WHERE actor.actor_id BETWEEN 1 AND 3; `) var dest []struct { @@ -217,8 +276,8 @@ ORDER BY actor.actor_id; } err := stmt.QueryContext(ctx, db, &dest) - fmt.Println(err) - require.Nil(t, err) + require.NoError(t, err) + testutils.AssertJSON(t, dest, ` [ { @@ -234,21 +293,6 @@ ORDER BY actor.actor_id; "LastName": "WAHLBERG", "LastUpdate": "2006-02-15T04:34:33Z", "Films": [ - { - "FilmID": 357, - "Title": "GILBERT PELICAN", - "Description": "A Fateful Tale of a Man And a Feminist who must Conquer a Crocodile in A Manhattan Penthouse", - "ReleaseYear": 2006, - "LanguageID": 1, - "OriginalLanguageID": null, - "RentalDuration": 7, - "RentalRate": 0.99, - "Length": 114, - "ReplacementCost": 13.99, - "Rating": "G", - "SpecialFeatures": "Trailers,Commentaries", - "LastUpdate": "2006-02-15T05:03:42Z" - }, { "FilmID": 561, "Title": "MASK PEACH", @@ -263,6 +307,21 @@ ORDER BY actor.actor_id; "Rating": "NC-17", "SpecialFeatures": "Commentaries,Deleted Scenes", "LastUpdate": "2006-02-15T05:03:42Z" + }, + { + "FilmID": 357, + "Title": "GILBERT PELICAN", + "Description": "A Fateful Tale of a Man And a Feminist who must Conquer a Crocodile in A Manhattan Penthouse", + "ReleaseYear": 2006, + "LanguageID": 1, + "OriginalLanguageID": null, + "RentalDuration": 7, + "RentalRate": 0.99, + "Length": 114, + "ReplacementCost": 13.99, + "Rating": "G", + "SpecialFeatures": "Trailers,Commentaries", + "LastUpdate": "2006-02-15T05:03:42Z" } ] }, From ab8e282e34da8b120495cc19b3d6ffaf0acb2b77 Mon Sep 17 00:00:00 2001 From: Andres Date: Mon, 19 Jan 2026 12:46:39 -0300 Subject: [PATCH 2/6] fix: row scanning always using strict scan --- qrm/qrm.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qrm/qrm.go b/qrm/qrm.go index 6fc081f..a56b3e8 100644 --- a/qrm/qrm.go +++ b/qrm/qrm.go @@ -229,7 +229,9 @@ func ScanOneRowToDest(scanContext *ScanContext, rows *sql.Rows, destPtr interfac return fmt.Errorf("jet: failed to scan a row into destination, %w", err) } - scanContext.EnsureEveryColumnRead() // can panic + if scanContext.rowNum == 1 && GlobalConfig.StrictScan { + scanContext.EnsureEveryColumnRead() // can panic + } return nil } From 36450552e601e03001b77381ddc04793a2e50cbc Mon Sep 17 00:00:00 2001 From: go-jet Date: Wed, 21 Jan 2026 12:02:09 +0100 Subject: [PATCH 3/6] Add test for rows strict scan. --- tests/postgres/scan_test.go | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/postgres/scan_test.go b/tests/postgres/scan_test.go index 910bd09..3ff5af9 100644 --- a/tests/postgres/scan_test.go +++ b/tests/postgres/scan_test.go @@ -841,6 +841,42 @@ func TestRowsScan(t *testing.T) { requireQueryLogged(t, stmt, 0) } +func TestRowsNonStrictScan(t *testing.T) { + stmt := SELECT( + Inventory.AllColumns, + Store.AllColumns, + ).FROM( + Inventory.INNER_JOIN(Store, Store.StoreID.EQ(Inventory.StoreID)), + ).ORDER_BY( + Inventory.InventoryID.ASC(), + ) + + require.PanicsWithValue(t, "jet: columns never used: 'store.store_id', 'store.manager_staff_id', 'store.address_id', 'store.last_update'", func() { + rows, err := stmt.Rows(context.Background(), db) + + var dest model.Inventory + + for rows.Next() { + err = rows.Scan(&dest) + require.NoError(t, err) + } + }) + + allowUnusedColumns(func() { + rows, err := stmt.Rows(context.Background(), db) + + var dest model.Inventory + + for rows.Next() { + err = rows.Scan(&dest) + require.NoError(t, err) + } + + require.NoError(t, rows.Close()) + require.NoError(t, rows.Err()) + }) +} + func TestScanNullColumn(t *testing.T) { stmt := SELECT( Address.AllColumns, From 38725aa49042f078e2443bb25d9f9a7e6755293a Mon Sep 17 00:00:00 2001 From: go-jet <47941548+go-jet@users.noreply.github.com> Date: Wed, 21 Jan 2026 12:14:39 +0100 Subject: [PATCH 4/6] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 913cbf8..b2211bb 100644 --- a/README.md +++ b/README.md @@ -633,7 +633,7 @@ Typically, two releases are published each year — one in early spring and anot ## License -Copyright 2019-2025 Goran Bjelanovic +Copyright 2019-2026 Goran Bjelanovic Licensed under the Apache License, Version 2.0. ## Support the Project From 6cc4f0bc58379d330677a2f0ad0df50df2f79545 Mon Sep 17 00:00:00 2001 From: go-jet Date: Wed, 21 Jan 2026 13:24:28 +0100 Subject: [PATCH 5/6] Fix for incorrect subquery column type. --- internal/jet/alias.go | 38 ++++++++- internal/jet/array_expression.go | 7 +- tests/postgres/alltypes_test.go | 134 ++++++++++++++++++++++++++++++- 3 files changed, 171 insertions(+), 8 deletions(-) diff --git a/internal/jet/alias.go b/internal/jet/alias.go index d096a65..031dae1 100644 --- a/internal/jet/alias.go +++ b/internal/jet/alias.go @@ -27,8 +27,7 @@ func (a *alias) fromImpl(subQuery SelectTable) Projection { // This function is used to create dummy columns when exporting sub-query columns using subQuery.AllColumns() // In most case we don't care about type of the column, except when sub-query columns are used as SELECT_JSON projection. -// We need to know type to encode value for json unmarshal. At the moment only bool, time and blob columns are of interest, -// so we don't have to support every column type. +// We need to know type to encode value for json unmarshal. func newDummyColumnForExpression(exp Expression, name string) ColumnExpression { switch exp.(type) { @@ -54,6 +53,41 @@ func newDummyColumnForExpression(exp Expression, name string) ColumnExpression { return IntervalColumn(name) case StringExpression: return StringColumn(name) + + case Array[BoolExpression]: + return ArrayColumn[BoolExpression](name) + case Array[IntegerExpression]: + return ArrayColumn[IntegerExpression](name) + case Array[FloatExpression]: + return ArrayColumn[FloatExpression](name) + case Array[BlobExpression]: + return ArrayColumn[BlobExpression](name) + case Array[DateExpression]: + return ArrayColumn[DateExpression](name) + case Array[TimeExpression]: + return ArrayColumn[TimeExpression](name) + case Array[TimezExpression]: + return ArrayColumn[TimezExpression](name) + case Array[TimestampExpression]: + return ArrayColumn[TimestampExpression](name) + case Array[TimestampzExpression]: + return ArrayColumn[TimestampzExpression](name) + case Array[IntervalExpression]: + return ArrayColumn[IntervalExpression](name) + case Array[StringExpression]: + return ArrayColumn[StringExpression](name) + + case Range[Int4Expression], Range[Int8Expression]: + return RangeColumn[IntegerExpression](name) + case Range[NumericExpression]: + return RangeColumn[NumericExpression](name) + case Range[DateExpression]: + return RangeColumn[DateExpression](name) + case Range[TimestampExpression]: + return RangeColumn[TimestampExpression](name) + case Range[TimestampzExpression]: + return RangeColumn[TimestampzExpression](name) + } return StringColumn(name) diff --git a/internal/jet/array_expression.go b/internal/jet/array_expression.go index e0ab8dc..b054baa 100644 --- a/internal/jet/array_expression.go +++ b/internal/jet/array_expression.go @@ -80,9 +80,10 @@ type arrayExpressionWrapper[E Expression] struct { } func newArrayExpressionWrap[E Expression](expression Expression) Array[E] { - arrayExpressionWrapper := arrayExpressionWrapper[E]{Expression: expression} - arrayExpressionWrapper.arrayInterfaceImpl.parent = &arrayExpressionWrapper - return &arrayExpressionWrapper + arrayExpressionWrapper := &arrayExpressionWrapper[E]{Expression: expression} + arrayExpressionWrapper.arrayInterfaceImpl.parent = arrayExpressionWrapper + expression.setRoot(arrayExpressionWrapper) + return arrayExpressionWrapper } // ArrayExp is array expression wrapper around arbitrary expression. diff --git a/tests/postgres/alltypes_test.go b/tests/postgres/alltypes_test.go index d7338fe..057be0a 100644 --- a/tests/postgres/alltypes_test.go +++ b/tests/postgres/alltypes_test.go @@ -2,12 +2,12 @@ package postgres import ( "encoding/base64" + "fmt" "github.com/go-jet/jet/v2/internal/utils/ptr" - "github.com/stretchr/testify/assert" - "math" - "github.com/go-jet/jet/v2/qrm" "github.com/lib/pq" + "github.com/stretchr/testify/assert" + "math" "testing" "time" @@ -1728,6 +1728,134 @@ SELECT ROW($1::integer, $2::real, $3::text) AS "row", }) } +func TestSubQueryAllExpTypes(t *testing.T) { + + subquery := SELECT( + Bool(true).AS("bool"), + Int(11).AS("int"), + Text("doe").AS("text"), + Date(2000, 2, 2).AS("date"), + Time(11, 20, 40).AS("time"), + Timez(11, 20, 40, 200, "UTC").AS("timez"), + Timestamp(2030, 3, 4, 11, 20, 40).AS("timestamp"), + Timestampz(2023, 1, 2, 11, 20, 40, 200, "UTC").AS("timestampz"), + INTERVAL(100, HOUR).AS("interval"), + Bytea("bytes").AS("bytea"), + + ARRAY(Bool(true)).AS("bool_arr"), + ARRAY(Int(11)).AS("int_arr"), + ARRAY(Text("doe")).AS("text_arr"), + ARRAY(Date(2000, 2, 2)).AS("date_arr"), + ARRAY(Time(11, 20, 40)).AS("time_arr"), + ARRAY(Timez(11, 20, 40, 200, "UTC")).AS("timez_arr"), + ARRAY(Timestamp(2030, 3, 4, 11, 20, 40)).AS("timestamp_arr"), + ARRAY(Timestampz(2023, 1, 2, 11, 20, 40, 200, "UTC")).AS("timestampz_arr"), + ARRAY(INTERVAL(100, HOUR)).AS("interval_arr"), + ARRAY(Bytea("bytes")).AS("bytea_arr"), + + INT4_RANGE(Int(1), Int(200)).AS("int4_range"), + DATE_RANGE(Date(2000, 2, 2), Date(2000, 3, 3)).AS("date_range"), + NUM_RANGE(Float(33.22), Float(22.1)).AS("num_range"), + TS_RANGE(LOCALTIMESTAMP(), LOCALTIMESTAMP()).AS("ts_range"), + TSTZ_RANGE(NOW(), NOW()).AS("tstz_range"), + ).AsTable("sub") + + var result = "\n" + for _, projection := range subquery.AllColumns() { + result += fmt.Sprintf("Column type: %T\n", projection) + } + + require.Equal(t, result, ` +Column type: *jet.boolColumnImpl +Column type: *jet.integerColumnImpl +Column type: *jet.stringColumnImpl +Column type: *jet.dateColumnImpl +Column type: *jet.timeColumnImpl +Column type: *jet.timezColumnImpl +Column type: *jet.timestampColumnImpl +Column type: *jet.timestampzColumnImpl +Column type: *jet.intervalColumnImpl +Column type: *jet.blobColumnImpl +Column type: *jet.arrayColumnImpl[github.com/go-jet/jet/v2/internal/jet.BoolExpression] +Column type: *jet.arrayColumnImpl[github.com/go-jet/jet/v2/internal/jet.IntegerExpression] +Column type: *jet.arrayColumnImpl[github.com/go-jet/jet/v2/internal/jet.StringExpression] +Column type: *jet.arrayColumnImpl[github.com/go-jet/jet/v2/internal/jet.DateExpression] +Column type: *jet.arrayColumnImpl[github.com/go-jet/jet/v2/internal/jet.TimeExpression] +Column type: *jet.arrayColumnImpl[github.com/go-jet/jet/v2/internal/jet.TimezExpression] +Column type: *jet.arrayColumnImpl[github.com/go-jet/jet/v2/internal/jet.TimestampExpression] +Column type: *jet.arrayColumnImpl[github.com/go-jet/jet/v2/internal/jet.TimestampzExpression] +Column type: *jet.arrayColumnImpl[github.com/go-jet/jet/v2/internal/jet.IntervalExpression] +Column type: *jet.arrayColumnImpl[github.com/go-jet/jet/v2/internal/jet.BlobExpression] +Column type: *jet.rangeColumnImpl[github.com/go-jet/jet/v2/internal/jet.IntegerExpression] +Column type: *jet.rangeColumnImpl[github.com/go-jet/jet/v2/internal/jet.DateExpression] +Column type: *jet.rangeColumnImpl[github.com/go-jet/jet/v2/internal/jet.NumericExpression] +Column type: *jet.rangeColumnImpl[github.com/go-jet/jet/v2/internal/jet.TimestampExpression] +Column type: *jet.rangeColumnImpl[github.com/go-jet/jet/v2/internal/jet.TimestampzExpression] +`) + + stmt := SELECT( + subquery.AllColumns(), + ).FROM(subquery) + + testutils.AssertStatementSql(t, stmt, ` +SELECT sub.bool AS "bool", + sub.int AS "int", + sub.text AS "text", + sub.date AS "date", + sub.time AS "time", + sub.timez AS "timez", + sub.timestamp AS "timestamp", + sub.timestampz AS "timestampz", + sub.interval AS "interval", + sub.bytea AS "bytea", + sub.bool_arr AS "bool_arr", + sub.int_arr AS "int_arr", + sub.text_arr AS "text_arr", + sub.date_arr AS "date_arr", + sub.time_arr AS "time_arr", + sub.timez_arr AS "timez_arr", + sub.timestamp_arr AS "timestamp_arr", + sub.timestampz_arr AS "timestampz_arr", + sub.interval_arr AS "interval_arr", + sub.bytea_arr AS "bytea_arr", + sub.int4_range AS "int4_range", + sub.date_range AS "date_range", + sub.num_range AS "num_range", + sub.ts_range AS "ts_range", + sub.tstz_range AS "tstz_range" +FROM ( + SELECT $1::boolean AS "bool", + $2 AS "int", + $3::text AS "text", + $4::date AS "date", + $5::time without time zone AS "time", + $6::time with time zone AS "timez", + $7::timestamp without time zone AS "timestamp", + $8::timestamp with time zone AS "timestampz", + INTERVAL '100 HOUR' AS "interval", + $9::bytea AS "bytea", + ARRAY[$10::boolean] AS "bool_arr", + ARRAY[$11] AS "int_arr", + ARRAY[$12::text] AS "text_arr", + ARRAY[$13::date] AS "date_arr", + ARRAY[$14::time without time zone] AS "time_arr", + ARRAY[$15::time with time zone] AS "timez_arr", + ARRAY[$16::timestamp without time zone] AS "timestamp_arr", + ARRAY[$17::timestamp with time zone] AS "timestampz_arr", + ARRAY[INTERVAL '100 HOUR'] AS "interval_arr", + ARRAY[$18::bytea] AS "bytea_arr", + int4range($19, $20) AS "int4_range", + daterange($21::date, $22::date) AS "date_range", + numrange($23, $24) AS "num_range", + tsrange(LOCALTIMESTAMP, LOCALTIMESTAMP) AS "ts_range", + tstzrange(NOW(), NOW()) AS "tstz_range" + ) AS sub; +`) + + _, err := stmt.Exec(db) + require.NoError(t, err) +} + func TestAllTypesSubQueryFrom(t *testing.T) { subQuery := SELECT( AllTypes.Boolean, From 7f215544d5a647fa76c7d675c4bb5b5b18fbf419 Mon Sep 17 00:00:00 2001 From: go-jet Date: Thu, 22 Jan 2026 12:13:39 +0100 Subject: [PATCH 6/6] Test fix. --- cmd/jet/version.go | 2 +- tests/postgres/alltypes_test.go | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/cmd/jet/version.go b/cmd/jet/version.go index cfdb257..3f152c2 100644 --- a/cmd/jet/version.go +++ b/cmd/jet/version.go @@ -1,3 +1,3 @@ package main -const version = "v2.14.0" +const version = "v2.14.1" diff --git a/tests/postgres/alltypes_test.go b/tests/postgres/alltypes_test.go index 057be0a..1f00520 100644 --- a/tests/postgres/alltypes_test.go +++ b/tests/postgres/alltypes_test.go @@ -1729,10 +1729,11 @@ SELECT ROW($1::integer, $2::real, $3::text) AS "row", } func TestSubQueryAllExpTypes(t *testing.T) { + skipForCockroachDB(t) subquery := SELECT( Bool(true).AS("bool"), - Int(11).AS("int"), + Int32(11).AS("int"), Text("doe").AS("text"), Date(2000, 2, 2).AS("date"), Time(11, 20, 40).AS("time"), @@ -1743,7 +1744,7 @@ func TestSubQueryAllExpTypes(t *testing.T) { Bytea("bytes").AS("bytea"), ARRAY(Bool(true)).AS("bool_arr"), - ARRAY(Int(11)).AS("int_arr"), + ARRAY(Int32(11)).AS("int_arr"), ARRAY(Text("doe")).AS("text_arr"), ARRAY(Date(2000, 2, 2)).AS("date_arr"), ARRAY(Time(11, 20, 40)).AS("time_arr"), @@ -1754,10 +1755,10 @@ func TestSubQueryAllExpTypes(t *testing.T) { ARRAY(Bytea("bytes")).AS("bytea_arr"), INT4_RANGE(Int(1), Int(200)).AS("int4_range"), - DATE_RANGE(Date(2000, 2, 2), Date(2000, 3, 3)).AS("date_range"), - NUM_RANGE(Float(33.22), Float(22.1)).AS("num_range"), - TS_RANGE(LOCALTIMESTAMP(), LOCALTIMESTAMP()).AS("ts_range"), - TSTZ_RANGE(NOW(), NOW()).AS("tstz_range"), + DATE_RANGE(Date(2000, 2, 2), Date(2010, 3, 3)).AS("date_range"), + NUM_RANGE(Float(0.22), Float(22.1)).AS("num_range"), + TS_RANGE(LOCALTIMESTAMP(), LOCALTIMESTAMP().ADD(INTERVAL(1, HOUR))).AS("ts_range"), + TSTZ_RANGE(NOW(), NOW().ADD(INTERVAL(3, MONTH))).AS("tstz_range"), ).AsTable("sub") var result = "\n" @@ -1825,7 +1826,7 @@ SELECT sub.bool AS "bool", sub.tstz_range AS "tstz_range" FROM ( SELECT $1::boolean AS "bool", - $2 AS "int", + $2::integer AS "int", $3::text AS "text", $4::date AS "date", $5::time without time zone AS "time", @@ -1835,7 +1836,7 @@ FROM ( INTERVAL '100 HOUR' AS "interval", $9::bytea AS "bytea", ARRAY[$10::boolean] AS "bool_arr", - ARRAY[$11] AS "int_arr", + ARRAY[$11::integer] AS "int_arr", ARRAY[$12::text] AS "text_arr", ARRAY[$13::date] AS "date_arr", ARRAY[$14::time without time zone] AS "time_arr", @@ -1847,8 +1848,8 @@ FROM ( int4range($19, $20) AS "int4_range", daterange($21::date, $22::date) AS "date_range", numrange($23, $24) AS "num_range", - tsrange(LOCALTIMESTAMP, LOCALTIMESTAMP) AS "ts_range", - tstzrange(NOW(), NOW()) AS "tstz_range" + tsrange(LOCALTIMESTAMP, LOCALTIMESTAMP + INTERVAL '1 HOUR') AS "ts_range", + tstzrange(NOW(), NOW() + INTERVAL '3 MONTH') AS "tstz_range" ) AS sub; `)