Merge pull request #549 from go-jet/subq-col-typ
Sub-query AllColumns() method doesn't returns correct type for all the column types.
This commit is contained in:
commit
eeadd8ee72
4 changed files with 173 additions and 9 deletions
|
|
@ -1,3 +1,3 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
const version = "v2.14.0"
|
const version = "v2.14.1"
|
||||||
|
|
|
||||||
|
|
@ -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()
|
// 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.
|
// 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,
|
// We need to know type to encode value for json unmarshal.
|
||||||
// so we don't have to support every column type.
|
|
||||||
func newDummyColumnForExpression(exp Expression, name string) ColumnExpression {
|
func newDummyColumnForExpression(exp Expression, name string) ColumnExpression {
|
||||||
|
|
||||||
switch exp.(type) {
|
switch exp.(type) {
|
||||||
|
|
@ -54,6 +53,41 @@ func newDummyColumnForExpression(exp Expression, name string) ColumnExpression {
|
||||||
return IntervalColumn(name)
|
return IntervalColumn(name)
|
||||||
case StringExpression:
|
case StringExpression:
|
||||||
return StringColumn(name)
|
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)
|
return StringColumn(name)
|
||||||
|
|
|
||||||
|
|
@ -80,9 +80,10 @@ type arrayExpressionWrapper[E Expression] struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newArrayExpressionWrap[E Expression](expression Expression) Array[E] {
|
func newArrayExpressionWrap[E Expression](expression Expression) Array[E] {
|
||||||
arrayExpressionWrapper := arrayExpressionWrapper[E]{Expression: expression}
|
arrayExpressionWrapper := &arrayExpressionWrapper[E]{Expression: expression}
|
||||||
arrayExpressionWrapper.arrayInterfaceImpl.parent = &arrayExpressionWrapper
|
arrayExpressionWrapper.arrayInterfaceImpl.parent = arrayExpressionWrapper
|
||||||
return &arrayExpressionWrapper
|
expression.setRoot(arrayExpressionWrapper)
|
||||||
|
return arrayExpressionWrapper
|
||||||
}
|
}
|
||||||
|
|
||||||
// ArrayExp is array expression wrapper around arbitrary expression.
|
// ArrayExp is array expression wrapper around arbitrary expression.
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@ package postgres
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
"github.com/go-jet/jet/v2/internal/utils/ptr"
|
"github.com/go-jet/jet/v2/internal/utils/ptr"
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"math"
|
|
||||||
|
|
||||||
"github.com/go-jet/jet/v2/qrm"
|
"github.com/go-jet/jet/v2/qrm"
|
||||||
"github.com/lib/pq"
|
"github.com/lib/pq"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"math"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -1728,6 +1728,135 @@ SELECT ROW($1::integer, $2::real, $3::text) AS "row",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSubQueryAllExpTypes(t *testing.T) {
|
||||||
|
skipForCockroachDB(t)
|
||||||
|
|
||||||
|
subquery := SELECT(
|
||||||
|
Bool(true).AS("bool"),
|
||||||
|
Int32(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(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"),
|
||||||
|
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(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"
|
||||||
|
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::integer 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::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",
|
||||||
|
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 + INTERVAL '1 HOUR') AS "ts_range",
|
||||||
|
tstzrange(NOW(), NOW() + INTERVAL '3 MONTH') AS "tstz_range"
|
||||||
|
) AS sub;
|
||||||
|
`)
|
||||||
|
|
||||||
|
_, err := stmt.Exec(db)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
func TestAllTypesSubQueryFrom(t *testing.T) {
|
func TestAllTypesSubQueryFrom(t *testing.T) {
|
||||||
subQuery := SELECT(
|
subQuery := SELECT(
|
||||||
AllTypes.Boolean,
|
AllTypes.Boolean,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue