Add support for SELECT_JSON statements.
This commit is contained in:
parent
7047de44a9
commit
7b16e432ff
46 changed files with 2732 additions and 307 deletions
|
|
@ -26,7 +26,7 @@ services:
|
|||
- ./testdata/init/mysql:/docker-entrypoint-initdb.d
|
||||
|
||||
mariadb:
|
||||
image: mariadb:10.3
|
||||
image: mariadb:11.7
|
||||
command: ['--default-authentication-plugin=mysql_native_password', '--log_bin_trust_function_creators=1']
|
||||
restart: always
|
||||
environment:
|
||||
|
|
|
|||
|
|
@ -23,19 +23,63 @@ func TestAllTypes(t *testing.T) {
|
|||
|
||||
var dest []model.AllTypes
|
||||
|
||||
err := AllTypes.
|
||||
SELECT(AllTypes.AllColumns).
|
||||
err := SELECT(AllTypes.AllColumns).
|
||||
FROM(AllTypes).
|
||||
LIMIT(2).
|
||||
Query(db, &dest)
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, len(dest), 2)
|
||||
|
||||
//testutils.PrintJson(dest)
|
||||
testutils.AssertJSON(t, dest, allTypesJson)
|
||||
}
|
||||
|
||||
func TestAllTypesJSON(t *testing.T) {
|
||||
|
||||
stmt := SELECT_JSON_ARR(
|
||||
AllTypes.AllColumns.Except(
|
||||
AllTypes.JSON,
|
||||
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)
|
||||
|
||||
var dest []model.AllTypes
|
||||
|
||||
err := stmt.QueryJSON(ctx, db, &dest)
|
||||
require.NoError(t, err)
|
||||
|
||||
// fix float rounding lost before comparison
|
||||
dest[0].Float = 3.33
|
||||
dest[0].FloatPtr = ptr.Of(3.33)
|
||||
dest[1].Float = 3.33
|
||||
|
||||
//fmt.Println(allTypesJson)
|
||||
testutils.AssertJSON(t, dest, allTypesJson)
|
||||
}
|
||||
|
||||
func TestAllTypesViewSelect(t *testing.T) {
|
||||
|
||||
type AllTypesView model.AllTypes
|
||||
|
|
|
|||
138
tests/mysql/bench_test.go
Normal file
138
tests/mysql/bench_test.go
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
//go:build bench
|
||||
// +build bench
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"github.com/go-jet/jet/v2/internal/testutils"
|
||||
. "github.com/go-jet/jet/v2/mysql"
|
||||
"github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/model"
|
||||
. "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/table"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type allInfo []struct {
|
||||
model.Actor
|
||||
|
||||
Films []struct {
|
||||
model.Film
|
||||
|
||||
Language model.Language
|
||||
Categories []model.Category
|
||||
|
||||
Inventories []struct {
|
||||
model.Inventory
|
||||
|
||||
Rentals []struct {
|
||||
model.Rental
|
||||
|
||||
Customer model.Customer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkTestDVDsJoinEverything(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
testDVDsJoinEverything(b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDVDsJoinEverything(t *testing.T) {
|
||||
testDVDsJoinEverything(t)
|
||||
}
|
||||
|
||||
func testDVDsJoinEverything(t require.TestingT) {
|
||||
stmt := SELECT(
|
||||
Actor.AllColumns,
|
||||
Film.AllColumns,
|
||||
Language.AllColumns,
|
||||
Category.AllColumns,
|
||||
Inventory.AllColumns,
|
||||
Rental.AllColumns,
|
||||
Customer.AllColumns,
|
||||
).FROM(
|
||||
Actor.
|
||||
LEFT_JOIN(FilmActor, Actor.ActorID.EQ(FilmActor.ActorID)).
|
||||
LEFT_JOIN(Film, Film.FilmID.EQ(FilmActor.FilmID)).
|
||||
LEFT_JOIN(Language, Language.LanguageID.EQ(Film.LanguageID)).
|
||||
LEFT_JOIN(FilmCategory, FilmCategory.FilmID.EQ(Film.FilmID)).
|
||||
LEFT_JOIN(Category, Category.CategoryID.EQ(FilmCategory.CategoryID)).
|
||||
LEFT_JOIN(Inventory, Inventory.FilmID.EQ(Film.FilmID)).
|
||||
LEFT_JOIN(Rental, Rental.InventoryID.EQ(Inventory.InventoryID)).
|
||||
LEFT_JOIN(Customer, Customer.CustomerID.EQ(Rental.CustomerID)),
|
||||
).ORDER_BY(
|
||||
Actor.ActorID.ASC(),
|
||||
Film.FilmID.ASC(),
|
||||
Category.CategoryID.ASC(),
|
||||
Inventory.InventoryID.ASC(),
|
||||
Rental.RentalID.ASC(),
|
||||
)
|
||||
|
||||
var dest allInfo
|
||||
|
||||
err := stmt.Query(db, &dest)
|
||||
require.NoError(t, err)
|
||||
|
||||
//testutils.SaveJSONFile(dest, "./testdata/results/mysql/dvds_join_everything.json")
|
||||
testutils.AssertJSONFile(t, dest, "./testdata/results/mysql/dvds_join_everything.json")
|
||||
}
|
||||
|
||||
func BenchmarkTestDVDsJoinEverythingJSON(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
testDVDsJoinEverythingJSON(b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDVDsJoinEverythingJSON(t *testing.T) {
|
||||
testDVDsJoinEverythingJSON(t)
|
||||
}
|
||||
|
||||
func testDVDsJoinEverythingJSON(t require.TestingT) {
|
||||
stmt := SELECT_JSON_ARR(
|
||||
Actor.ActorID, Actor.FirstName, Actor.LastName, Actor.LastUpdate,
|
||||
|
||||
SELECT_JSON_ARR(
|
||||
Film.AllColumns,
|
||||
|
||||
SELECT_JSON_OBJ(Language.AllColumns).
|
||||
FROM(Language).
|
||||
WHERE(Language.LanguageID.EQ(Film.LanguageID)).AS("Language"),
|
||||
|
||||
SELECT_JSON_ARR(Category.AllColumns).
|
||||
FROM(Category.INNER_JOIN(FilmCategory, FilmCategory.CategoryID.EQ(Category.CategoryID))).
|
||||
WHERE(FilmCategory.FilmID.EQ(Film.FilmID)).AS("Categories"),
|
||||
|
||||
SELECT_JSON_ARR(
|
||||
Inventory.AllColumns,
|
||||
|
||||
SELECT_JSON_ARR(
|
||||
Rental.AllColumns,
|
||||
|
||||
SELECT_JSON_OBJ(Customer.AllColumns).
|
||||
FROM(Customer).
|
||||
WHERE(Customer.CustomerID.EQ(Rental.CustomerID)).AS("Customer"),
|
||||
).FROM(Rental).
|
||||
WHERE(Rental.InventoryID.EQ(Inventory.InventoryID)).
|
||||
ORDER_BY(Rental.RentalID).AS("Rentals"),
|
||||
).FROM(Inventory).
|
||||
WHERE(Inventory.FilmID.EQ(Film.FilmID)).
|
||||
ORDER_BY(Inventory.InventoryID).AS("Inventories"),
|
||||
).FROM(Film.
|
||||
INNER_JOIN(FilmActor, FilmActor.FilmID.EQ(Film.FilmID)),
|
||||
).WHERE(FilmActor.ActorID.EQ(Actor.ActorID)).
|
||||
ORDER_BY(Film.FilmID.ASC()).AS("Films"),
|
||||
).FROM(Actor).
|
||||
ORDER_BY(Actor.ActorID.ASC())
|
||||
|
||||
//fmt.Println(stmt.DebugSql())
|
||||
|
||||
var dest allInfo
|
||||
|
||||
err := stmt.QueryJSON(ctx, db, &dest)
|
||||
require.NoError(t, err)
|
||||
|
||||
//testutils.SaveJSONFile(dest, "./testdata/results/mysql/dvds_join_everything2.json")
|
||||
testutils.AssertJSONFile(t, dest, "./testdata/results/mysql/dvds_join_everything.json")
|
||||
}
|
||||
437
tests/mysql/select_json_test.go
Normal file
437
tests/mysql/select_json_test.go
Normal file
|
|
@ -0,0 +1,437 @@
|
|||
package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/go-jet/jet/v2/qrm"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/go-jet/jet/v2/internal/testutils"
|
||||
. "github.com/go-jet/jet/v2/mysql"
|
||||
"github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/model"
|
||||
. "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/table"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var ctx = context.Background()
|
||||
|
||||
func TestSelectJsonObj(t *testing.T) {
|
||||
stmt := SELECT_JSON_OBJ(Actor.AllColumns).
|
||||
FROM(Actor).
|
||||
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"
|
||||
FROM dvds.actor
|
||||
WHERE actor.actor_id = ?;
|
||||
`, int64(2))
|
||||
|
||||
var dest model.Actor
|
||||
|
||||
err := stmt.QueryJSON(ctx, db, &dest)
|
||||
require.Nil(t, err)
|
||||
|
||||
testutils.AssertDeepEqual(t, dest, actor2)
|
||||
requireLogged(t, stmt)
|
||||
requireQueryLogged(t, stmt, 1)
|
||||
}
|
||||
|
||||
func TestSelectJsonObj_NestedObj(t *testing.T) {
|
||||
stmt := SELECT_JSON_OBJ(
|
||||
Actor.AllColumns,
|
||||
|
||||
SELECT_JSON_OBJ(Film.AllColumns).
|
||||
FROM(FilmActor.INNER_JOIN(Film, Film.FilmID.EQ(FilmActor.FilmID))).
|
||||
WHERE(Actor.ActorID.EQ(FilmActor.ActorID)).
|
||||
ORDER_BY(Film.Length.DESC()).
|
||||
LIMIT(1).AS("LongestFilm"),
|
||||
).FROM(
|
||||
Actor,
|
||||
).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,
|
||||
'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"
|
||||
FROM dvds.actor
|
||||
WHERE actor.actor_id = ?;
|
||||
`)
|
||||
|
||||
var dest struct {
|
||||
model.Actor
|
||||
|
||||
LongestFilm model.Film
|
||||
}
|
||||
|
||||
err := stmt.QueryJSON(ctx, db, &dest)
|
||||
require.Nil(t, err)
|
||||
testutils.AssertJSON(t, dest, `
|
||||
{
|
||||
"ActorID": 2,
|
||||
"FirstName": "NICK",
|
||||
"LastName": "WAHLBERG",
|
||||
"LastUpdate": "2006-02-15T04:34:33Z",
|
||||
"LongestFilm": {
|
||||
"FilmID": 958,
|
||||
"Title": "WARDROBE PHANTOM",
|
||||
"Description": "A Action-Packed Display of a Mad Cow And a Astronaut who must Kill a Car in Ancient India",
|
||||
"ReleaseYear": 2006,
|
||||
"LanguageID": 1,
|
||||
"OriginalLanguageID": null,
|
||||
"RentalDuration": 6,
|
||||
"RentalRate": 2.99,
|
||||
"Length": 178,
|
||||
"ReplacementCost": 19.99,
|
||||
"Rating": "G",
|
||||
"SpecialFeatures": "Trailers,Commentaries",
|
||||
"LastUpdate": "2006-02-15T05:03:42Z"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
}
|
||||
|
||||
func TestSelectJsonArr(t *testing.T) {
|
||||
stmt := SELECT_JSON_ARR(Actor.AllColumns).
|
||||
FROM(Actor).
|
||||
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"
|
||||
FROM dvds.actor
|
||||
ORDER BY actor.actor_id;
|
||||
`)
|
||||
|
||||
var dest []model.Actor
|
||||
|
||||
err := stmt.QueryJSON(ctx, db, &dest)
|
||||
require.Nil(t, err)
|
||||
|
||||
testutils.AssertJSONFile(t, dest, "./testdata/results/mysql/all_actors.json")
|
||||
requireLogged(t, stmt)
|
||||
requireQueryLogged(t, stmt, 1)
|
||||
}
|
||||
|
||||
func TestSelectJsonArr_NestedArr(t *testing.T) {
|
||||
stmt := SELECT_JSON_ARR(
|
||||
Actor.AllColumns,
|
||||
|
||||
SELECT_JSON_ARR(
|
||||
Film.AllColumns,
|
||||
).FROM(
|
||||
FilmActor.INNER_JOIN(
|
||||
Film,
|
||||
Film.FilmID.EQ(FilmActor.FilmID).AND(
|
||||
Actor.ActorID.EQ(FilmActor.ActorID)),
|
||||
),
|
||||
).WHERE(
|
||||
Film.FilmID.MOD(Int(17)).EQ(Int(0)),
|
||||
).ORDER_BY(
|
||||
Film.Length.DESC(),
|
||||
).AS("Films"),
|
||||
).FROM(
|
||||
Actor,
|
||||
).WHERE(
|
||||
Actor.ActorID.BETWEEN(Int(1), Int(3)),
|
||||
).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,
|
||||
'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"
|
||||
FROM dvds.actor
|
||||
WHERE actor.actor_id BETWEEN 1 AND 3
|
||||
ORDER BY actor.actor_id;
|
||||
`)
|
||||
|
||||
var dest []struct {
|
||||
model.Actor
|
||||
|
||||
Films []model.Film
|
||||
}
|
||||
|
||||
err := stmt.QueryJSON(ctx, db, &dest)
|
||||
fmt.Println(err)
|
||||
require.Nil(t, err)
|
||||
testutils.AssertJSON(t, dest, `
|
||||
[
|
||||
{
|
||||
"ActorID": 1,
|
||||
"FirstName": "PENELOPE",
|
||||
"LastName": "GUINESS",
|
||||
"LastUpdate": "2006-02-15T04:34:33Z",
|
||||
"Films": null
|
||||
},
|
||||
{
|
||||
"ActorID": 2,
|
||||
"FirstName": "NICK",
|
||||
"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",
|
||||
"Description": "A Boring Character Study of a Student And a Robot who must Meet a Woman in California",
|
||||
"ReleaseYear": 2006,
|
||||
"LanguageID": 1,
|
||||
"OriginalLanguageID": null,
|
||||
"RentalDuration": 6,
|
||||
"RentalRate": 2.99,
|
||||
"Length": 123,
|
||||
"ReplacementCost": 26.99,
|
||||
"Rating": "NC-17",
|
||||
"SpecialFeatures": "Commentaries,Deleted Scenes",
|
||||
"LastUpdate": "2006-02-15T05:03:42Z"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ActorID": 3,
|
||||
"FirstName": "ED",
|
||||
"LastName": "CHASE",
|
||||
"LastUpdate": "2006-02-15T04:34:33Z",
|
||||
"Films": [
|
||||
{
|
||||
"FilmID": 17,
|
||||
"Title": "ALONE TRIP",
|
||||
"Description": "A Fast-Paced Character Study of a Composer And a Dog who must Outgun a Boat in An Abandoned Fun House",
|
||||
"ReleaseYear": 2006,
|
||||
"LanguageID": 1,
|
||||
"OriginalLanguageID": null,
|
||||
"RentalDuration": 3,
|
||||
"RentalRate": 0.99,
|
||||
"Length": 82,
|
||||
"ReplacementCost": 14.99,
|
||||
"Rating": "R",
|
||||
"SpecialFeatures": "Trailers,Behind the Scenes",
|
||||
"LastUpdate": "2006-02-15T05:03:42Z"
|
||||
},
|
||||
{
|
||||
"FilmID": 289,
|
||||
"Title": "EVE RESURRECTION",
|
||||
"Description": "A Awe-Inspiring Yarn of a Pastry Chef And a Database Administrator who must Challenge a Teacher in A Baloon",
|
||||
"ReleaseYear": 2006,
|
||||
"LanguageID": 1,
|
||||
"OriginalLanguageID": null,
|
||||
"RentalDuration": 5,
|
||||
"RentalRate": 4.99,
|
||||
"Length": 66,
|
||||
"ReplacementCost": 25.99,
|
||||
"Rating": "G",
|
||||
"SpecialFeatures": "Trailers,Commentaries,Deleted Scenes",
|
||||
"LastUpdate": "2006-02-15T05:03:42Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
`)
|
||||
|
||||
}
|
||||
|
||||
func TestSelectJson_GroupBy(t *testing.T) {
|
||||
skipForMariaDB(t) // scope issues with select without FROM
|
||||
|
||||
subQuery := SELECT(
|
||||
Customer.AllColumns,
|
||||
|
||||
SUM(Payment.Amount).AS("sum"),
|
||||
AVG(Payment.Amount).AS("avg"),
|
||||
MAX(Payment.Amount).AS("max"),
|
||||
MIN(Payment.Amount).AS("min"),
|
||||
COUNT(Payment.Amount).AS("count"),
|
||||
).FROM(
|
||||
Payment.
|
||||
INNER_JOIN(Customer, Customer.CustomerID.EQ(Payment.CustomerID)),
|
||||
).GROUP_BY(
|
||||
Customer.CustomerID,
|
||||
).HAVING(
|
||||
SUMf(Payment.Amount).GT(Float(125)),
|
||||
).ORDER_BY(
|
||||
Customer.CustomerID, SUM(Payment.Amount).ASC(),
|
||||
).AsTable("customers_info")
|
||||
|
||||
stmt := SELECT_JSON_ARR(
|
||||
subQuery.AllColumns().Except( // TODO: remove when ColumnList.From() is implemented
|
||||
FloatColumn("sum"),
|
||||
FloatColumn("avg"),
|
||||
FloatColumn("max"),
|
||||
FloatColumn("min"),
|
||||
FloatColumn("count"),
|
||||
),
|
||||
|
||||
SELECT_JSON_OBJ(
|
||||
FloatColumn("sum").From(subQuery),
|
||||
FloatColumn("avg").From(subQuery),
|
||||
FloatColumn("max").From(subQuery),
|
||||
FloatColumn("min").From(subQuery),
|
||||
FloatColumn("count").From(subQuery),
|
||||
).AS("amount"),
|
||||
).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"
|
||||
FROM (
|
||||
SELECT customer.customer_id AS "customer.customer_id",
|
||||
customer.store_id AS "customer.store_id",
|
||||
customer.first_name AS "customer.first_name",
|
||||
customer.last_name AS "customer.last_name",
|
||||
customer.email AS "customer.email",
|
||||
customer.address_id AS "customer.address_id",
|
||||
customer.active AS "customer.active",
|
||||
customer.create_date AS "customer.create_date",
|
||||
customer.last_update AS "customer.last_update",
|
||||
SUM(payment.amount) AS "sum",
|
||||
AVG(payment.amount) AS "avg",
|
||||
MAX(payment.amount) AS "max",
|
||||
MIN(payment.amount) AS "min",
|
||||
COUNT(payment.amount) AS "count"
|
||||
FROM dvds.payment
|
||||
INNER JOIN dvds.customer ON (customer.customer_id = payment.customer_id)
|
||||
GROUP BY customer.customer_id
|
||||
HAVING SUM(payment.amount) > 125
|
||||
ORDER BY customer.customer_id, SUM(payment.amount) ASC
|
||||
) AS customers_info;
|
||||
`, `""`, "`"))
|
||||
|
||||
var dest []struct {
|
||||
model.Customer
|
||||
|
||||
Amount struct {
|
||||
Sum float64
|
||||
Avg float64
|
||||
Max float64
|
||||
Min float64
|
||||
Count int64
|
||||
}
|
||||
}
|
||||
|
||||
err := stmt.QueryJSON(ctx, db, &dest)
|
||||
fmt.Println(err)
|
||||
require.Nil(t, err)
|
||||
|
||||
testutils.AssertJSONFile(t, dest, "./testdata/results/mysql/customer_payment_sum.json")
|
||||
requireLogged(t, stmt)
|
||||
}
|
||||
|
||||
func TestSelectJsonObject_EmptyResult(t *testing.T) {
|
||||
|
||||
t.Run("json obj", func(t *testing.T) {
|
||||
stmt := SELECT_JSON_OBJ(Actor.AllColumns).
|
||||
FROM(Actor).
|
||||
WHERE(Actor.FirstName.EQ(String("Kowalski")))
|
||||
|
||||
var dest model.Actor
|
||||
|
||||
err := stmt.QueryJSON(ctx, db, &dest)
|
||||
require.ErrorIs(t, err, qrm.ErrNoRows)
|
||||
})
|
||||
|
||||
t.Run("json arr", func(t *testing.T) {
|
||||
stmt := SELECT_JSON_ARR(Actor.AllColumns).
|
||||
FROM(Actor).
|
||||
WHERE(Actor.FirstName.EQ(String("Kowalski")))
|
||||
|
||||
var dest []model.Actor
|
||||
|
||||
err := stmt.QueryJSON(ctx, db, &dest)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, dest)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSelectJson_ProjectionNotAliased(t *testing.T) {
|
||||
|
||||
t.Run("expression not aliased", func(t *testing.T) {
|
||||
testutils.AssertPanicErr(t, func() {
|
||||
stmt := SELECT_JSON_ARR(
|
||||
Int(2).ADD(Customer.CustomerID),
|
||||
).FROM(Customer)
|
||||
|
||||
stmt.DebugSql()
|
||||
|
||||
}, "jet: expression need to be aliased when used as SELECT JSON projection.")
|
||||
})
|
||||
}
|
||||
|
|
@ -19,9 +19,9 @@ import (
|
|||
)
|
||||
|
||||
func TestSelect_ScanToStruct(t *testing.T) {
|
||||
query := Actor.
|
||||
SELECT(Actor.AllColumns).
|
||||
query := SELECT(Actor.AllColumns).
|
||||
DISTINCT().
|
||||
FROM(Actor).
|
||||
WHERE(Actor.ActorID.EQ(Int(2)))
|
||||
|
||||
testutils.AssertStatementSql(t, query, `
|
||||
|
|
@ -50,9 +50,56 @@ var actor2 = model.Actor{
|
|||
LastUpdate: *testutils.TimestampWithoutTimeZone("2006-02-15 04:34:33", 2),
|
||||
}
|
||||
|
||||
func TestSelect_NestedObject(t *testing.T) {
|
||||
stmt := SELECT(
|
||||
Actor.AllColumns,
|
||||
Film.AllColumns,
|
||||
).FROM(
|
||||
Actor.
|
||||
LEFT_JOIN(FilmActor, FilmActor.ActorID.EQ(Actor.ActorID)).
|
||||
LEFT_JOIN(Film, Film.FilmID.EQ(FilmActor.FilmID)),
|
||||
).WHERE(
|
||||
Actor.ActorID.EQ(Int(2)),
|
||||
).ORDER_BY(
|
||||
Film.LastUpdate.DESC(),
|
||||
).LIMIT(1)
|
||||
|
||||
var dest struct {
|
||||
model.Actor
|
||||
|
||||
LatestFilm model.Film
|
||||
}
|
||||
|
||||
err := stmt.Query(db, &dest)
|
||||
require.NoError(t, err)
|
||||
testutils.AssertJSON(t, dest, `
|
||||
{
|
||||
"ActorID": 2,
|
||||
"FirstName": "NICK",
|
||||
"LastName": "WAHLBERG",
|
||||
"LastUpdate": "2006-02-15T04:34:33Z",
|
||||
"LatestFilm": {
|
||||
"FilmID": 3,
|
||||
"Title": "ADAPTATION HOLES",
|
||||
"Description": "A Astounding Reflection of a Lumberjack And a Car who must Sink a Lumberjack in A Baloon Factory",
|
||||
"ReleaseYear": 2006,
|
||||
"LanguageID": 1,
|
||||
"OriginalLanguageID": null,
|
||||
"RentalDuration": 7,
|
||||
"RentalRate": 2.99,
|
||||
"Length": 50,
|
||||
"ReplacementCost": 18.99,
|
||||
"Rating": "NC-17",
|
||||
"SpecialFeatures": "Trailers,Deleted Scenes",
|
||||
"LastUpdate": "2006-02-15T05:03:42Z"
|
||||
}
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
||||
func TestSelect_ScanToSlice(t *testing.T) {
|
||||
query := Actor.
|
||||
SELECT(Actor.AllColumns).
|
||||
query := SELECT(Actor.AllColumns).
|
||||
FROM(Actor).
|
||||
ORDER_BY(Actor.ActorID)
|
||||
|
||||
testutils.AssertStatementSql(t, query, `
|
||||
|
|
@ -107,19 +154,20 @@ GROUP BY payment.customer_id
|
|||
HAVING SUM(payment.amount) > 125.6
|
||||
ORDER BY payment.customer_id, SUM(payment.amount) ASC;
|
||||
`
|
||||
query := Payment.
|
||||
INNER_JOIN(Customer, Customer.CustomerID.EQ(Payment.CustomerID)).
|
||||
SELECT(
|
||||
Customer.AllColumns,
|
||||
query := SELECT(
|
||||
Customer.AllColumns,
|
||||
|
||||
SUMf(Payment.Amount).AS("amount.sum"),
|
||||
AVG(Payment.Amount).AS("amount.avg"),
|
||||
MAX(Payment.PaymentDate).AS("amount.max_date"),
|
||||
MAXf(Payment.Amount).AS("amount.max"),
|
||||
MIN(Payment.PaymentDate).AS("amount.min_date"),
|
||||
MINf(Payment.Amount).AS("amount.min"),
|
||||
COUNT(Payment.Amount).AS("amount.count"),
|
||||
).
|
||||
SUMf(Payment.Amount).AS("amount.sum"),
|
||||
AVG(Payment.Amount).AS("amount.avg"),
|
||||
MAX(Payment.PaymentDate).AS("amount.max_date"),
|
||||
MAXf(Payment.Amount).AS("amount.max"),
|
||||
MIN(Payment.PaymentDate).AS("amount.min_date"),
|
||||
MINf(Payment.Amount).AS("amount.min"),
|
||||
COUNT(Payment.Amount).AS("amount.count"),
|
||||
).FROM(
|
||||
Payment.
|
||||
INNER_JOIN(Customer, Customer.CustomerID.EQ(Payment.CustomerID)),
|
||||
).
|
||||
GROUP_BY(Payment.CustomerID).
|
||||
HAVING(
|
||||
SUMf(Payment.Amount).GT(Float(125.6)),
|
||||
|
|
@ -1122,7 +1170,7 @@ WHERE payment.payment_id < ?
|
|||
WINDOW w1 AS (PARTITION BY payment.payment_date), w2 AS (w1), w3 AS (w2 ORDER BY payment.customer_id)
|
||||
ORDER BY payment.customer_id;
|
||||
`
|
||||
query := Payment.SELECT(
|
||||
query := SELECT(
|
||||
AVG(Payment.Amount).OVER(),
|
||||
AVG(Payment.Amount).OVER(Window("w1")),
|
||||
AVG(Payment.Amount).OVER(
|
||||
|
|
@ -1131,7 +1179,7 @@ ORDER BY payment.customer_id;
|
|||
RANGE(PRECEDING(UNBOUNDED), FOLLOWING(UNBOUNDED)),
|
||||
),
|
||||
AVG(Payment.Amount).OVER(Window("w3").RANGE(PRECEDING(UNBOUNDED), FOLLOWING(UNBOUNDED))),
|
||||
).
|
||||
).FROM(Payment).
|
||||
WHERE(Payment.PaymentID.LT(Int(10))).
|
||||
WINDOW("w1").AS(PARTITION_BY(Payment.PaymentDate)).
|
||||
WINDOW("w2").AS(Window("w1")).
|
||||
|
|
|
|||
|
|
@ -35,6 +35,58 @@ func TestAllTypesSelect(t *testing.T) {
|
|||
testutils.AssertDeepEqual(t, dest[1], allTypesRow1)
|
||||
}
|
||||
|
||||
func TestAllTypesSelectJson(t *testing.T) {
|
||||
|
||||
stmt := SELECT_JSON_ARR(
|
||||
AllTypesAllColumns.Except(
|
||||
AllTypes.JSON, AllTypes.JSONPtr,
|
||||
AllTypes.Jsonb, AllTypes.JsonbPtr,
|
||||
AllTypes.TextArray, AllTypes.TextArrayPtr,
|
||||
AllTypes.JsonbArray, AllTypes.IntegerArray, AllTypes.IntegerArrayPtr,
|
||||
AllTypes.TextMultiDimArray, AllTypes.TextMultiDimArrayPtr,
|
||||
),
|
||||
CAST(AllTypes.JSONPtr).AS_TEXT().AS("jsonPtr"),
|
||||
CAST(AllTypes.JSON).AS_TEXT().AS("JSON"),
|
||||
CAST(AllTypes.JsonbPtr).AS_TEXT().AS("jsonbPtr"),
|
||||
CAST(AllTypes.Jsonb).AS_TEXT().AS("Jsonb"),
|
||||
CAST(AllTypes.TextArrayPtr).AS_TEXT().AS("TextArrayPtr"),
|
||||
CAST(AllTypes.TextArray).AS_TEXT().AS("TextArray"),
|
||||
CAST(AllTypes.JsonbArray).AS_TEXT().AS("JsonbArray"),
|
||||
CAST(AllTypes.IntegerArray).AS_TEXT().AS("IntegerArray"),
|
||||
CAST(AllTypes.IntegerArrayPtr).AS_TEXT().AS("IntegerArrayPtr"),
|
||||
CAST(AllTypes.TextMultiDimArray).AS_TEXT().AS("TextMultiDimArray"),
|
||||
CAST(AllTypes.TextMultiDimArrayPtr).AS_TEXT().AS("TextMultiDimArrayPtr"),
|
||||
).FROM(AllTypes)
|
||||
|
||||
//fmt.Println(stmt.DebugSql())
|
||||
|
||||
var dest []model.AllTypes
|
||||
|
||||
err := stmt.QueryJSON(ctx, db, &dest)
|
||||
require.NoError(t, err)
|
||||
|
||||
// fix inconsistencies between postgres and cockroachdb.
|
||||
// cockroachdb returns char[N] columns with trailing whitespaces trimmed
|
||||
if sourceIsCockroachDB() {
|
||||
dest[0].Char = allTypesRow0.Char
|
||||
dest[0].CharPtr = allTypesRow0.CharPtr
|
||||
|
||||
dest[1].Char = allTypesRow1.Char
|
||||
dest[1].CharPtr = allTypesRow1.CharPtr
|
||||
}
|
||||
|
||||
// set time local before comparison
|
||||
dest[0].Timestampz = dest[0].Timestampz.Local()
|
||||
|
||||
if dest[0].TimestampzPtr != nil {
|
||||
dest[0].TimestampzPtr = ptr.Of(dest[0].TimestampzPtr.Local())
|
||||
}
|
||||
dest[1].Timestampz = dest[1].Timestampz.Local()
|
||||
|
||||
require.Equal(t, dest[0], allTypesRow0)
|
||||
require.Equal(t, dest[1], allTypesRow1)
|
||||
}
|
||||
|
||||
func TestAllTypesViewSelect(t *testing.T) {
|
||||
type AllTypesView model.AllTypes
|
||||
var dest []AllTypesView
|
||||
|
|
@ -146,40 +198,42 @@ RETURNING all_types.bytea AS "all_types.bytea",
|
|||
all_types.bytea_ptr AS "all_types.bytea_ptr";
|
||||
`, byteArrHex, byteArrBin)
|
||||
|
||||
var inserted model.AllTypes
|
||||
err := insertStmt.Query(db, &inserted)
|
||||
require.NoError(t, err)
|
||||
testutils.ExecuteInTxAndRollback(t, db, func(tx qrm.DB) {
|
||||
var inserted model.AllTypes
|
||||
err := insertStmt.Query(tx, &inserted)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, string(*inserted.ByteaPtr), "Hello Gopher!")
|
||||
// It is not possible to initiate bytea column using hex format '\xDEADBEEF' with pq driver.
|
||||
// pq driver always encodes parameter string if destination column is of type bytea.
|
||||
// Probably pq driver error.
|
||||
// require.Equal(t, string(inserted.Bytea), "Hello Gopher!")
|
||||
require.Equal(t, string(*inserted.ByteaPtr), "Hello Gopher!")
|
||||
// It is not possible to initiate bytea column using hex format '\xDEADBEEF' with pq driver.
|
||||
// pq driver always encodes parameter string if destination column is of type bytea.
|
||||
// Probably pq driver error.
|
||||
// require.Equal(t, string(inserted.Bytea), "Hello Gopher!")
|
||||
|
||||
stmt := SELECT(
|
||||
AllTypes.Bytea,
|
||||
AllTypes.ByteaPtr,
|
||||
).FROM(
|
||||
AllTypes,
|
||||
).WHERE(
|
||||
AllTypes.ByteaPtr.EQ(Bytea(byteArrBin)),
|
||||
)
|
||||
stmt := SELECT(
|
||||
AllTypes.Bytea,
|
||||
AllTypes.ByteaPtr,
|
||||
).FROM(
|
||||
AllTypes,
|
||||
).WHERE(
|
||||
AllTypes.ByteaPtr.EQ(Bytea(byteArrBin)),
|
||||
)
|
||||
|
||||
testutils.AssertStatementSql(t, stmt, `
|
||||
testutils.AssertStatementSql(t, stmt, `
|
||||
SELECT all_types.bytea AS "all_types.bytea",
|
||||
all_types.bytea_ptr AS "all_types.bytea_ptr"
|
||||
FROM test_sample.all_types
|
||||
WHERE all_types.bytea_ptr = $1::bytea;
|
||||
`, byteArrBin)
|
||||
|
||||
var dest model.AllTypes
|
||||
var dest model.AllTypes
|
||||
|
||||
err = stmt.Query(db, &dest)
|
||||
require.NoError(t, err)
|
||||
err = stmt.Query(tx, &dest)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, string(*dest.ByteaPtr), "Hello Gopher!")
|
||||
// Probably pq driver error.
|
||||
// require.Equal(t, string(dest.Bytea), "Hello Gopher!")
|
||||
require.Equal(t, string(*dest.ByteaPtr), "Hello Gopher!")
|
||||
// Probably pq driver error.
|
||||
// require.Equal(t, string(dest.Bytea), "Hello Gopher!")
|
||||
})
|
||||
}
|
||||
|
||||
func TestAllTypesFromSubQuery(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -188,7 +188,130 @@ ORDER BY "Artist"."ArtistId", "Album"."AlbumId", "Track"."TrackId";
|
|||
`)
|
||||
}
|
||||
|
||||
type AllArtistDetails []struct { //list of all artist
|
||||
model.Artist
|
||||
|
||||
Albums []struct { // list of albums per artist
|
||||
model.Album
|
||||
|
||||
Tracks []struct { // list of tracks per album
|
||||
model.Track
|
||||
|
||||
Genre model.Genre // track genre
|
||||
MediaType model.MediaType // track media type
|
||||
|
||||
Playlists []model.Playlist // list of playlist where track is used
|
||||
|
||||
Invoices []struct { // list of invoices where track occurs
|
||||
model.Invoice
|
||||
|
||||
Customer struct { // customer data for invoice
|
||||
model.Customer
|
||||
|
||||
Employee *struct { // employee data for customer if exists
|
||||
model.Employee
|
||||
|
||||
Manager *model.Employee `alias:"Manager"`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkJoinEverythingJSON(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
testJoinEverythingJSON(b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJoinEverythingJSON(t *testing.T) {
|
||||
testJoinEverythingJSON(t)
|
||||
}
|
||||
|
||||
func testJoinEverythingJSON(t require.TestingT) {
|
||||
|
||||
manager := Employee.AS("Manager")
|
||||
|
||||
stmt := SELECT_JSON_ARR(
|
||||
Artist.AllColumns,
|
||||
|
||||
SELECT_JSON_ARR(
|
||||
Album.AllColumns,
|
||||
|
||||
SELECT_JSON_ARR(
|
||||
Track.AllColumns,
|
||||
|
||||
SELECT_JSON_OBJ(Genre.AllColumns).
|
||||
FROM(Genre).
|
||||
WHERE(Genre.GenreId.EQ(Track.GenreId)).AS("Genre"),
|
||||
|
||||
SELECT_JSON_OBJ(MediaType.AllColumns).
|
||||
FROM(MediaType).
|
||||
WHERE(MediaType.MediaTypeId.EQ(Track.MediaTypeId)).AS("MediaType"),
|
||||
|
||||
SELECT_JSON_ARR(Playlist.AllColumns).
|
||||
FROM(Playlist.INNER_JOIN(
|
||||
PlaylistTrack,
|
||||
Playlist.PlaylistId.EQ(PlaylistTrack.PlaylistId))).
|
||||
WHERE(PlaylistTrack.TrackId.EQ(Track.TrackId)).
|
||||
ORDER_BY(Playlist.PlaylistId).AS("Playlists"),
|
||||
|
||||
SELECT_JSON_ARR(
|
||||
Invoice.AllColumns,
|
||||
|
||||
SELECT_JSON_OBJ(
|
||||
Customer.AllColumns,
|
||||
|
||||
SELECT_JSON_OBJ(
|
||||
Employee.AllColumns,
|
||||
|
||||
SELECT_JSON_OBJ(manager.AllColumns).
|
||||
FROM(manager).
|
||||
WHERE(manager.EmployeeId.EQ(Employee.ReportsTo)).AS("Manager"),
|
||||
).FROM(Employee).
|
||||
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)),
|
||||
).WHERE(InvoiceLine.TrackId.EQ(Track.TrackId)).
|
||||
ORDER_BY(Invoice.InvoiceId).AS("Invoices"),
|
||||
).FROM(Track).
|
||||
WHERE(Track.AlbumId.EQ(Album.AlbumId)).
|
||||
ORDER_BY(Track.TrackId).AS("Tracks"),
|
||||
).FROM(Album).
|
||||
WHERE(Album.ArtistId.EQ(Artist.ArtistId)).
|
||||
ORDER_BY(Album.AlbumId).AS("Albums"),
|
||||
).FROM(Artist).
|
||||
ORDER_BY(Artist.ArtistId)
|
||||
|
||||
//fmt.Println(stmt.DebugSql())
|
||||
|
||||
var dest AllArtistDetails
|
||||
|
||||
err := stmt.QueryJSON(ctx, db, &dest)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, len(dest), 275)
|
||||
//testutils.SaveJSONFile(dest, "./testdata/results/postgres/joined_everything2.json")
|
||||
testutils.AssertJSONFile(t, dest, "./testdata/results/postgres/joined_everything.json")
|
||||
requireLogged(t, stmt)
|
||||
requireQueryLogged(t, stmt, 1)
|
||||
}
|
||||
|
||||
func BenchmarkJoinEverything(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
testJoinEverything(b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJoinEverything(t *testing.T) {
|
||||
testJoinEverything(t)
|
||||
}
|
||||
|
||||
func testJoinEverything(t require.TestingT) {
|
||||
|
||||
manager := Employee.AS("Manager")
|
||||
|
||||
|
|
@ -223,37 +346,6 @@ func TestJoinEverything(t *testing.T) {
|
|||
Invoice.InvoiceId, Customer.CustomerId,
|
||||
)
|
||||
|
||||
var dest []struct { //list of all artist
|
||||
model.Artist
|
||||
|
||||
Albums []struct { // list of albums per artist
|
||||
model.Album
|
||||
|
||||
Tracks []struct { // list of tracks per album
|
||||
model.Track
|
||||
|
||||
Genre model.Genre // track genre
|
||||
MediaType model.MediaType // track media type
|
||||
|
||||
Playlists []model.Playlist // list of playlist where track is used
|
||||
|
||||
Invoices []struct { // list of invoices where track occurs
|
||||
model.Invoice
|
||||
|
||||
Customer struct { // customer data for invoice
|
||||
model.Customer
|
||||
|
||||
Employee *struct { // employee data for customer if exists
|
||||
model.Employee
|
||||
|
||||
Manager *model.Employee `alias:"Manager"`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testutils.AssertStatementSql(t, stmt, `
|
||||
SELECT "Artist"."ArtistId" AS "Artist.ArtistId",
|
||||
"Artist"."Name" AS "Artist.Name",
|
||||
|
|
@ -344,7 +436,7 @@ FROM chinook."Artist"
|
|||
LEFT JOIN chinook."Employee" AS "Manager" ON ("Manager"."EmployeeId" = "Employee"."ReportsTo")
|
||||
ORDER BY "Artist"."ArtistId", "Album"."AlbumId", "Track"."TrackId", "Genre"."GenreId", "MediaType"."MediaTypeId", "Playlist"."PlaylistId", "Invoice"."InvoiceId", "Customer"."CustomerId";
|
||||
`)
|
||||
|
||||
var dest AllArtistDetails
|
||||
err := stmt.QueryContext(context.Background(), db, &dest)
|
||||
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
|
|
@ -119,14 +119,22 @@ func init() {
|
|||
})
|
||||
}
|
||||
|
||||
func requireLogged(t *testing.T, statement postgres.Statement) {
|
||||
func requireLogged(t require.TestingT, statement postgres.Statement) {
|
||||
if _, ok := t.(*testing.B); ok {
|
||||
return // skip assert for benchmarks
|
||||
}
|
||||
|
||||
query, args := statement.Sql()
|
||||
require.Equal(t, loggedSQL, query)
|
||||
require.Equal(t, loggedSQLArgs, args)
|
||||
require.Equal(t, loggedDebugSQL, statement.DebugSql())
|
||||
}
|
||||
|
||||
func requireQueryLogged(t *testing.T, statement postgres.Statement, rowsProcessed int64) {
|
||||
func requireQueryLogged(t require.TestingT, statement postgres.Statement, rowsProcessed int64) {
|
||||
if _, ok := t.(*testing.B); ok {
|
||||
return // skip assert for benchmarks
|
||||
}
|
||||
|
||||
query, args := statement.Sql()
|
||||
queryLogged, argsLogged := queryInfo.Statement.Sql()
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,50 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
func TestNorthwindJoinEverything(t *testing.T) {
|
||||
type Dest []struct {
|
||||
model.Customers
|
||||
|
||||
Demographics model.CustomerDemographics
|
||||
|
||||
Orders []struct {
|
||||
model.Orders
|
||||
|
||||
Shipper model.Shippers
|
||||
|
||||
Employee struct {
|
||||
model.Employees
|
||||
|
||||
Territories []struct {
|
||||
model.Territories
|
||||
|
||||
Region model.Region
|
||||
}
|
||||
}
|
||||
|
||||
Details []struct {
|
||||
model.OrderDetails
|
||||
|
||||
Products struct {
|
||||
model.Products
|
||||
|
||||
Category model.Categories
|
||||
Supplier model.Suppliers
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkTestNorthwindJoinEverything(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
testNorthwindJoinEverything(b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTestNorthwindJoinEverything(t *testing.T) {
|
||||
testNorthwindJoinEverything(t)
|
||||
}
|
||||
|
||||
func testNorthwindJoinEverything(t require.TestingT) {
|
||||
|
||||
stmt :=
|
||||
SELECT(
|
||||
|
|
@ -21,6 +64,9 @@ func TestNorthwindJoinEverything(t *testing.T) {
|
|||
Products.AllColumns,
|
||||
Categories.AllColumns,
|
||||
Suppliers.AllColumns,
|
||||
Employees.AllColumns,
|
||||
Territories.AllColumns,
|
||||
Region.AllColumns,
|
||||
).FROM(
|
||||
Customers.
|
||||
LEFT_JOIN(CustomerCustomerDemo, Customers.CustomerID.EQ(CustomerCustomerDemo.CustomerID)).
|
||||
|
|
@ -35,35 +81,110 @@ func TestNorthwindJoinEverything(t *testing.T) {
|
|||
LEFT_JOIN(EmployeeTerritories, EmployeeTerritories.EmployeeID.EQ(Employees.EmployeeID)).
|
||||
LEFT_JOIN(Territories, EmployeeTerritories.TerritoryID.EQ(Territories.TerritoryID)).
|
||||
LEFT_JOIN(Region, Territories.RegionID.EQ(Region.RegionID)),
|
||||
).ORDER_BY(Customers.CustomerID, Orders.OrderID, Products.ProductID)
|
||||
).ORDER_BY(
|
||||
Customers.CustomerID,
|
||||
Orders.OrderID,
|
||||
Products.ProductID,
|
||||
Territories.TerritoryID,
|
||||
)
|
||||
|
||||
var dest []struct {
|
||||
model.Customers
|
||||
//fmt.Println(stmt.DebugSql())
|
||||
|
||||
Demographics model.CustomerDemographics
|
||||
|
||||
Orders []struct {
|
||||
model.Orders
|
||||
|
||||
Shipper model.Shippers
|
||||
|
||||
Details struct {
|
||||
model.OrderDetails
|
||||
|
||||
Products []struct {
|
||||
model.Products
|
||||
|
||||
Category model.Categories
|
||||
Supplier model.Suppliers
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var dest Dest
|
||||
|
||||
err := stmt.Query(db, &dest)
|
||||
require.NoError(t, err)
|
||||
|
||||
//jsonSave("./testdata/northwind-all.json", dest)
|
||||
//testutils.SaveJSONFile(dest, "./testdata/results/postgres/northwind-all.json")
|
||||
testutils.AssertJSONFile(t, dest, "./testdata/results/postgres/northwind-all.json")
|
||||
requireLogged(t, stmt)
|
||||
}
|
||||
|
||||
func BenchmarkTestNorthwindJoinEverythingJson(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
testNorthwindJoinEverythingJson(b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNorthwindJoinEverythingJson(t *testing.T) {
|
||||
testNorthwindJoinEverythingJson(t)
|
||||
}
|
||||
|
||||
func testNorthwindJoinEverythingJson(t require.TestingT) {
|
||||
|
||||
stmt := SELECT_JSON_ARR(
|
||||
Customers.AllColumns,
|
||||
|
||||
SELECT_JSON_OBJ(CustomerDemographics.AllColumns).
|
||||
FROM(CustomerDemographics.INNER_JOIN(CustomerCustomerDemo, CustomerCustomerDemo.CustomerTypeID.EQ(CustomerDemographics.CustomerTypeID))).
|
||||
WHERE(CustomerCustomerDemo.CustomerID.EQ(Customers.CustomerID)).AS("Demographics"),
|
||||
|
||||
SELECT_JSON_ARR(
|
||||
Orders.AllColumns,
|
||||
|
||||
SELECT_JSON_OBJ(Shippers.AllColumns).
|
||||
FROM(Shippers).
|
||||
WHERE(Shippers.ShipperID.EQ(Orders.ShipVia)).AS("Shipper"),
|
||||
|
||||
SELECT_JSON_OBJ(
|
||||
Employees.AllColumns,
|
||||
SELECT_JSON_ARR(
|
||||
Territories.AllColumns,
|
||||
|
||||
SELECT_JSON_OBJ(Region.AllColumns).
|
||||
FROM(Region).
|
||||
WHERE(Region.RegionID.EQ(Territories.RegionID)).AS("Region"),
|
||||
).FROM(
|
||||
EmployeeTerritories.LEFT_JOIN(
|
||||
Territories,
|
||||
EmployeeTerritories.TerritoryID.EQ(Territories.TerritoryID)),
|
||||
).WHERE(
|
||||
EmployeeTerritories.EmployeeID.EQ(Employees.EmployeeID), // TODO: move to join
|
||||
).AS("Territories"),
|
||||
).FROM(Employees).
|
||||
WHERE(Orders.EmployeeID.EQ(Employees.EmployeeID)).AS("Employee"),
|
||||
|
||||
SELECT_JSON_ARR(
|
||||
OrderDetails.AllColumns,
|
||||
|
||||
SELECT_JSON_OBJ(
|
||||
Products.AllColumns,
|
||||
|
||||
SELECT_JSON_OBJ(
|
||||
Categories.AllColumns,
|
||||
).FROM(Categories).
|
||||
WHERE(Categories.CategoryID.EQ(Products.CategoryID)).AS("Category"),
|
||||
|
||||
SELECT_JSON_OBJ(Suppliers.AllColumns).
|
||||
FROM(Suppliers).
|
||||
WHERE(Suppliers.SupplierID.EQ(Products.SupplierID)).AS("Supplier"),
|
||||
).FROM(Products).
|
||||
WHERE(Products.ProductID.EQ(OrderDetails.ProductID)).AS("Products"),
|
||||
).FROM(
|
||||
OrderDetails,
|
||||
).WHERE(
|
||||
OrderDetails.OrderID.EQ(Orders.OrderID),
|
||||
).AS("Details"),
|
||||
).FROM(
|
||||
Orders,
|
||||
).WHERE(
|
||||
Orders.CustomerID.EQ(Customers.CustomerID),
|
||||
).ORDER_BY(
|
||||
Orders.OrderID,
|
||||
).AS("Orders"),
|
||||
).FROM(
|
||||
Customers,
|
||||
).ORDER_BY(
|
||||
Customers.CustomerID,
|
||||
)
|
||||
|
||||
//fmt.Println(stmt.DebugSql())
|
||||
|
||||
var dest Dest
|
||||
|
||||
err := stmt.QueryJSON(ctx, db, &dest)
|
||||
require.NoError(t, err)
|
||||
|
||||
//testutils.SaveJSONFile(dest, "./testdata/results/postgres/northwind-all2.json")
|
||||
testutils.AssertJSONFile(t, dest, "./testdata/results/postgres/northwind-all.json")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -220,20 +220,7 @@ func TestUUIDComplex(t *testing.T) {
|
|||
requireLogged(t, query)
|
||||
})
|
||||
|
||||
t.Run("slice of structs left join", func(t *testing.T) {
|
||||
leftQuery := Person.LEFT_JOIN(PersonPhone, PersonPhone.PersonID.EQ(Person.PersonID)).
|
||||
SELECT(Person.AllColumns, PersonPhone.AllColumns).
|
||||
ORDER_BY(Person.PersonID.ASC(), PersonPhone.PhoneID.ASC())
|
||||
var dest []struct {
|
||||
model.Person
|
||||
Phones []struct {
|
||||
model.PersonPhone
|
||||
}
|
||||
}
|
||||
err := leftQuery.Query(db, &dest)
|
||||
|
||||
require.NoError(t, err)
|
||||
testutils.AssertJSON(t, dest, `
|
||||
var expectedSliceOfStructsLeftJoin = `
|
||||
[
|
||||
{
|
||||
"PersonID": "b68dbff4-a87d-11e9-a7f2-98ded00c39c6",
|
||||
|
|
@ -274,10 +261,50 @@ func TestUUIDComplex(t *testing.T) {
|
|||
]
|
||||
}
|
||||
]
|
||||
`)
|
||||
`
|
||||
|
||||
t.Run("slice of structs left join", func(t *testing.T) {
|
||||
leftQuery := Person.LEFT_JOIN(PersonPhone, PersonPhone.PersonID.EQ(Person.PersonID)).
|
||||
SELECT(Person.AllColumns, PersonPhone.AllColumns).
|
||||
ORDER_BY(Person.PersonID.ASC(), PersonPhone.PhoneID.ASC())
|
||||
var dest []struct {
|
||||
model.Person
|
||||
Phones []struct {
|
||||
model.PersonPhone
|
||||
}
|
||||
}
|
||||
err := leftQuery.Query(db, &dest)
|
||||
|
||||
require.NoError(t, err)
|
||||
testutils.AssertJSON(t, dest, expectedSliceOfStructsLeftJoin)
|
||||
requireLogged(t, leftQuery)
|
||||
})
|
||||
|
||||
t.Run("select json", func(t *testing.T) {
|
||||
jsonQuery := SELECT_JSON_ARR(
|
||||
Person.AllColumns,
|
||||
SELECT_JSON_ARR(PersonPhone.AllColumns).
|
||||
FROM(PersonPhone).
|
||||
WHERE(PersonPhone.PersonID.EQ(Person.PersonID)).
|
||||
ORDER_BY(PersonPhone.PhoneID).AS("Phones"),
|
||||
).FROM(
|
||||
Person,
|
||||
).ORDER_BY(
|
||||
Person.PersonID.ASC(),
|
||||
)
|
||||
|
||||
var dest []struct {
|
||||
model.Person
|
||||
Phones []struct {
|
||||
model.PersonPhone
|
||||
}
|
||||
}
|
||||
|
||||
err := jsonQuery.QueryJSON(ctx, db, &dest)
|
||||
require.NoError(t, err)
|
||||
testutils.AssertJSON(t, dest, expectedSliceOfStructsLeftJoin)
|
||||
})
|
||||
|
||||
}
|
||||
func TestEnumType(t *testing.T) {
|
||||
query := Person.
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ func TestScanToStruct(t *testing.T) {
|
|||
|
||||
err := query.Query(db, &dest)
|
||||
require.Error(t, err)
|
||||
require.EqualError(t, err, "jet: can't scan int64('\\x01') to 'InventoryID uuid.UUID': Scan: unable to scan type int64 into UUID")
|
||||
require.EqualError(t, err, "jet: can't assign int64('\\x01') to 'InventoryID uuid.UUID': Scan: unable to scan type int64 into UUID")
|
||||
})
|
||||
|
||||
t.Run("type mismatch base type", func(t *testing.T) {
|
||||
|
|
|
|||
908
tests/postgres/select_json_test.go
Normal file
908
tests/postgres/select_json_test.go
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -21,36 +21,36 @@ import (
|
|||
)
|
||||
|
||||
func TestSelect_ScanToStruct(t *testing.T) {
|
||||
expectedSQL := `
|
||||
|
||||
t.Run("standard", func(t *testing.T) {
|
||||
stmt := SELECT(Actor.AllColumns).
|
||||
DISTINCT().
|
||||
FROM(Actor).
|
||||
WHERE(Actor.ActorID.EQ(Int(2)))
|
||||
|
||||
testutils.AssertDebugStatementSql(t, stmt, `
|
||||
SELECT DISTINCT actor.actor_id AS "actor.actor_id",
|
||||
actor.first_name AS "actor.first_name",
|
||||
actor.last_name AS "actor.last_name",
|
||||
actor.last_update AS "actor.last_update"
|
||||
FROM dvds.actor
|
||||
WHERE actor.actor_id = 2;
|
||||
`
|
||||
`, int64(2))
|
||||
|
||||
query := SELECT(Actor.AllColumns).
|
||||
DISTINCT().
|
||||
FROM(Actor).
|
||||
WHERE(Actor.ActorID.EQ(Int(2)))
|
||||
var dest model.Actor
|
||||
err := stmt.Query(db, &dest)
|
||||
|
||||
testutils.AssertDebugStatementSql(t, query, expectedSQL, int64(2))
|
||||
require.NoError(t, err)
|
||||
testutils.AssertDeepEqual(t, dest, actor2)
|
||||
requireLogged(t, stmt)
|
||||
})
|
||||
}
|
||||
|
||||
actor := model.Actor{}
|
||||
err := query.Query(db, &actor)
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedActor := model.Actor{
|
||||
ActorID: 2,
|
||||
FirstName: "Nick",
|
||||
LastName: "Wahlberg",
|
||||
LastUpdate: *testutils.TimestampWithoutTimeZone("2013-05-26 14:47:57.62", 2),
|
||||
}
|
||||
|
||||
testutils.AssertDeepEqual(t, actor, expectedActor)
|
||||
requireLogged(t, query)
|
||||
var actor2 = model.Actor{
|
||||
ActorID: 2,
|
||||
FirstName: "Nick",
|
||||
LastName: "Wahlberg",
|
||||
LastUpdate: *testutils.TimestampWithoutTimeZone("2013-05-26 14:47:57.62", 2),
|
||||
}
|
||||
|
||||
func TestSelectDistinctOn(t *testing.T) {
|
||||
|
|
@ -85,7 +85,6 @@ ORDER BY rental.staff_id ASC, rental.customer_id ASC, rental.rental_id ASC;
|
|||
|
||||
err := stmt.Query(db, &dest)
|
||||
require.NoError(t, err)
|
||||
|
||||
testutils.AssertJSON(t, dest, `
|
||||
[
|
||||
{
|
||||
|
|
@ -187,6 +186,21 @@ ORDER BY customer.customer_id ASC;
|
|||
testutils.AssertDeepEqual(t, lastCustomer, customers[598])
|
||||
|
||||
requireLogged(t, query)
|
||||
|
||||
t.Run("select json", func(t *testing.T) {
|
||||
stmt := SELECT_JSON_ARR(
|
||||
Customer.AllColumns,
|
||||
).FROM(
|
||||
Customer,
|
||||
).ORDER_BY(Customer.CustomerID.ASC())
|
||||
|
||||
var dest []model.Customer
|
||||
|
||||
err := stmt.QueryJSON(ctx, db, &dest)
|
||||
require.NoError(t, err)
|
||||
|
||||
testutils.AssertDeepEqual(t, customers, dest)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSelectAndUnionInProjection(t *testing.T) {
|
||||
|
|
@ -217,15 +231,14 @@ FROM dvds.payment
|
|||
LIMIT 12;
|
||||
`
|
||||
|
||||
query := Payment.
|
||||
SELECT(
|
||||
Payment.PaymentID,
|
||||
Customer.SELECT(Customer.CustomerID).LIMIT(1),
|
||||
UNION(
|
||||
Payment.SELECT(Payment.PaymentID).LIMIT(1).OFFSET(10),
|
||||
Payment.SELECT(Payment.PaymentID).LIMIT(1).OFFSET(2),
|
||||
).LIMIT(1),
|
||||
).
|
||||
query := SELECT(
|
||||
Payment.PaymentID,
|
||||
Customer.SELECT(Customer.CustomerID).LIMIT(1),
|
||||
UNION(
|
||||
Payment.SELECT(Payment.PaymentID).LIMIT(1).OFFSET(10),
|
||||
Payment.SELECT(Payment.PaymentID).LIMIT(1).OFFSET(2),
|
||||
).LIMIT(1),
|
||||
).FROM(Payment).
|
||||
LIMIT(12)
|
||||
|
||||
//fmt.Println(query.DebugSql())
|
||||
|
|
@ -2771,7 +2784,8 @@ ORDER BY actor.actor_id ASC, film.film_id ASC;
|
|||
err := stmt.Query(db, &dest)
|
||||
require.NoError(t, err)
|
||||
|
||||
//jsonSave("./testdata/quick-start-dest.json", dest)
|
||||
//testutils.SaveJSONFile(dest, "./testdata/results/postgres/quick-start-dest.json")
|
||||
|
||||
testutils.AssertJSONFile(t, dest, "./testdata/results/postgres/quick-start-dest.json")
|
||||
|
||||
var dest2 []struct {
|
||||
|
|
@ -2784,7 +2798,7 @@ ORDER BY actor.actor_id ASC, film.film_id ASC;
|
|||
err = stmt.Query(db, &dest2)
|
||||
require.NoError(t, err)
|
||||
|
||||
//jsonSave("./testdata/quick-start-dest2.json", dest2)
|
||||
//testutils.SaveJSONFile(dest2, "./testdata/results/postgres/quick-start-dest2.json")
|
||||
testutils.AssertJSONFile(t, dest2, "./testdata/results/postgres/quick-start-dest2.json")
|
||||
}
|
||||
|
||||
|
|
@ -2966,7 +2980,7 @@ WHERE payment.payment_id < $1
|
|||
WINDOW w1 AS (PARTITION BY payment.payment_date), w2 AS (w1), w3 AS (w2 ORDER BY payment.customer_id)
|
||||
ORDER BY payment.customer_id;
|
||||
`
|
||||
query := Payment.SELECT(
|
||||
query := SELECT(
|
||||
AVG(Payment.Amount).OVER(),
|
||||
AVG(Payment.Amount).OVER(Window("w1")),
|
||||
AVG(Payment.Amount).OVER(
|
||||
|
|
@ -2976,6 +2990,7 @@ ORDER BY payment.customer_id;
|
|||
),
|
||||
AVG(Payment.Amount).OVER(Window("w3").RANGE(PRECEDING(UNBOUNDED), FOLLOWING(UNBOUNDED))),
|
||||
).
|
||||
FROM(Payment).
|
||||
WHERE(Payment.PaymentID.LT(Int(10))).
|
||||
WINDOW("w1").AS(PARTITION_BY(Payment.PaymentDate)).
|
||||
WINDOW("w2").AS(Window("w1")).
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 1c501acb72bea389788404988ef0130b733f9cee
|
||||
Subproject commit 0997c825e6569fc49b69ffbef959eadab9013e00
|
||||
Loading…
Add table
Add a link
Reference in a new issue