Add LATERAL query support

This commit is contained in:
go-jet 2021-05-03 19:31:04 +02:00
parent 4ef0113f6b
commit 0f773b26d6
6 changed files with 224 additions and 12 deletions

23
mysql/lateral.go Normal file
View file

@ -0,0 +1,23 @@
package mysql
import "github.com/go-jet/jet/v2/internal/jet"
func LATERAL(selectStmt SelectStatement) lateralImpl {
return lateralImpl{
selectStmt: selectStmt,
}
}
type lateralImpl struct {
selectStmt SelectStatement
}
func (l lateralImpl) AS(alias string) SelectTable {
subQuery := &selectTableImpl{
SelectTable: jet.NewLateral(l.selectStmt, alias),
}
subQuery.readableTableInterfaceImpl.parent = subQuery
return subQuery
}

View file

@ -2,9 +2,19 @@ package postgres
import "github.com/go-jet/jet/v2/internal/jet" import "github.com/go-jet/jet/v2/internal/jet"
func LATERAL(selectStmt SelectStatement, alias string) SelectTable { func LATERAL(selectStmt SelectStatement) lateralImpl {
return lateralImpl{
selectStmt: selectStmt,
}
}
type lateralImpl struct {
selectStmt SelectStatement
}
func (l lateralImpl) AS(alias string) SelectTable {
subQuery := &selectTableImpl{ subQuery := &selectTableImpl{
SelectTable: jet.NewLateral(selectStmt, alias), SelectTable: jet.NewLateral(l.selectStmt, alias),
} }
subQuery.readableTableInterfaceImpl.parent = subQuery subQuery.readableTableInterfaceImpl.parent = subQuery

View file

@ -3,7 +3,12 @@ package postgres
import "testing" import "testing"
func TestLATERAL(t *testing.T) { func TestLATERAL(t *testing.T) {
assertSerialize(t, LATERAL(SELECT(Int(1)), "lat1"), `LATERAL ( assertSerialize(t,
LATERAL(
SELECT(Int(1)),
).AS("lat1"),
`LATERAL (
SELECT $1 SELECT $1
) AS lat1`) ) AS lat1`)
} }

View file

@ -64,3 +64,9 @@ func requireLogged(t *testing.T, statement postgres.Statement) {
require.Equal(t, loggedSQLArgs, args) require.Equal(t, loggedSQLArgs, args)
require.Equal(t, loggedDebugSQL, statement.DebugSql()) require.Equal(t, loggedDebugSQL, statement.DebugSql())
} }
func skipForMariaDB(t *testing.T) {
if sourceIsMariaDB() {
t.SkipNow()
}
}

View file

@ -1,15 +1,17 @@
package mysql package mysql
import ( import (
"strings"
"testing"
"github.com/go-jet/jet/v2/internal/testutils" "github.com/go-jet/jet/v2/internal/testutils"
. "github.com/go-jet/jet/v2/mysql" . "github.com/go-jet/jet/v2/mysql"
"github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/enum" "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/enum"
"github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/model" "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/model"
. "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/table" . "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/table"
"github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/view" "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/view"
"github.com/stretchr/testify/require"
"testing" "github.com/stretchr/testify/require"
) )
func TestSelect_ScanToStruct(t *testing.T) { func TestSelect_ScanToStruct(t *testing.T) {
@ -787,3 +789,101 @@ LIMIT 5;
require.Equal(t, dest.Films[1].Title, "ACE GOLDFINGER") require.Equal(t, dest.Films[1].Title, "ACE GOLDFINGER")
require.Equal(t, dest.Films[4].Title, "AFRICAN EGG") require.Equal(t, dest.Films[4].Title, "AFRICAN EGG")
} }
func TestLateral(t *testing.T) {
skipForMariaDB(t) // MariaDB does not implement LATERAL
languages := LATERAL(
SELECT(
Language.AllColumns,
).FROM(
Language,
).WHERE(
Language.Name.NOT_IN(String("spanish")).
AND(Film.LanguageID.EQ(Language.LanguageID)),
),
).AS("films")
stmt := SELECT(
Film.FilmID,
Film.Title,
languages.AllColumns(),
).FROM(
Film.CROSS_JOIN(languages),
).WHERE(
Film.FilmID.EQ(Int(1)),
).ORDER_BY(
Film.FilmID,
).LIMIT(1)
testutils.AssertDebugStatementSql(t, stmt, strings.Replace(`
SELECT film.film_id AS "film.film_id",
film.title AS "film.title",
films.''language.language_id'' AS "language.language_id",
films.''language.name'' AS "language.name",
films.''language.last_update'' AS "language.last_update"
FROM dvds.film
CROSS JOIN LATERAL (
SELECT language.language_id AS "language.language_id",
language.name AS "language.name",
language.last_update AS "language.last_update"
FROM dvds.language
WHERE (language.name NOT IN ('spanish')) AND (film.language_id = language.language_id)
) AS films
WHERE film.film_id = 1
ORDER BY film.film_id
LIMIT 1;
`, "''", "`", -1))
type FilmLanguage struct {
model.Film
model.Language
}
var dest []FilmLanguage
err := stmt.Query(db, &dest)
require.NoError(t, err)
require.Equal(t, dest[0].Film.Title, "ACADEMY DINOSAUR")
require.Equal(t, dest[0].Language.Name, "English")
t.Run("implicit cross join", func(t *testing.T) {
stmt2 := SELECT(
Film.FilmID,
Film.Title,
languages.AllColumns(),
).FROM(
Film,
languages,
).WHERE(
Film.FilmID.EQ(Int(1)),
).ORDER_BY(
Film.FilmID,
).LIMIT(1)
testutils.AssertDebugStatementSql(t, stmt2, strings.Replace(`
SELECT film.film_id AS "film.film_id",
film.title AS "film.title",
films.''language.language_id'' AS "language.language_id",
films.''language.name'' AS "language.name",
films.''language.last_update'' AS "language.last_update"
FROM dvds.film,
LATERAL (
SELECT language.language_id AS "language.language_id",
language.name AS "language.name",
language.last_update AS "language.last_update"
FROM dvds.language
WHERE (language.name NOT IN ('spanish')) AND (film.language_id = language.language_id)
) AS films
WHERE film.film_id = 1
ORDER BY film.film_id
LIMIT 1;
`, "''", "`", -1))
var dest2 []FilmLanguage
err2 := stmt2.Query(db, &dest2)
require.NoError(t, err2)
require.Equal(t, dest, dest2)
})
}

View file

@ -1,15 +1,17 @@
package postgres package postgres
import ( import (
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/go-jet/jet/v2/internal/testutils" "github.com/go-jet/jet/v2/internal/testutils"
. "github.com/go-jet/jet/v2/postgres" . "github.com/go-jet/jet/v2/postgres"
"github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/enum" "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/enum"
"github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/model" "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/model"
. "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/table" . "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/table"
"github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/view" "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/view"
"github.com/stretchr/testify/require"
"testing"
"time"
) )
func TestSelect_ScanToStruct(t *testing.T) { func TestSelect_ScanToStruct(t *testing.T) {
@ -1905,22 +1907,88 @@ func TestLateral(t *testing.T) {
Language.Name.NOT_IN(String("spanish")). Language.Name.NOT_IN(String("spanish")).
AND(Film.LanguageID.EQ(Language.LanguageID)), AND(Film.LanguageID.EQ(Language.LanguageID)),
), ),
"films") ).AS("films")
stmt := SELECT( stmt := SELECT(
Film.AllColumns, Film.FilmID,
Film.Title,
languages.AllColumns(), languages.AllColumns(),
).FROM( ).FROM(
Film.CROSS_JOIN(languages), Film.CROSS_JOIN(languages),
).WHERE( ).WHERE(
Film.FilmID.EQ(Int(1)), Film.FilmID.EQ(Int(1)),
) ).ORDER_BY(
Film.FilmID,
).LIMIT(1)
var dest []struct { testutils.AssertDebugStatementSql(t, stmt, `
SELECT film.film_id AS "film.film_id",
film.title AS "film.title",
films."language.language_id" AS "language.language_id",
films."language.name" AS "language.name",
films."language.last_update" AS "language.last_update"
FROM dvds.film
CROSS JOIN LATERAL (
SELECT language.language_id AS "language.language_id",
language.name AS "language.name",
language.last_update AS "language.last_update"
FROM dvds.language
WHERE (language.name NOT IN ('spanish')) AND (film.language_id = language.language_id)
) AS films
WHERE film.film_id = 1
ORDER BY film.film_id
LIMIT 1;
`)
type FilmLanguage struct {
model.Film model.Film
model.Language model.Language
} }
var dest []FilmLanguage
err := stmt.Query(db, &dest) err := stmt.Query(db, &dest)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, dest[0].Film.Title, "Academy Dinosaur")
require.Equal(t, dest[0].Language.Name, "English ")
t.Run("implicit cross join", func(t *testing.T) {
stmt2 := SELECT(
Film.FilmID,
Film.Title,
languages.AllColumns(),
).FROM(
Film,
languages,
).WHERE(
Film.FilmID.EQ(Int(1)),
).ORDER_BY(
Film.FilmID,
).LIMIT(1)
testutils.AssertDebugStatementSql(t, stmt2, `
SELECT film.film_id AS "film.film_id",
film.title AS "film.title",
films."language.language_id" AS "language.language_id",
films."language.name" AS "language.name",
films."language.last_update" AS "language.last_update"
FROM dvds.film,
LATERAL (
SELECT language.language_id AS "language.language_id",
language.name AS "language.name",
language.last_update AS "language.last_update"
FROM dvds.language
WHERE (language.name NOT IN ('spanish')) AND (film.language_id = language.language_id)
) AS films
WHERE film.film_id = 1
ORDER BY film.film_id
LIMIT 1;
`)
var dest2 []FilmLanguage
err2 := stmt2.Query(db, &dest2)
require.NoError(t, err2)
require.Equal(t, dest, dest2)
})
} }