Add support for VALUES statement.
This commit is contained in:
parent
3fcbbec427
commit
8d112f7db8
41 changed files with 1296 additions and 131 deletions
|
|
@ -13,6 +13,7 @@ type Dialect interface {
|
|||
ArgumentPlaceholder() QueryPlaceholderFunc
|
||||
IsReservedWord(name string) bool
|
||||
SerializeOrderBy() func(expression Expression, ascending, nullsFirst *bool) SerializerFunc
|
||||
ValuesDefaultColumnName(index int) string
|
||||
}
|
||||
|
||||
// SerializerFunc func
|
||||
|
|
@ -35,6 +36,7 @@ type DialectParams struct {
|
|||
ArgumentPlaceholder QueryPlaceholderFunc
|
||||
ReservedWords []string
|
||||
SerializeOrderBy func(expression Expression, ascending, nullsFirst *bool) SerializerFunc
|
||||
ValuesDefaultColumnName func(index int) string
|
||||
}
|
||||
|
||||
// NewDialect creates new dialect with params
|
||||
|
|
@ -49,6 +51,7 @@ func NewDialect(params DialectParams) Dialect {
|
|||
argumentPlaceholder: params.ArgumentPlaceholder,
|
||||
reservedWords: arrayOfStringsToMapOfStrings(params.ReservedWords),
|
||||
serializeOrderBy: params.SerializeOrderBy,
|
||||
valuesDefaultColumnName: params.ValuesDefaultColumnName,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -62,6 +65,7 @@ type dialectImpl struct {
|
|||
argumentPlaceholder QueryPlaceholderFunc
|
||||
reservedWords map[string]bool
|
||||
serializeOrderBy func(expression Expression, ascending, nullsFirst *bool) SerializerFunc
|
||||
valuesDefaultColumnName func(index int) string
|
||||
}
|
||||
|
||||
func (d *dialectImpl) Name() string {
|
||||
|
|
@ -107,6 +111,10 @@ func (d *dialectImpl) SerializeOrderBy() func(expression Expression, ascending,
|
|||
return d.serializeOrderBy
|
||||
}
|
||||
|
||||
func (d *dialectImpl) ValuesDefaultColumnName(index int) string {
|
||||
return d.valuesDefaultColumnName(index)
|
||||
}
|
||||
|
||||
func arrayOfStringsToMapOfStrings(arr []string) map[string]bool {
|
||||
ret := map[string]bool{}
|
||||
for _, elem := range arr {
|
||||
|
|
|
|||
|
|
@ -51,12 +51,12 @@ func (e *ExpressionInterfaceImpl) IS_NOT_NULL() BoolExpression {
|
|||
|
||||
// IN checks if this expressions matches any in expressions list
|
||||
func (e *ExpressionInterfaceImpl) IN(expressions ...Expression) BoolExpression {
|
||||
return newBinaryBoolOperatorExpression(e.Parent, WRAP(expressions...), "IN")
|
||||
return newBinaryBoolOperatorExpression(e.Parent, wrap(expressions...), "IN")
|
||||
}
|
||||
|
||||
// NOT_IN checks if this expressions is different of all expressions in expressions list
|
||||
func (e *ExpressionInterfaceImpl) NOT_IN(expressions ...Expression) BoolExpression {
|
||||
return newBinaryBoolOperatorExpression(e.Parent, WRAP(expressions...), "NOT IN")
|
||||
return newBinaryBoolOperatorExpression(e.Parent, wrap(expressions...), "NOT IN")
|
||||
}
|
||||
|
||||
// AS the temporary alias name to assign to the expression
|
||||
|
|
@ -316,15 +316,6 @@ func (s *complexExpression) serialize(statement StatementType, out *SQLBuilder,
|
|||
}
|
||||
}
|
||||
|
||||
type skipParenthesisWrap struct {
|
||||
Expression
|
||||
}
|
||||
|
||||
func skipWrap(expression Expression) Expression {
|
||||
return &skipParenthesisWrap{expression}
|
||||
}
|
||||
|
||||
// since the expression is a function parameter, there is no need to wrap it in parentheses
|
||||
func (s *skipParenthesisWrap) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
|
||||
s.Expression.serialize(statement, out, append(options, NoWrap)...)
|
||||
func wrap(expressions ...Expression) Expression {
|
||||
return NewFunc("", expressions, nil)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,9 +56,9 @@ func (p *orderSetAggregateFuncExpression) serialize(statement StatementType, out
|
|||
out.WriteString(p.name)
|
||||
|
||||
if p.fraction != nil {
|
||||
WRAP(p.fraction).serialize(statement, out, FallTrough(options)...)
|
||||
wrap(p.fraction).serialize(statement, out, FallTrough(options)...)
|
||||
} else {
|
||||
WRAP().serialize(statement, out, FallTrough(options)...)
|
||||
wrap().serialize(statement, out, FallTrough(options)...)
|
||||
}
|
||||
out.WriteString("WITHIN GROUP")
|
||||
p.orderBy.serialize(statement, out)
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ AVG(table1.col_int) AS "avg",
|
|||
table2.col3 AS "col3",
|
||||
table2.col4 AS "col4"`)
|
||||
|
||||
subQueryProjections := projectionList.fromImpl(NewSelectTable(nil, "subQuery"))
|
||||
subQueryProjections := projectionList.fromImpl(NewSelectTable(nil, "subQuery", nil))
|
||||
|
||||
assertProjectionSerialize(t, subQueryProjections,
|
||||
`"subQuery"."table1.col3" AS "table1.col3",
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ type rawStatementImpl struct {
|
|||
}
|
||||
|
||||
// RawStatement creates new sql statements from raw query and optional map of named arguments
|
||||
func RawStatement(dialect Dialect, rawQuery string, namedArgument ...map[string]interface{}) Statement {
|
||||
func RawStatement(dialect Dialect, rawQuery string, namedArgument ...map[string]interface{}) SerializerStatement {
|
||||
newRawStatement := rawStatementImpl{
|
||||
serializerStatementInterfaceImpl: serializerStatementInterfaceImpl{
|
||||
dialect: dialect,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package jet
|
|||
// RowExpression interface
|
||||
type RowExpression interface {
|
||||
Expression
|
||||
HasProjections
|
||||
|
||||
EQ(rhs RowExpression) BoolExpression
|
||||
NOT_EQ(rhs RowExpression) BoolExpression
|
||||
|
|
@ -16,7 +17,9 @@ type RowExpression interface {
|
|||
}
|
||||
|
||||
type rowInterfaceImpl struct {
|
||||
parent RowExpression
|
||||
parent Expression
|
||||
dialect Dialect
|
||||
elemCount int
|
||||
}
|
||||
|
||||
func (n *rowInterfaceImpl) EQ(rhs RowExpression) BoolExpression {
|
||||
|
|
@ -51,13 +54,44 @@ func (n *rowInterfaceImpl) LT_EQ(rhs RowExpression) BoolExpression {
|
|||
return LtEq(n.parent, rhs)
|
||||
}
|
||||
|
||||
//---------------------------------------------------//
|
||||
func (n *rowInterfaceImpl) projections() ProjectionList {
|
||||
var ret ProjectionList
|
||||
|
||||
for i := 0; i < n.elemCount; i++ {
|
||||
rowColumn := NewColumnImpl(n.dialect.ValuesDefaultColumnName(i), "", nil)
|
||||
ret = append(ret, &rowColumn)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// ---------------------------------------------------//
|
||||
type rowExpressionWrapper struct {
|
||||
rowInterfaceImpl
|
||||
Expression
|
||||
}
|
||||
|
||||
func newRowExpression(name string, dialect Dialect, expressions ...Expression) RowExpression {
|
||||
ret := &rowExpressionWrapper{}
|
||||
ret.rowInterfaceImpl.parent = ret
|
||||
|
||||
ret.Expression = NewFunc(name, expressions, ret)
|
||||
ret.dialect = dialect
|
||||
ret.elemCount = len(expressions)
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// ROW function is used to create a tuple value that consists of a set of expressions or column values.
|
||||
func ROW(dialect Dialect, expressions ...Expression) RowExpression {
|
||||
return newRowExpression("ROW", dialect, expressions...)
|
||||
}
|
||||
|
||||
// WRAP creates row expressions without ROW keyword `( expression1, expression2, ... )`.
|
||||
func WRAP(dialect Dialect, expressions ...Expression) RowExpression {
|
||||
return newRowExpression("", dialect, expressions...)
|
||||
}
|
||||
|
||||
// RowExp serves as a wrapper for an arbitrary expression, treating it as a row expression.
|
||||
// This enables the Go compiler to interpret any expression as a row expression
|
||||
// Note: This does not modify the generated SQL builder output by adding a SQL CAST operation.
|
||||
|
|
@ -66,13 +100,3 @@ func RowExp(expression Expression) RowExpression {
|
|||
rowExpressionWrap.rowInterfaceImpl.parent = &rowExpressionWrap
|
||||
return &rowExpressionWrap
|
||||
}
|
||||
|
||||
// ROW function is used to create a tuple value that consists of a set of expressions or column values.
|
||||
func ROW(expressions ...Expression) RowExpression {
|
||||
return RowExp(NewFunc("ROW", expressions, nil))
|
||||
}
|
||||
|
||||
// WRAP creates row expressions without ROW keyword `( expression1, expression2, ... )`.
|
||||
func WRAP(expressions ...Expression) RowExpression {
|
||||
return RowExp(NewFunc("", expressions, nil))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,15 +8,21 @@ type SelectTable interface {
|
|||
}
|
||||
|
||||
type selectTableImpl struct {
|
||||
Statement SerializerHasProjections
|
||||
alias string
|
||||
Statement SerializerHasProjections
|
||||
alias string
|
||||
columnAliases []ColumnExpression
|
||||
}
|
||||
|
||||
// NewSelectTable func
|
||||
func NewSelectTable(selectStmt SerializerHasProjections, alias string) selectTableImpl {
|
||||
func NewSelectTable(selectStmt SerializerHasProjections, alias string, columnAliases []ColumnExpression) selectTableImpl {
|
||||
selectTable := selectTableImpl{
|
||||
Statement: selectStmt,
|
||||
alias: alias,
|
||||
Statement: selectStmt,
|
||||
alias: alias,
|
||||
columnAliases: columnAliases,
|
||||
}
|
||||
|
||||
for _, column := range selectTable.columnAliases {
|
||||
column.setSubQuery(selectTable)
|
||||
}
|
||||
|
||||
return selectTable
|
||||
|
|
@ -31,6 +37,10 @@ func (s selectTableImpl) Alias() string {
|
|||
}
|
||||
|
||||
func (s selectTableImpl) AllColumns() ProjectionList {
|
||||
if len(s.columnAliases) > 0 {
|
||||
return ColumnListToProjectionList(s.columnAliases)
|
||||
}
|
||||
|
||||
projectionList := s.projections().fromImpl(s)
|
||||
return projectionList.(ProjectionList)
|
||||
}
|
||||
|
|
@ -40,6 +50,12 @@ func (s selectTableImpl) serialize(statement StatementType, out *SQLBuilder, opt
|
|||
|
||||
out.WriteString("AS")
|
||||
out.WriteIdentifier(s.alias)
|
||||
|
||||
if len(s.columnAliases) > 0 {
|
||||
out.WriteByte('(')
|
||||
SerializeColumnExpressionNames(s.columnAliases, out)
|
||||
out.WriteByte(')')
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------
|
||||
|
|
@ -50,7 +66,7 @@ type lateralImpl struct {
|
|||
|
||||
// NewLateral creates new lateral expression from select statement with alias
|
||||
func NewLateral(selectStmt SerializerStatement, alias string) SelectTable {
|
||||
return lateralImpl{selectTableImpl: NewSelectTable(selectStmt, alias)}
|
||||
return lateralImpl{selectTableImpl: NewSelectTable(selectStmt, alias, nil)}
|
||||
}
|
||||
|
||||
func (s lateralImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
|
||||
|
|
|
|||
35
internal/jet/values.go
Normal file
35
internal/jet/values.go
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
package jet
|
||||
|
||||
// Values hold a set of one or more rows
|
||||
type Values []RowExpression
|
||||
|
||||
func (v Values) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
|
||||
out.WriteByte('(')
|
||||
out.IncreaseIdent(5)
|
||||
|
||||
out.NewLine()
|
||||
out.WriteString("VALUES")
|
||||
|
||||
for rowIndex, row := range v {
|
||||
if rowIndex > 0 {
|
||||
out.WriteString(",")
|
||||
out.NewLine()
|
||||
} else {
|
||||
out.IncreaseIdent(7)
|
||||
}
|
||||
|
||||
row.serialize(statement, out, options...)
|
||||
}
|
||||
out.DecreaseIdent(7)
|
||||
out.DecreaseIdent(5)
|
||||
out.NewLine()
|
||||
out.WriteByte(')')
|
||||
}
|
||||
|
||||
func (v Values) projections() ProjectionList {
|
||||
if len(v) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return v[0].projections()
|
||||
}
|
||||
|
|
@ -64,7 +64,7 @@ type CommonTableExpression struct {
|
|||
// CTE creates new named CommonTableExpression
|
||||
func CTE(name string, columns ...ColumnExpression) CommonTableExpression {
|
||||
cte := CommonTableExpression{
|
||||
selectTableImpl: NewSelectTable(nil, name),
|
||||
selectTableImpl: NewSelectTable(nil, name, columns),
|
||||
Columns: columns,
|
||||
}
|
||||
|
||||
|
|
@ -99,12 +99,3 @@ func (c CommonTableExpression) serialize(statement StatementType, out *SQLBuilde
|
|||
out.WriteIdentifier(c.alias)
|
||||
}
|
||||
}
|
||||
|
||||
// AllColumns returns list of all projections in the CTE
|
||||
func (c CommonTableExpression) AllColumns() ProjectionList {
|
||||
if len(c.Columns) > 0 {
|
||||
return ColumnListToProjectionList(c.Columns)
|
||||
}
|
||||
|
||||
return c.selectTableImpl.AllColumns()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue