Encode json values implicitly in the sql queries according the golang json package spec.

This commit is contained in:
go-jet 2025-03-08 19:01:37 +01:00
parent 9616bb5cfe
commit 17646ca99c
54 changed files with 1446 additions and 744 deletions

View file

@ -43,29 +43,82 @@ func TestAllTypesJSON(t *testing.T) {
AllTypes.JSONPtr,
AllTypes.Bit,
AllTypes.BitPtr,
AllTypes.Blob,
AllTypes.BlobPtr,
AllTypes.Binary,
AllTypes.BinaryPtr,
AllTypes.VarBinary,
AllTypes.VarBinaryPtr,
),
CAST(AllTypes.JSON).AS_CHAR().AS("Json"),
CAST(AllTypes.JSONPtr).AS_CHAR().AS("JsonPtr"),
CAST(AllTypes.Bit).AS_CHAR().AS("Bit"),
CAST(AllTypes.BitPtr).AS_CHAR().AS("BitPtr"),
// TODO: remove when binary string is implemented
CONCAT(String("\\x"), HEX(AllTypes.Blob)).AS("Blob"),
CONCAT(String("\\x"), HEX(AllTypes.BlobPtr)).AS("BlobPtr"),
CONCAT(String("\\x"), HEX(AllTypes.Binary)).AS("Binary"),
CONCAT(String("\\x"), HEX(AllTypes.BinaryPtr)).AS("BinaryPtr"),
CONCAT(String("\\x"), HEX(AllTypes.VarBinary)).AS("VarBinary"),
CONCAT(String("\\x"), HEX(AllTypes.VarBinaryPtr)).AS("VarBinaryPtr"),
).FROM(AllTypes)
testutils.AssertStatementSql(t, stmt, strings.ReplaceAll(`
SELECT JSON_ARRAYAGG(JSON_OBJECT(
'id', all_types.id,
'boolean', all_types.boolean = 1,
'booleanPtr', all_types.boolean_ptr = 1,
'tinyInt', all_types.tiny_int,
'uTinyInt', all_types.u_tiny_int,
'smallInt', all_types.small_int,
'uSmallInt', all_types.u_small_int,
'mediumInt', all_types.medium_int,
'uMediumInt', all_types.u_medium_int,
'integer', all_types.''integer'',
'uInteger', all_types.u_integer,
'bigInt', all_types.big_int,
'uBigInt', all_types.u_big_int,
'tinyIntPtr', all_types.tiny_int_ptr,
'uTinyIntPtr', all_types.u_tiny_int_ptr,
'smallIntPtr', all_types.small_int_ptr,
'uSmallIntPtr', all_types.u_small_int_ptr,
'mediumIntPtr', all_types.medium_int_ptr,
'uMediumIntPtr', all_types.u_medium_int_ptr,
'integerPtr', all_types.integer_ptr,
'uIntegerPtr', all_types.u_integer_ptr,
'bigIntPtr', all_types.big_int_ptr,
'uBigIntPtr', all_types.u_big_int_ptr,
'decimal', all_types.''decimal'',
'decimalPtr', all_types.decimal_ptr,
'numeric', all_types.''numeric'',
'numericPtr', all_types.numeric_ptr,
'float', all_types.''float'',
'floatPtr', all_types.float_ptr,
'double', all_types.''double'',
'doublePtr', all_types.double_ptr,
'real', all_types.''real'',
'realPtr', all_types.real_ptr,
'time', CONCAT('0000-01-01T', DATE_FORMAT(all_types.time,'%H:%i:%s.%fZ')),
'timePtr', CONCAT('0000-01-01T', DATE_FORMAT(all_types.time_ptr,'%H:%i:%s.%fZ')),
'date', CONCAT(DATE_FORMAT(all_types.date,'%Y-%m-%d'), 'T00:00:00Z'),
'datePtr', CONCAT(DATE_FORMAT(all_types.date_ptr,'%Y-%m-%d'), 'T00:00:00Z'),
'dateTime', DATE_FORMAT(all_types.date_time,'%Y-%m-%dT%H:%i:%s.%fZ'),
'dateTimePtr', DATE_FORMAT(all_types.date_time_ptr,'%Y-%m-%dT%H:%i:%s.%fZ'),
'timestamp', DATE_FORMAT(all_types.timestamp,'%Y-%m-%dT%H:%i:%s.%fZ'),
'timestampPtr', DATE_FORMAT(all_types.timestamp_ptr,'%Y-%m-%dT%H:%i:%s.%fZ'),
'year', all_types.year,
'yearPtr', all_types.year_ptr,
'char', all_types.''char'',
'charPtr', all_types.char_ptr,
'varChar', all_types.var_char,
'varCharPtr', all_types.var_char_ptr,
'binary', TO_BASE64(all_types.''binary''),
'binaryPtr', TO_BASE64(all_types.binary_ptr),
'varBinary', TO_BASE64(all_types.var_binary),
'varBinaryPtr', TO_BASE64(all_types.var_binary_ptr),
'blob', TO_BASE64(all_types.''blob''),
'blobPtr', TO_BASE64(all_types.blob_ptr),
'text', all_types.text,
'textPtr', all_types.text_ptr,
'enum', all_types.enum,
'enumPtr', all_types.enum_ptr,
'set', all_types.''set'',
'setPtr', all_types.set_ptr,
'Json', CAST(all_types.json AS CHAR),
'JsonPtr', CAST(all_types.json_ptr AS CHAR),
'Bit', CAST(all_types.bit AS CHAR),
'BitPtr', CAST(all_types.bit_ptr AS CHAR)
)) AS "json"
FROM test_sample.all_types;
`, "''", "`"))
var dest []model.AllTypes
err := stmt.QueryJSON(ctx, db, &dest)
@ -76,7 +129,6 @@ func TestAllTypesJSON(t *testing.T) {
dest[0].FloatPtr = ptr.Of(3.33)
dest[1].Float = 3.33
//fmt.Println(allTypesJson)
testutils.AssertJSON(t, dest, allTypesJson)
}
@ -1182,6 +1234,118 @@ func TestAllTypesInsertOnDuplicateKeyUpdate(t *testing.T) {
require.NoError(t, err)
}
func TestAllTypesSubQueryFrom(t *testing.T) {
subQuery := SELECT(
AllTypes.Boolean,
AllTypes.Integer,
AllTypes.Double,
AllTypes.Text,
AllTypes.Date,
AllTypes.Time,
AllTypes.Timestamp,
AllTypes.Blob,
).FROM(
AllTypes,
).AsTable("sub_query")
stmt := SELECT(
AllTypes.Boolean.From(subQuery),
AllTypes.Integer.From(subQuery),
AllTypes.Double.From(subQuery),
AllTypes.Text.From(subQuery),
AllTypes.Date.From(subQuery),
AllTypes.Time.From(subQuery),
AllTypes.Timestamp.From(subQuery),
AllTypes.Blob.From(subQuery),
).FROM(
subQuery,
)
testutils.AssertStatementSql(t, stmt, strings.ReplaceAll(`
SELECT sub_query.''all_types.boolean'' AS "all_types.boolean",
sub_query.''all_types.integer'' AS "all_types.integer",
sub_query.''all_types.double'' AS "all_types.double",
sub_query.''all_types.text'' AS "all_types.text",
sub_query.''all_types.date'' AS "all_types.date",
sub_query.''all_types.time'' AS "all_types.time",
sub_query.''all_types.timestamp'' AS "all_types.timestamp",
sub_query.''all_types.blob'' AS "all_types.blob"
FROM (
SELECT all_types.boolean AS "all_types.boolean",
all_types.''integer'' AS "all_types.integer",
all_types.''double'' AS "all_types.double",
all_types.text AS "all_types.text",
all_types.date AS "all_types.date",
all_types.time AS "all_types.time",
all_types.timestamp AS "all_types.timestamp",
all_types.''blob'' AS "all_types.blob"
FROM test_sample.all_types
) AS sub_query;
`, "''", "`"))
var dest []model.AllTypes
err := stmt.Query(db, &dest)
require.NoError(t, err)
require.NotEmpty(t, dest)
t.Run("using SELECT_JSON", func(t *testing.T) {
stmtJson := SELECT_JSON_ARR(
AllTypes.Boolean.From(subQuery),
AllTypes.Integer.From(subQuery),
AllTypes.Double.From(subQuery),
AllTypes.Text.From(subQuery),
AllTypes.Date.From(subQuery),
AllTypes.Time.From(subQuery),
AllTypes.Timestamp.From(subQuery),
AllTypes.Blob.From(subQuery),
).FROM(
subQuery,
)
testutils.AssertDebugStatementSql(t, stmtJson, strings.ReplaceAll(`
SELECT JSON_ARRAYAGG(JSON_OBJECT(
'boolean', sub_query.''all_types.boolean'' = 1,
'integer', sub_query.''all_types.integer'',
'double', sub_query.''all_types.double'',
'text', sub_query.''all_types.text'',
'date', CONCAT(DATE_FORMAT(sub_query.''all_types.date'','%Y-%m-%d'), 'T00:00:00Z'),
'time', CONCAT('0000-01-01T', DATE_FORMAT(sub_query.''all_types.time'','%H:%i:%s.%fZ')),
'timestamp', DATE_FORMAT(sub_query.''all_types.timestamp'','%Y-%m-%dT%H:%i:%s.%fZ'),
'blob', TO_BASE64(sub_query.''all_types.blob'')
)) AS "json"
FROM (
SELECT all_types.boolean AS "all_types.boolean",
all_types.''integer'' AS "all_types.integer",
all_types.''double'' AS "all_types.double",
all_types.text AS "all_types.text",
all_types.date AS "all_types.date",
all_types.time AS "all_types.time",
all_types.timestamp AS "all_types.timestamp",
all_types.''blob'' AS "all_types.blob"
FROM test_sample.all_types
) AS sub_query;
`, "''", "`"))
var destJson []model.AllTypes
err := stmtJson.QueryJSON(ctx, db, &destJson)
require.NoError(t, err)
t.Run("using AllColumns()", func(t *testing.T) {
stmtJsonAllColumns := SELECT_JSON_ARR(
subQuery.AllColumns(),
).FROM(
subQuery,
)
require.Equal(t, stmtJson.DebugSql(), stmtJsonAllColumns.DebugSql())
})
testutils.AssertJsonEqual(t, dest, destJson)
})
}
var toInsert = model.AllTypes{
Boolean: false,
BooleanPtr: ptr.Of(true),

View file

@ -23,10 +23,12 @@ func TestSelectJsonObj(t *testing.T) {
WHERE(Actor.ActorID.EQ(Int(2)))
testutils.AssertStatementSql(t, stmt, `
SELECT JSON_OBJECT('actorID', actor.actor_id,
'firstName', actor.first_name,
'lastName', actor.last_name,
'lastUpdate', actor.last_update) AS "json"
SELECT 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
WHERE actor.actor_id = ?;
`, int64(2))
@ -57,30 +59,34 @@ func TestSelectJsonObj_NestedObj(t *testing.T) {
)
testutils.AssertStatementSql(t, stmt, `
SELECT JSON_OBJECT('actorID', actor.actor_id,
'firstName', actor.first_name,
'lastName', actor.last_name,
'lastUpdate', actor.last_update,
'LongestFilm', (
SELECT JSON_OBJECT('filmID', film.film_id,
'title', film.title,
'description', film.description,
'releaseYear', film.release_year,
'languageID', film.language_id,
'originalLanguageID', film.original_language_id,
'rentalDuration', film.rental_duration,
'rentalRate', film.rental_rate,
'length', film.length,
'replacementCost', film.replacement_cost,
'rating', film.rating,
'specialFeatures', film.special_features,
'lastUpdate', film.last_update) AS "json"
FROM dvds.film_actor
INNER JOIN dvds.film ON (film.film_id = film_actor.film_id)
WHERE actor.actor_id = film_actor.actor_id
ORDER BY film.length DESC
LIMIT ?
)) AS "json"
SELECT 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'),
'LongestFilm', (
SELECT JSON_OBJECT(
'filmID', film.film_id,
'title', film.title,
'description', film.description,
'releaseYear', film.release_year,
'languageID', film.language_id,
'originalLanguageID', film.original_language_id,
'rentalDuration', film.rental_duration,
'rentalRate', film.rental_rate,
'length', film.length,
'replacementCost', film.replacement_cost,
'rating', film.rating,
'specialFeatures', film.special_features,
'lastUpdate', DATE_FORMAT(film.last_update,'%Y-%m-%dT%H:%i:%s.%fZ')
) AS "json"
FROM dvds.film_actor
INNER JOIN dvds.film ON (film.film_id = film_actor.film_id)
WHERE actor.actor_id = film_actor.actor_id
ORDER BY film.length DESC
LIMIT ?
)
) AS "json"
FROM dvds.actor
WHERE actor.actor_id = ?;
`)
@ -125,10 +131,12 @@ func TestSelectJsonArr(t *testing.T) {
ORDER_BY(Actor.ActorID)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT JSON_ARRAYAGG(JSON_OBJECT('actorID', actor.actor_id,
'firstName', actor.first_name,
'lastName', actor.last_name,
'lastUpdate', actor.last_update)) AS "json"
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;
`)
@ -169,29 +177,33 @@ func TestSelectJsonArr_NestedArr(t *testing.T) {
)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT JSON_ARRAYAGG(JSON_OBJECT('actorID', actor.actor_id,
'firstName', actor.first_name,
'lastName', actor.last_name,
'lastUpdate', actor.last_update,
'Films', (
SELECT JSON_ARRAYAGG(JSON_OBJECT('filmID', film.film_id,
'title', film.title,
'description', film.description,
'releaseYear', film.release_year,
'languageID', film.language_id,
'originalLanguageID', film.original_language_id,
'rentalDuration', film.rental_duration,
'rentalRate', film.rental_rate,
'length', film.length,
'replacementCost', film.replacement_cost,
'rating', film.rating,
'specialFeatures', film.special_features,
'lastUpdate', film.last_update)) 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"
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'),
'Films', (
SELECT JSON_ARRAYAGG(JSON_OBJECT(
'filmID', film.film_id,
'title', film.title,
'description', film.description,
'releaseYear', film.release_year,
'languageID', film.language_id,
'originalLanguageID', film.original_language_id,
'rentalDuration', film.rental_duration,
'rentalRate', film.rental_rate,
'length', film.length,
'replacementCost', film.replacement_cost,
'rating', film.rating,
'specialFeatures', film.special_features,
'lastUpdate', DATE_FORMAT(film.last_update,'%Y-%m-%dT%H:%i:%s.%fZ')
)) 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"
FROM dvds.actor
WHERE actor.actor_id BETWEEN 1 AND 3
ORDER BY actor.actor_id;
@ -337,22 +349,26 @@ func TestSelectJson_GroupBy(t *testing.T) {
).FROM(subQuery)
testutils.AssertDebugStatementSql(t, stmt, strings.ReplaceAll(`
SELECT JSON_ARRAYAGG(JSON_OBJECT('customerID', customers_info.""customer.customer_id"",
'storeID', customers_info.""customer.store_id"",
'firstName', customers_info.""customer.first_name"",
'lastName', customers_info.""customer.last_name"",
'email', customers_info.""customer.email"",
'addressID', customers_info.""customer.address_id"",
'active', customers_info.""customer.active"",
'createDate', customers_info.""customer.create_date"",
'lastUpdate', customers_info.""customer.last_update"",
'amount', (
SELECT JSON_OBJECT('sum', customers_info.sum,
'avg', customers_info.avg,
'max', customers_info.max,
'min', customers_info.min,
'count', customers_info.count) AS "json"
))) AS "json"
SELECT JSON_ARRAYAGG(JSON_OBJECT(
'customerID', customers_info.''customer.customer_id'',
'storeID', customers_info.''customer.store_id'',
'firstName', customers_info.''customer.first_name'',
'lastName', customers_info.''customer.last_name'',
'email', customers_info.''customer.email'',
'addressID', customers_info.''customer.address_id'',
'active', customers_info.''customer.active'' = 1,
'createDate', DATE_FORMAT(customers_info.''customer.create_date'','%Y-%m-%dT%H:%i:%s.%fZ'),
'lastUpdate', DATE_FORMAT(customers_info.''customer.last_update'','%Y-%m-%dT%H:%i:%s.%fZ'),
'amount', (
SELECT JSON_OBJECT(
'sum', customers_info.sum,
'avg', customers_info.avg,
'max', customers_info.max,
'min', customers_info.min,
'count', customers_info.count
) AS "json"
)
)) AS "json"
FROM (
SELECT customer.customer_id AS "customer.customer_id",
customer.store_id AS "customer.store_id",
@ -374,7 +390,7 @@ FROM (
HAVING SUM(payment.amount) > 125
ORDER BY customer.customer_id, SUM(payment.amount) ASC
) AS customers_info;
`, `""`, "`"))
`, "''", "`"))
var dest []struct {
model.Customer
@ -389,7 +405,6 @@ FROM (
}
err := stmt.QueryJSON(ctx, db, &dest)
fmt.Println(err)
require.Nil(t, err)
testutils.AssertJSONFile(t, dest, "./testdata/results/mysql/customer_payment_sum.json")

View file

@ -2,10 +2,10 @@ package postgres
import (
"encoding/base64"
"fmt"
"github.com/go-jet/jet/v2/internal/utils/ptr"
"github.com/stretchr/testify/assert"
"github.com/go-jet/jet/v2/qrm"
"github.com/stretchr/testify/assert"
"testing"
"time"
@ -46,6 +46,7 @@ func TestAllTypesSelectJson(t *testing.T) {
AllTypes.JsonbArray, AllTypes.IntegerArray, AllTypes.IntegerArrayPtr,
AllTypes.TextMultiDimArray, AllTypes.TextMultiDimArrayPtr,
),
// unsupported at the moment, casting to text allows these columns to be assigned to string fields
CAST(AllTypes.JSONPtr).AS_TEXT().AS("jsonPtr"),
CAST(AllTypes.JSON).AS_TEXT().AS("JSON"),
CAST(AllTypes.JsonbPtr).AS_TEXT().AS("jsonbPtr"),
@ -59,7 +60,75 @@ func TestAllTypesSelectJson(t *testing.T) {
CAST(AllTypes.TextMultiDimArrayPtr).AS_TEXT().AS("TextMultiDimArrayPtr"),
).FROM(AllTypes)
//fmt.Println(stmt.DebugSql())
testutils.AssertStatementSql(t, stmt, `
SELECT json_agg(row_to_json(records)) AS "json"
FROM (
SELECT all_types.small_int_ptr AS "smallIntPtr",
all_types.small_int AS "smallInt",
all_types.integer_ptr AS "integerPtr",
all_types.integer AS "integer",
all_types.big_int_ptr AS "bigIntPtr",
all_types.big_int AS "bigInt",
all_types.decimal_ptr AS "decimalPtr",
all_types.decimal AS "decimal",
all_types.numeric_ptr AS "numericPtr",
all_types.numeric AS "numeric",
all_types.real_ptr AS "realPtr",
all_types.real AS "real",
all_types.double_precision_ptr AS "doublePrecisionPtr",
all_types.double_precision AS "doublePrecision",
all_types.smallserial AS "smallserial",
all_types.serial AS "serial",
all_types.bigserial AS "bigserial",
all_types.var_char_ptr AS "varCharPtr",
all_types.var_char AS "varChar",
all_types.char_ptr AS "charPtr",
all_types.char AS "char",
all_types.text_ptr AS "textPtr",
all_types.text AS "text",
ENCODE(all_types.bytea_ptr, 'base64') AS "byteaPtr",
ENCODE(all_types.bytea, 'base64') AS "bytea",
all_types.timestampz_ptr AS "timestampzPtr",
all_types.timestampz AS "timestampz",
to_char(all_types.timestamp_ptr, 'YYYY-MM-DD"T"HH24:MI:SS.USZ') AS "timestampPtr",
to_char(all_types.timestamp, 'YYYY-MM-DD"T"HH24:MI:SS.USZ') AS "timestamp",
to_char(all_types.date_ptr::timestamp, 'YYYY-MM-DD') || 'T00:00:00Z' AS "datePtr",
to_char(all_types.date::timestamp, 'YYYY-MM-DD') || 'T00:00:00Z' AS "date",
'0000-01-01T' || to_char('2000-10-10'::date + all_types.timez_ptr, 'HH24:MI:SS.USTZH:TZM') AS "timezPtr",
'0000-01-01T' || to_char('2000-10-10'::date + all_types.timez, 'HH24:MI:SS.USTZH:TZM') AS "timez",
'0000-01-01T' || to_char('2000-10-10'::date + all_types.time_ptr, 'HH24:MI:SS.USZ') AS "timePtr",
'0000-01-01T' || to_char('2000-10-10'::date + all_types.time, 'HH24:MI:SS.USZ') AS "time",
all_types.interval_ptr AS "intervalPtr",
all_types.interval AS "interval",
all_types.boolean_ptr AS "booleanPtr",
all_types.boolean AS "boolean",
all_types.point_ptr AS "pointPtr",
all_types.bit_ptr AS "bitPtr",
all_types.bit AS "bit",
all_types.bit_varying_ptr AS "bitVaryingPtr",
all_types.bit_varying AS "bitVarying",
all_types.tsvector_ptr AS "tsvectorPtr",
all_types.tsvector AS "tsvector",
all_types.uuid_ptr AS "uuidPtr",
all_types.uuid AS "uuid",
all_types.xml_ptr AS "xmlPtr",
all_types.xml AS "xml",
all_types.mood_ptr AS "moodPtr",
all_types.mood AS "mood",
all_types.json_ptr::text AS "jsonPtr",
all_types.json::text AS "JSON",
all_types.jsonb_ptr::text AS "jsonbPtr",
all_types.jsonb::text AS "Jsonb",
all_types.text_array_ptr::text AS "TextArrayPtr",
all_types.text_array::text AS "TextArray",
all_types.jsonb_array::text AS "JsonbArray",
all_types.integer_array::text AS "IntegerArray",
all_types.integer_array_ptr::text AS "IntegerArrayPtr",
all_types.text_multi_dim_array::text AS "TextMultiDimArray",
all_types.text_multi_dim_array_ptr::text AS "TextMultiDimArrayPtr"
FROM test_sample.all_types
) AS records;
`)
var dest []model.AllTypes
@ -76,24 +145,30 @@ func TestAllTypesSelectJson(t *testing.T) {
dest[1].CharPtr = allTypesRow1.CharPtr
}
// set time local before comparison
dest[0].Timestampz = toCET(t, dest[0].Timestampz)
minus8 := time.FixedZone("UTC", -8*60*60)
plus1 := time.FixedZone("UTC", 60*60)
if dest[0].TimestampzPtr != nil {
dest[0].TimestampzPtr = ptr.Of(toCET(t, *dest[0].TimestampzPtr))
}
dest[1].Timestampz = toCET(t, dest[1].Timestampz)
// set time local before comparison
dest[0].Timez = *toTZ(&dest[0].Timez, minus8)
dest[0].TimezPtr = toTZ(dest[0].TimezPtr, minus8)
dest[1].Timez = *toTZ(&dest[1].Timez, minus8)
dest[1].TimezPtr = toTZ(dest[1].TimezPtr, minus8)
dest[0].Timestampz = *toTZ(&dest[0].Timestampz, plus1)
dest[0].TimestampzPtr = toTZ(dest[0].TimestampzPtr, plus1)
dest[1].Timestampz = *toTZ(&dest[1].Timestampz, plus1)
dest[1].TimestampzPtr = toTZ(dest[1].TimestampzPtr, plus1)
testutils.AssertJsonEqual(t, dest[0], allTypesRow0)
testutils.AssertJsonEqual(t, dest[1], allTypesRow1)
}
func toCET(t *testing.T, tm time.Time) time.Time {
cet, err := time.LoadLocation("CET") // "Europe/Berlin" also works
if err != nil {
t.Fail()
func toTZ(tm *time.Time, loc *time.Location) *time.Time {
if tm == nil {
return nil
}
return tm.In(cet)
return ptr.Of(tm.In(loc))
}
func TestAllTypesViewSelect(t *testing.T) {
@ -192,7 +267,7 @@ WHERE all_types.uuid = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11';
requireLogged(t, query)
}
func TestBytea(t *testing.T) {
func TestByteaInsert(t *testing.T) {
byteArrHex := "\\x48656c6c6f20476f7068657221"
byteArrBin := []byte("\x48\x65\x6c\x6c\x6f\x20\x47\x6f\x70\x68\x65\x72\x21")
@ -602,22 +677,22 @@ func TestStringOperators(t *testing.T) {
require.NoError(t, err)
}
func TestBlob(t *testing.T) {
func TestBytea(t *testing.T) {
var sampleBlob = Bytea([]byte{11, 0, 22, 33, 44})
var textBlob = Bytea([]byte("text blob"))
var sampleBytea = Bytea([]byte{11, 0, 22, 33, 44})
var textBytea = Bytea([]byte("text blob"))
stmt := SELECT(
AllTypes.Bytea.EQ(sampleBlob),
AllTypes.Bytea.EQ(sampleBytea),
AllTypes.Bytea.EQ(AllTypes.ByteaPtr),
AllTypes.Bytea.NOT_EQ(sampleBlob),
AllTypes.Bytea.GT(textBlob),
AllTypes.Bytea.NOT_EQ(sampleBytea),
AllTypes.Bytea.GT(textBytea),
AllTypes.Bytea.GT_EQ(AllTypes.ByteaPtr),
AllTypes.Bytea.LT(AllTypes.ByteaPtr),
AllTypes.Bytea.LT_EQ(sampleBlob),
AllTypes.Bytea.LT_EQ(sampleBytea),
AllTypes.Bytea.BETWEEN(Bytea([]byte("min")), Bytea([]byte("max"))),
AllTypes.Bytea.NOT_BETWEEN(AllTypes.Bytea, AllTypes.ByteaPtr),
AllTypes.Bytea.CONCAT(textBlob),
AllTypes.Bytea.CONCAT(textBytea),
func() ProjectionList {
if sourceIsCockroachDB() {
@ -629,25 +704,25 @@ func TestBlob(t *testing.T) {
AllTypes.Bytea.NOT_LIKE(Bytea("b'%pattern%'")),
BTRIM(AllTypes.Bytea, Bytea([]byte{33})),
RTRIM(AllTypes.ByteaPtr, sampleBlob),
LTRIM(sampleBlob, textBlob),
CONCAT(sampleBlob, AllTypes.ByteaPtr, textBlob),
BIT_COUNT(sampleBlob).EQ(Int(3)),
LENGTH(textBlob, UTF8).EQ(Int(4)),
RTRIM(AllTypes.ByteaPtr, sampleBytea),
LTRIM(sampleBytea, textBytea),
CONCAT(sampleBytea, AllTypes.ByteaPtr, textBytea),
BIT_COUNT(sampleBytea).EQ(Int(3)),
LENGTH(textBytea, UTF8).EQ(Int(4)),
CONVERT(textBlob, UTF8, WIN1252),
CONVERT(AllTypes.Bytea, UTF8, LATIN1).EQ(sampleBlob),
CONVERT(textBytea, UTF8, WIN1252),
CONVERT(AllTypes.Bytea, UTF8, LATIN1).EQ(sampleBytea),
}
}(),
BIT_LENGTH(textBlob),
OCTET_LENGTH(textBlob),
BIT_LENGTH(textBytea),
OCTET_LENGTH(textBytea),
GET_BIT(textBlob, Int(2)).EQ(Int(23)),
GET_BYTE(sampleBlob, Int(1)).EQ(Int(0)),
SET_BIT(textBlob, Int(1), Int(0)).EQ(sampleBlob),
SET_BYTE(textBlob, Int(1), Int(0)).EQ(textBlob),
LENGTH(sampleBlob),
GET_BIT(textBytea, Int(2)).EQ(Int(23)),
GET_BYTE(sampleBytea, Int(1)).EQ(Int(0)),
SET_BIT(textBytea, Int(1), Int(0)).EQ(sampleBytea),
SET_BYTE(textBytea, Int(1), Int(0)).EQ(textBytea),
LENGTH(sampleBytea),
SUBSTR(AllTypes.Bytea, Int(0), Int(2)),
@ -657,16 +732,16 @@ func TestBlob(t *testing.T) {
SHA384(AllTypes.Bytea),
SHA512(AllTypes.Bytea),
ENCODE(sampleBlob, Base64),
DECODE(String("A234C12B"), Hex).EQ(sampleBlob),
ENCODE(sampleBytea, Base64),
DECODE(String("A234C12B"), Hex).EQ(sampleBytea),
CONVERT_FROM(AllTypes.ByteaPtr, UTF8).EQ(AllTypes.VarChar),
CONVERT_TO(AllTypes.Text, UTF8).NOT_EQ(textBlob),
CONVERT_TO(AllTypes.Text, UTF8).NOT_EQ(textBytea),
RawBytea("DECODE(#1::text, #2)", RawArgs{
"#1": "A234C12B",
"#2": "hex",
}).EQ(sampleBlob),
}).EQ(sampleBytea),
).FROM(
AllTypes,
)
@ -690,27 +765,27 @@ SELECT all_types.bytea = $1::bytea,
LTRIM($12::bytea, $13::bytea),
CONCAT($14::bytea, all_types.bytea_ptr, $15::bytea),
BIT_COUNT($16::bytea) = $17,
LENGTH($18::bytea, $19::text) = $20,
CONVERT($21::bytea, $22::text, $23::text),
CONVERT(all_types.bytea, $24::text, $25::text) = $26::bytea,
BIT_LENGTH($27::bytea),
OCTET_LENGTH($28::bytea),
GET_BIT($29::bytea, $30) = $31,
GET_BYTE($32::bytea, $33) = $34,
SET_BIT($35::bytea, $36, $37) = $38::bytea,
SET_BYTE($39::bytea, $40, $41) = $42::bytea,
LENGTH($43::bytea),
SUBSTR(all_types.bytea, $44, $45),
LENGTH($18::bytea, 'UTF8') = $19,
CONVERT($20::bytea, 'UTF8', 'WIN1252'),
CONVERT(all_types.bytea, 'UTF8', 'LATIN1') = $21::bytea,
BIT_LENGTH($22::bytea),
OCTET_LENGTH($23::bytea),
GET_BIT($24::bytea, $25) = $26,
GET_BYTE($27::bytea, $28) = $29,
SET_BIT($30::bytea, $31, $32) = $33::bytea,
SET_BYTE($34::bytea, $35, $36) = $37::bytea,
LENGTH($38::bytea),
SUBSTR(all_types.bytea, $39, $40),
MD5(all_types.bytea),
SHA224(all_types.bytea),
SHA256(all_types.bytea),
SHA384(all_types.bytea),
SHA512(all_types.bytea),
ENCODE($46::bytea, $47::text),
DECODE($48::text, $49::text) = $50::bytea,
CONVERT_FROM(all_types.bytea_ptr, $51::text) = all_types.var_char,
CONVERT_TO(all_types.text, $52::text) != $53::bytea,
(DECODE($54::text, $55)) = $56::bytea
ENCODE($41::bytea, 'base64'),
DECODE($42::text, 'hex') = $43::bytea,
CONVERT_FROM(all_types.bytea_ptr, 'UTF8') = all_types.var_char,
CONVERT_TO(all_types.text, 'UTF8') != $44::bytea,
(DECODE($45::text, $46)) = $47::bytea
FROM test_sample.all_types;
`)
}
@ -727,28 +802,75 @@ func TestBlobConversion(t *testing.T) {
printable := []byte("this is blob")
stmt := SELECT(
Bytea(nonPrintable).AS("non_printable"),
Bytea(printable).AS("printable"),
Bytea(nonPrintable).AS("test_dest.non_printable"),
Bytea(printable).AS("test_dest.printable"),
ENCODE(Bytea(nonPrintable), Base64).AS("non_printable_base64"),
CONVERT_FROM(Bytea(printable), UTF8).AS("printable_utf8"),
Bytea(nonPrintable).CONCAT(Bytea(printable)).AS("test_dest.bytea_concat"),
ENCODE(Bytea(nonPrintable), Base64).AS("test_dest.non_printable_base64"),
CONVERT_FROM(Bytea(printable), UTF8).AS("test_dest.printable_utf8"),
)
var dest struct {
testutils.AssertDebugStatementSql(t, stmt, `
SELECT '\x0b16212c37'::bytea AS "test_dest.non_printable",
'\x7468697320697320626c6f62'::bytea AS "test_dest.printable",
('\x0b16212c37'::bytea || '\x7468697320697320626c6f62'::bytea) AS "test_dest.bytea_concat",
ENCODE('\x0b16212c37'::bytea, 'base64') AS "test_dest.non_printable_base64",
CONVERT_FROM('\x7468697320697320626c6f62'::bytea, 'UTF8') AS "test_dest.printable_utf8";
`)
type testDest struct {
NonPrintable []byte
Printable []byte
NonPrintableBase64 []byte
ByteaConcat []byte
NonPrintableBase64 string
PrintableUTF8 string
}
var dest testDest
err := stmt.Query(db, &dest)
require.NoError(t, err)
require.Equal(t, dest.NonPrintable, nonPrintable)
require.Equal(t, dest.Printable, printable)
require.Equal(t, dest.NonPrintableBase64, []byte(base64.StdEncoding.EncodeToString(nonPrintable)))
require.Equal(t, dest.ByteaConcat, append(nonPrintable, printable...))
require.Equal(t, dest.NonPrintableBase64, base64.StdEncoding.EncodeToString(nonPrintable))
require.Equal(t, dest.PrintableUTF8, string(printable))
t.Run("using select json", func(t *testing.T) {
stmtJson := SELECT_JSON_OBJ(
Bytea(nonPrintable).AS("nonPrintable"),
Bytea(printable).AS("printable"),
Bytea(nonPrintable).CONCAT(Bytea(printable)).AS("byteaConcat"),
ENCODE(Bytea(nonPrintable), Base64).AS("nonPrintableBase64"),
CONVERT_FROM(Bytea(printable), UTF8).AS("printableUtf8"),
)
testutils.AssertStatementSql(t, stmtJson, `
SELECT row_to_json(records) AS "json"
FROM (
SELECT ENCODE($1::bytea, 'base64') AS "nonPrintable",
ENCODE($2::bytea, 'base64') AS "printable",
ENCODE($3::bytea || $4::bytea, 'base64') AS "byteaConcat",
ENCODE($5::bytea, 'base64') AS "nonPrintableBase64",
CONVERT_FROM($6::bytea, 'UTF8') AS "printableUtf8"
) AS records;
`)
var destSelectJson testDest
err := stmtJson.QueryJSON(ctx, db, &destSelectJson)
require.NoError(t, err)
testutils.PrintJson(destSelectJson)
require.Equal(t, dest, destSelectJson)
})
}
func TestBoolOperators(t *testing.T) {
@ -1140,6 +1262,190 @@ func TestTimeExpression(t *testing.T) {
require.NoError(t, err)
}
func TestTimeScan(t *testing.T) {
loc, err := time.LoadLocation("Japan")
require.NoError(t, err)
timeT := time.Date(3, 3, 3, 11, 22, 33, 0, time.UTC)
timeWithNanoSeconds := time.Date(3, 3, 3, 1, 2, 3, 1000, time.UTC)
timez := time.Date(3, 3, 3, 7, 8, 9, 0, time.UTC)
timezWithNanoSeconds := time.Date(3, 3, 3, 4, 5, 6, 1000, loc)
// '1999-01-08 04:05:06'
timestamp := time.Date(1999, 01, 8, 4, 5, 6, 0, time.UTC)
timestampWithNanoSeconds := time.Date(3, 3, 3, 8, 9, 10, 1000, time.UTC)
timestampz := time.Date(2003, 10, 3, 9, 10, 11, 0, loc)
timestampzWithNanoSeconds := time.Date(3, 3, 3, 8, 9, 10, 1000, loc)
date := time.Date(2010, 2, 3, 0, 0, 0, 0, time.UTC)
stmt := SELECT(
TimeT(timeT).AS("time"),
TimeT(timeWithNanoSeconds).AS("timeWithNanoSeconds"),
TimezT(timez).AS("timez"),
TimezT(timezWithNanoSeconds).AS("timezWithNanoSeconds"),
Timestamp(1999, 01, 8, 4, 5, 6).AS("timestamp"),
TimestampT(timestampWithNanoSeconds).AS("timestampWithNanoSeconds"),
TimestampzT(timestampz).AS("timestampz"),
TimestampzT(timestampzWithNanoSeconds).AS("timestampzWithNanoSeconds"),
DateT(date).AS("date"),
TimeT(timeT).ADD(INTERVAL(2, HOUR)).AS("timeExpression"),
SELECT_JSON_OBJ(
TimeT(timeT).AS("time"),
TimeT(timeWithNanoSeconds).AS("timeWithNanoSeconds"),
TimezT(timez).AS("timez"),
TimezT(timezWithNanoSeconds).AS("timezWithNanoSeconds"),
TimestampT(timestamp).AS("timestamp"),
TimestampT(timestampWithNanoSeconds).AS("timestampWithNanoSeconds"),
TimestampzT(timestampz).AS("timestampz"),
TimestampzT(timestampzWithNanoSeconds).AS("timestampzWithNanoSeconds"),
DateT(date).AS("date"),
TimeT(timeT).ADD(INTERVAL(2, HOUR)).AS("timeExpression"),
).AS("json"),
)
testutils.AssertStatementSql(t, stmt, `
SELECT $1::time without time zone AS "time",
$2::time without time zone AS "timeWithNanoSeconds",
$3::time with time zone AS "timez",
$4::time with time zone AS "timezWithNanoSeconds",
$5::timestamp without time zone AS "timestamp",
$6::timestamp without time zone AS "timestampWithNanoSeconds",
$7::timestamp with time zone AS "timestampz",
$8::timestamp with time zone AS "timestampzWithNanoSeconds",
$9::date AS "date",
($10::time without time zone + INTERVAL '2 HOUR') AS "timeExpression",
(
SELECT row_to_json(json_records) AS "json_json"
FROM (
SELECT '0000-01-01T' || to_char('2000-10-10'::date + $11::time without time zone, 'HH24:MI:SS.USZ') AS "time",
'0000-01-01T' || to_char('2000-10-10'::date + $12::time without time zone, 'HH24:MI:SS.USZ') AS "timeWithNanoSeconds",
'0000-01-01T' || to_char('2000-10-10'::date + $13::time with time zone, 'HH24:MI:SS.USTZH:TZM') AS "timez",
'0000-01-01T' || to_char('2000-10-10'::date + $14::time with time zone, 'HH24:MI:SS.USTZH:TZM') AS "timezWithNanoSeconds",
to_char($15::timestamp without time zone, 'YYYY-MM-DD"T"HH24:MI:SS.USZ') AS "timestamp",
to_char($16::timestamp without time zone, 'YYYY-MM-DD"T"HH24:MI:SS.USZ') AS "timestampWithNanoSeconds",
$17::timestamp with time zone AS "timestampz",
$18::timestamp with time zone AS "timestampzWithNanoSeconds",
to_char($19::date::timestamp, 'YYYY-MM-DD') || 'T00:00:00Z' AS "date",
'0000-01-01T' || to_char('2000-10-10'::date + ($20::time without time zone + INTERVAL '2 HOUR'), 'HH24:MI:SS.USZ') AS "timeExpression"
) AS json_records
) AS "json";
`)
var dest struct {
Time time.Time
TimeWithNanoSeconds time.Time
Timez time.Time
TimezWithNanoSeconds time.Time
Timestamp time.Time
TimestampWithNanoSeconds time.Time
Timestampz time.Time
TimestampzWithNanoSeconds time.Time
Date time.Time
TimeExpression time.Time
Json struct {
Time time.Time
TimeWithNanoSeconds time.Time
Timez time.Time
TimezWithNanoSeconds time.Time
Timestamp time.Time
TimestampWithNanoSeconds time.Time
Timestampz time.Time
TimestampzWithNanoSeconds time.Time
Date time.Time
TimeExpression time.Time
} `json_column:"json"`
}
err = stmt.Query(db, &dest)
require.NoError(t, err)
ensureTimezEqual(t, timeT.Add(2*time.Hour), dest.TimeExpression, loc)
ensureTimezEqual(t, timeT.Add(2*time.Hour), dest.Json.TimeExpression, loc)
ensureTimezEqual(t, timeT, dest.Time, loc)
ensureTimezEqual(t, timeT, dest.Json.Time, loc)
ensureTimezEqual(t, timeWithNanoSeconds, dest.TimeWithNanoSeconds, loc)
ensureTimezEqual(t, timeWithNanoSeconds, dest.Json.TimeWithNanoSeconds, loc)
ensureTimezEqual(t, timez, dest.Timez, loc)
ensureTimezEqual(t, timez, dest.Json.Timez, loc)
ensureTimezEqual(t, timezWithNanoSeconds, dest.TimezWithNanoSeconds, loc)
ensureTimezEqual(t, timezWithNanoSeconds, dest.Json.TimezWithNanoSeconds, loc)
ensureTimezEqual(t, timestamp, dest.Timestamp, loc)
ensureTimezEqual(t, timestamp, dest.Json.Timestamp, loc)
ensureTimezEqual(t, timestampWithNanoSeconds, dest.TimestampWithNanoSeconds, loc)
ensureTimezEqual(t, timestampWithNanoSeconds, dest.Json.TimestampWithNanoSeconds, loc)
ensureTimezEqual(t, timestampz, dest.Timestampz, loc)
ensureTimezEqual(t, timestampz, dest.Json.Timestampz, loc)
ensureTimezEqual(t, timestampzWithNanoSeconds, dest.TimestampzWithNanoSeconds, loc)
ensureTimezEqual(t, timestampzWithNanoSeconds, dest.Json.TimestampzWithNanoSeconds, loc)
ensureTimezEqual(t, date, dest.Date, loc)
ensureTimezEqual(t, date, dest.Json.Date, loc)
t.Run("json only", func(t *testing.T) {
stmtJson := SELECT_JSON_OBJ(
TimeT(timeT).AS("time"),
TimeT(timeWithNanoSeconds).AS("timeWithNanoSeconds"),
TimezT(timez).AS("timez"),
TimezT(timezWithNanoSeconds).AS("timezWithNanoSeconds"),
Timestamp(1999, 01, 8, 4, 5, 6).AS("timestamp"),
TimestampT(timestampWithNanoSeconds).AS("timestampWithNanoSeconds"),
TimestampzT(timestampz).AS("timestampz"),
TimestampzT(timestampzWithNanoSeconds).AS("timestampzWithNanoSeconds"),
DateT(date).AS("date"),
)
var jsonDest struct {
Time time.Time
TimeWithNanoSeconds time.Time
Timez time.Time
TimezWithNanoSeconds time.Time
Timestamp time.Time
TimestampWithNanoSeconds time.Time
Timestampz time.Time
TimestampzWithNanoSeconds time.Time
Date time.Time
}
err := stmtJson.QueryJSON(ctx, db, &jsonDest)
require.NoError(t, err)
})
}
func ensureTimezEqual(t *testing.T, time1, time2 time.Time, loc *time.Location) {
time1Loc := time1.In(loc)
time2Loc := time2.In(loc)
require.Equal(t, time1Loc.Hour(), time2Loc.Hour())
require.Equal(t, time1Loc.Minute(), time2Loc.Minute())
require.Equal(t, time1Loc.Second(), time2Loc.Second())
require.Equal(t, toMicroSeconds(time1Loc.Nanosecond()), toMicroSeconds(time2Loc.Nanosecond()))
}
func toMicroSeconds(nanoseconds int) int {
return nanoseconds / 1000
}
func TestIntervalSetFunctionality(t *testing.T) {
t.Run("updateQueryIntervalTest", func(t *testing.T) {
@ -1251,7 +1557,50 @@ func TestInterval(t *testing.T) {
AllTypes.IntervalPtr.DIV(Float(22.222)).EQ(AllTypes.IntervalPtr),
).FROM(AllTypes)
//fmt.Println(stmt.DebugSql())
fmt.Println(stmt.Sql())
testutils.AssertDebugStatementSql(t, stmt, `
SELECT INTERVAL '1 YEAR',
INTERVAL '1 MONTH',
INTERVAL '1 WEEK',
INTERVAL '1 DAY',
INTERVAL '1 HOUR',
INTERVAL '1 MINUTE',
INTERVAL '1 SECOND',
INTERVAL '1 MILLISECOND',
INTERVAL '1 MICROSECOND',
INTERVAL '1 DECADE',
INTERVAL '1 CENTURY',
INTERVAL '1 MILLENNIUM',
INTERVAL '1 YEAR 10 MONTH',
INTERVAL '1 YEAR 10 MONTH 20 DAY',
INTERVAL '1 YEAR 10 MONTH 20 DAY 3 HOUR',
INTERVAL '1 YEAR' IS NOT NULL,
INTERVAL '1 YEAR' AS "one year",
INTERVAL '0 MICROSECOND',
INTERVAL '1 MICROSECOND',
INTERVAL '1000 MICROSECOND',
INTERVAL '1 SECOND',
INTERVAL '1 MINUTE',
INTERVAL '1 HOUR',
INTERVAL '1 DAY',
INTERVAL '1 DAY 2 HOUR 3 MINUTE 4 SECOND 5 MICROSECOND',
(all_types.interval = INTERVAL '2 HOUR 20 MINUTE') = TRUE::boolean,
(all_types.interval_ptr != INTERVAL '2 HOUR 20 MINUTE') = FALSE::boolean,
(all_types.interval IS DISTINCT FROM INTERVAL '2 HOUR 20 MINUTE') = all_types.boolean,
(all_types.interval_ptr IS NOT DISTINCT FROM INTERVAL '10 MICROSECOND') = all_types.boolean,
(all_types.interval < all_types.interval_ptr) = all_types.boolean_ptr,
(all_types.interval <= all_types.interval_ptr) = all_types.boolean_ptr,
(all_types.interval > all_types.interval_ptr) = all_types.boolean_ptr,
(all_types.interval >= all_types.interval_ptr) = all_types.boolean_ptr,
all_types.interval BETWEEN INTERVAL '1 HOUR' AND INTERVAL '2 HOUR',
all_types.interval NOT BETWEEN all_types.interval_ptr AND INTERVAL '30 SECOND',
(all_types.interval + all_types.interval_ptr) = INTERVAL '17 SECOND',
(all_types.interval - all_types.interval_ptr) = INTERVAL '100 MICROSECOND',
(all_types.interval_ptr * 11) = all_types.interval,
(all_types.interval_ptr / 22.222) = all_types.interval_ptr
FROM test_sample.all_types;
`)
err := stmt.Query(db, &struct{}{})
require.NoError(t, err)
@ -1368,6 +1717,7 @@ func TestAllTypesSubQueryFrom(t *testing.T) {
AllTypes.Time,
AllTypes.Timez,
AllTypes.Timestamp,
AllTypes.Timestampz,
AllTypes.Interval,
AllTypes.Bytea,
).FROM(
@ -1383,6 +1733,7 @@ func TestAllTypesSubQueryFrom(t *testing.T) {
AllTypes.Time.From(subQuery),
AllTypes.Timez.From(subQuery),
AllTypes.Timestamp.From(subQuery),
AllTypes.Timestampz.From(subQuery),
AllTypes.Interval.From(subQuery),
AllTypes.Bytea.From(subQuery),
).FROM(
@ -1398,6 +1749,7 @@ SELECT "subQuery"."all_types.boolean" AS "all_types.boolean",
"subQuery"."all_types.time" AS "all_types.time",
"subQuery"."all_types.timez" AS "all_types.timez",
"subQuery"."all_types.timestamp" AS "all_types.timestamp",
"subQuery"."all_types.timestampz" AS "all_types.timestampz",
"subQuery"."all_types.interval" AS "all_types.interval",
"subQuery"."all_types.bytea" AS "all_types.bytea"
FROM (
@ -1409,6 +1761,7 @@ FROM (
all_types.time AS "all_types.time",
all_types.timez AS "all_types.timez",
all_types.timestamp AS "all_types.timestamp",
all_types.timestampz AS "all_types.timestampz",
all_types.interval AS "all_types.interval",
all_types.bytea AS "all_types.bytea"
FROM test_sample.all_types
@ -1419,6 +1772,83 @@ FROM (
err := stmt.Query(db, &dest)
require.NoError(t, err)
t.Run("using SELECT_JSON", func(t *testing.T) {
stmtJson := SELECT_JSON_ARR(
AllTypes.Boolean.From(subQuery),
AllTypes.Integer.From(subQuery),
AllTypes.DoublePrecision.From(subQuery),
AllTypes.Text.From(subQuery),
AllTypes.Date.From(subQuery),
AllTypes.Time.From(subQuery),
AllTypes.Timez.From(subQuery),
AllTypes.Timestamp.From(subQuery),
AllTypes.Timestampz.From(subQuery),
AllTypes.Interval.From(subQuery),
AllTypes.Bytea.From(subQuery),
).FROM(
subQuery,
)
testutils.AssertDebugStatementSql(t, stmtJson, `
SELECT json_agg(row_to_json(records)) AS "json"
FROM (
SELECT "subQuery"."all_types.boolean" AS "boolean",
"subQuery"."all_types.integer" AS "integer",
"subQuery"."all_types.double_precision" AS "doublePrecision",
"subQuery"."all_types.text" AS "text",
to_char("subQuery"."all_types.date"::timestamp, 'YYYY-MM-DD') || 'T00:00:00Z' AS "date",
'0000-01-01T' || to_char('2000-10-10'::date + "subQuery"."all_types.time", 'HH24:MI:SS.USZ') AS "time",
'0000-01-01T' || to_char('2000-10-10'::date + "subQuery"."all_types.timez", 'HH24:MI:SS.USTZH:TZM') AS "timez",
to_char("subQuery"."all_types.timestamp", 'YYYY-MM-DD"T"HH24:MI:SS.USZ') AS "timestamp",
"subQuery"."all_types.timestampz" AS "timestampz",
"subQuery"."all_types.interval" AS "interval",
ENCODE("subQuery"."all_types.bytea", 'base64') AS "bytea"
FROM (
SELECT all_types.boolean AS "all_types.boolean",
all_types.integer AS "all_types.integer",
all_types.double_precision AS "all_types.double_precision",
all_types.text AS "all_types.text",
all_types.date AS "all_types.date",
all_types.time AS "all_types.time",
all_types.timez AS "all_types.timez",
all_types.timestamp AS "all_types.timestamp",
all_types.timestampz AS "all_types.timestampz",
all_types.interval AS "all_types.interval",
all_types.bytea AS "all_types.bytea"
FROM test_sample.all_types
) AS "subQuery"
) AS records;
`)
var destJson []model.AllTypes
err := stmtJson.QueryJSON(ctx, db, &destJson)
require.NoError(t, err)
t.Run("using AllColumns()", func(t *testing.T) {
stmtJsonAllColumns := SELECT_JSON_ARR(
subQuery.AllColumns(),
).FROM(
subQuery,
)
require.Equal(t, stmtJson.DebugSql(), stmtJsonAllColumns.DebugSql())
})
// fix timezone before comparisons
minus8 := time.FixedZone("UTC", -8*60*60)
destJson[0].Timez = *toTZ(&destJson[0].Timez, minus8)
destJson[1].Timez = *toTZ(&destJson[1].Timez, minus8)
destJson[0].Timestampz = *toTZ(&destJson[0].Timestampz, time.UTC)
destJson[1].Timestampz = *toTZ(&destJson[1].Timestampz, time.UTC)
dest[0].Timestampz = *toTZ(&dest[0].Timestampz, time.UTC)
dest[1].Timestampz = *toTZ(&dest[1].Timestampz, time.UTC)
testutils.AssertJsonEqual(t, dest, destJson)
})
}
func TestAllTypesUpdateSet(t *testing.T) {

View file

@ -251,9 +251,8 @@ func testJoinEverythingJSON(t require.TestingT) {
WHERE(MediaType.MediaTypeId.EQ(Track.MediaTypeId)).AS("MediaType"),
SELECT_JSON_ARR(Playlist.AllColumns).
FROM(Playlist.INNER_JOIN(
PlaylistTrack,
Playlist.PlaylistId.EQ(PlaylistTrack.PlaylistId))).
FROM(Playlist.
INNER_JOIN(PlaylistTrack, Playlist.PlaylistId.EQ(PlaylistTrack.PlaylistId))).
WHERE(PlaylistTrack.TrackId.EQ(Track.TrackId)).
ORDER_BY(Playlist.PlaylistId).AS("Playlists"),
@ -273,9 +272,9 @@ func testJoinEverythingJSON(t require.TestingT) {
WHERE(Employee.EmployeeId.EQ(Customer.SupportRepId)).AS("Employee"),
).FROM(Customer).
WHERE(Customer.CustomerId.EQ(Invoice.CustomerId)).AS("Customer"),
).FROM(Invoice.INNER_JOIN(
InvoiceLine,
InvoiceLine.InvoiceId.EQ(Invoice.InvoiceId)),
).FROM(
Invoice.
INNER_JOIN(InvoiceLine, InvoiceLine.InvoiceId.EQ(Invoice.InvoiceId)),
).WHERE(InvoiceLine.TrackId.EQ(Track.TrackId)).
ORDER_BY(Invoice.InvoiceId).AS("Invoices"),
).FROM(Track).

View file

@ -20,6 +20,8 @@ import (
_ "github.com/jackc/pgx/v4/stdlib"
)
var ctx = context.Background()
var db *stmtcache.DB
var testRoot string

View file

@ -139,7 +139,7 @@ func testNorthwindJoinEverythingJson(t require.TestingT) {
Territories,
EmployeeTerritories.TerritoryID.EQ(Territories.TerritoryID)),
).WHERE(
EmployeeTerritories.EmployeeID.EQ(Employees.EmployeeID), // TODO: move to join
EmployeeTerritories.EmployeeID.EQ(Employees.EmployeeID),
).AS("Territories"),
).FROM(Employees).
WHERE(Orders.EmployeeID.EQ(Employees.EmployeeID)).AS("Employee"),

File diff suppressed because one or more lines are too long