Add support for VALUES statement.

This commit is contained in:
go-jet 2024-10-17 14:12:21 +02:00
parent 3fcbbec427
commit 8d112f7db8
41 changed files with 1296 additions and 131 deletions

View file

@ -1,6 +1,7 @@
package postgres
import (
"fmt"
"github.com/go-jet/jet/v2/internal/jet"
"strconv"
)
@ -25,6 +26,9 @@ func newDialect() jet.Dialect {
return "$" + strconv.Itoa(ord)
},
ReservedWords: reservedWords,
ValuesDefaultColumnName: func(index int) string {
return fmt.Sprintf("column%d", index+1)
},
}
return jet.NewDialect(dialectParams)

View file

@ -46,33 +46,33 @@ func TestExists(t *testing.T) {
func TestIN(t *testing.T) {
assertSerialize(t, Float(1.11).IN(table1.SELECT(table1Col1)),
`($1 IN (
`($1 IN ((
SELECT table1.col1 AS "table1.col1"
FROM db.table1
))`, float64(1.11))
)))`, float64(1.11))
assertSerialize(t, ROW(Int(12), table1Col1).IN(table2.SELECT(table2Col3, table3Col1)),
`(ROW($1, table1.col1) IN (
`(ROW($1, table1.col1) IN ((
SELECT table2.col3 AS "table2.col3",
table3.col1 AS "table3.col1"
FROM db.table2
))`, int64(12))
)))`, int64(12))
}
func TestNOT_IN(t *testing.T) {
assertSerialize(t, Float(1.11).NOT_IN(table1.SELECT(table1Col1)),
`($1 NOT IN (
`($1 NOT IN ((
SELECT table1.col1 AS "table1.col1"
FROM db.table1
))`, float64(1.11))
)))`, float64(1.11))
assertSerialize(t, ROW(Int(12), table1Col1).NOT_IN(table2.SELECT(table2Col3, table3Col1)),
`(ROW($1, table1.col1) NOT IN (
`(ROW($1, table1.col1) NOT IN ((
SELECT table2.col3 AS "table2.col3",
table3.col1 AS "table3.col1"
FROM db.table2
))`, int64(12))
)))`, int64(12))
}
func TestReservedWordEscaped(t *testing.T) {

View file

@ -14,7 +14,9 @@ var (
)
// ROW function is used to create a tuple value that consists of a set of expressions or column values.
var ROW = jet.ROW
func ROW(expressions ...Expression) RowExpression {
return jet.ROW(Dialect, expressions...)
}
// ------------------ Mathematical functions ---------------//
@ -425,10 +427,12 @@ func castFloatLiteral(fraction FloatExpression) FloatExpression {
// ),
var GROUPING_SETS = jet.GROUPING_SETS
// WRAP wraps list of expressions with brackets - ( expression1, expression2, ... )
// The construct (a, b) is normally recognized in expressions as a row constructor. WRAP and ROW method behave exactly the same,
// except when used in GROUPING_SETS. For top level GROUPING SETS expression lists WRAP has to be used.
var WRAP = jet.WRAP
// WRAP surrounds a list of expressions or columns with parentheses, producing new row: (expression1, expression2, ...)
// The construct (a, b) is normally recognized in expressions as a row constructor. WRAP and ROW methods behave exactly the same,
// except when used in GROUPING_SETS and VALUES. In these contexts, WRAP must be used instead of ROW.
func WRAP(expressions ...Expression) RowExpression {
return jet.WRAP(Dialect, expressions...)
}
// ROLLUP operator is used with the GROUP BY clause to generate all prefixes of a group of columns including the empty list.
// It creates extra rows in the result set that represent the subtotal values for each combination of columns.

View file

@ -1,7 +1,6 @@
package postgres
import (
"github.com/go-jet/jet/v2/internal/jet"
"github.com/stretchr/testify/require"
"testing"
"time"
@ -151,12 +150,13 @@ func TestInsert_ON_CONFLICT(t *testing.T) {
VALUES("one", "two").
VALUES("1", "2").
VALUES("theta", "beta").
ON_CONFLICT(table1ColBool).WHERE(table1ColBool.IS_NOT_FALSE()).DO_UPDATE(
SET(table1ColBool.SET(Bool(true)),
table2ColInt.SET(Int(1)),
ColumnList{table1Col1, table1ColBool}.SET(ROW(Int(2), String("two"))),
).WHERE(table1Col1.GT(Int(2))),
).
ON_CONFLICT(table1ColBool).WHERE(table1ColBool.IS_NOT_FALSE()).
DO_UPDATE(
SET(table1ColBool.SET(Bool(true)),
table2ColInt.SET(Int(1)),
ColumnList{table1Col1, table1ColBool}.SET(ROW(Int(2), String("two"))),
).WHERE(table1Col1.GT(Int(2))),
).
RETURNING(table1Col1, table1ColBool)
assertDebugStatementSql(t, stmt, `
@ -178,12 +178,12 @@ func TestInsert_ON_CONFLICT_ON_CONSTRAINT(t *testing.T) {
stmt := table1.INSERT(table1Col1, table1ColBool).
VALUES("one", "two").
VALUES("1", "2").
ON_CONFLICT().ON_CONSTRAINT("idk_primary_key").DO_UPDATE(
SET(table1ColBool.SET(Bool(false)),
table2ColInt.SET(Int(1)),
ColumnList{table1Col1, table1ColBool}.SET(jet.ROW(Int(2), String("two"))),
).WHERE(table1Col1.GT(Int(2))),
).
ON_CONFLICT().ON_CONSTRAINT("idk_primary_key").
DO_UPDATE(
SET(table1ColBool.SET(Bool(false)),
table2ColInt.SET(Int(1)),
ColumnList{table1Col1, table1ColBool}.SET(ROW(Int(2), String("two"))),
).WHERE(table1Col1.GT(Int(2)))).
RETURNING(table1Col1, table1ColBool)
assertDebugStatementSql(t, stmt, `

View file

@ -181,7 +181,7 @@ func (s *selectStatementImpl) FOR(lock RowLock) SelectStatement {
}
func (s *selectStatementImpl) AsTable(alias string) SelectTable {
return newSelectTable(s, alias)
return newSelectTable(s, alias, nil)
}
//-----------------------------------------------------

View file

@ -2,7 +2,7 @@ package postgres
import "github.com/go-jet/jet/v2/internal/jet"
// SelectTable is interface for postgres sub-queries
// SelectTable is interface for postgres temporary tables like sub-queries, VALUES, CTEs etc...
type SelectTable interface {
readableTable
jet.SelectTable
@ -13,9 +13,9 @@ type selectTableImpl struct {
readableTableInterfaceImpl
}
func newSelectTable(selectStmt jet.SerializerHasProjections, alias string) SelectTable {
func newSelectTable(serializerWithProjections jet.SerializerHasProjections, alias string, columnAliases []jet.ColumnExpression) SelectTable {
subQuery := &selectTableImpl{
SelectTable: jet.NewSelectTable(selectStmt, alias),
SelectTable: jet.NewSelectTable(serializerWithProjections, alias, columnAliases),
}
subQuery.readableTableInterfaceImpl.parent = subQuery

View file

@ -136,7 +136,7 @@ func (s *setStatementImpl) OFFSET_e(offset IntegerExpression) setStatement {
}
func (s *setStatementImpl) AsTable(alias string) SelectTable {
return newSelectTable(s, alias)
return newSelectTable(s, alias, nil)
}
const (

32
postgres/values.go Normal file
View file

@ -0,0 +1,32 @@
package postgres
import "github.com/go-jet/jet/v2/internal/jet"
type values struct {
jet.Values
}
// VALUES is a table value constructor that computes a set of one or more rows as a temporary constant table.
// Each row is defined by the WRAP constructor, which takes one or more expressions.
//
// Example usage:
//
// VALUES(
// WRAP(Int32(204), Float32(1.21)),
// WRAP(Int32(207), Float32(1.02)),
// )
func VALUES(rows ...RowExpression) values {
return values{Values: jet.Values(rows)}
}
// AS assigns an alias to the temporary VALUES table, allowing it to be referenced
// within SQL FROM clauses, just like a regular table.
// By default, VALUES columns are named `column1`, `column2`, etc... Default column aliasing can be
// overwritten by passing new list of columns.
//
// Example usage:
//
// VALUES(...).AS("film_values", IntegerColumn("length"), TimestampColumn("update_date"))
func (v values) AS(alias string, columns ...Column) SelectTable {
return newSelectTable(v, alias, columns)
}

View file

@ -6,7 +6,7 @@ import "github.com/go-jet/jet/v2/internal/jet"
type CommonTableExpression interface {
SelectTable
AS(statement jet.SerializerStatement) CommonTableExpression
AS(statement jet.SerializerHasProjections) CommonTableExpression
AS_NOT_MATERIALIZED(statement jet.SerializerStatement) CommonTableExpression
// ALIAS is used to create another alias of the CTE, if a CTE needs to appear multiple times in the main query.
ALIAS(alias string) SelectTable
@ -42,7 +42,7 @@ func CTE(name string, columns ...jet.ColumnExpression) CommonTableExpression {
}
// AS is used to define a CTE query
func (c *commonTableExpression) AS(statement jet.SerializerStatement) CommonTableExpression {
func (c *commonTableExpression) AS(statement jet.SerializerHasProjections) CommonTableExpression {
c.CommonTableExpression.Statement = statement
return c
}
@ -60,7 +60,7 @@ func (c *commonTableExpression) internalCTE() *jet.CommonTableExpression {
// ALIAS is used to create another alias of the CTE, if a CTE needs to appear multiple times in the main query.
func (c *commonTableExpression) ALIAS(name string) SelectTable {
return newSelectTable(c, name)
return newSelectTable(c, name, nil)
}
func toInternalCTE(ctes []CommonTableExpression) []*jet.CommonTableExpression {