Files
db/sqlgen/expression_sql_generator.go
what 37581f3a65 feat: 新增 BooleanFunctionLookup 支持函数式操作符
- sqlgen: 新增 BooleanFunctionLookup,生成 FUNC(LHS, RHS) 格式,优先级高于 BooleanOperatorLookup
- dialect/duckdb: 使用 regexp_matches 替代 ~ 实现部分匹配,等价于 MySQL REGEXP
- sqlgen/expression_sql_generator_test: 补充 BooleanFunctionLookup 测试用例,修正 encode_error 前缀
- sqlgen/sql_dialect_options_test: 验证 BooleanFunctionLookup 默认为 nil
- dialect/duckdb_test: 新增 regexp_matches SQL 生成测试
2026-05-21 13:34:04 +08:00

758 lines
22 KiB
Go

package sqlgen
import (
"database/sql/driver"
"reflect"
"strconv"
"time"
"unicode/utf8"
"git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/internal/util"
)
type (
// An adapter interface to be used by a Dataset to generate SQL for a specific dialect.
// See DefaultAdapter for a concrete implementation and examples.
ExpressionSQLGenerator interface {
Dialect() string
Generate(b sb.SQLBuilder, val interface{})
}
// The default adapter. This class should be used when building a new adapter. When creating a new adapter you can
// either override methods, or more typically update default values.
// See (github.com/doug-martin/goqu/dialect/postgres)
expressionSQLGenerator struct {
dialect string
dialectOptions *SQLDialectOptions
}
)
var (
replacementRune = '?'
TrueLiteral = exp.NewLiteralExpression("TRUE")
FalseLiteral = exp.NewLiteralExpression("FALSE")
ErrEmptyIdentifier = errors.New(
`a empty identifier was encountered, please specify a "schema", "table" or "column"`,
)
ErrUnexpectedNamedWindow = errors.New(`unexpected named window function`)
ErrEmptyCaseWhens = errors.New(`when conditions not found for case statement`)
)
func errUnsupportedExpressionType(e exp.Expression) error {
return errors.New("unsupported expression type %T", e)
}
func errUnsupportedIdentifierExpression(t interface{}) error {
return errors.New("unexpected col type must be string or LiteralExpression received %T", t)
}
func errUnsupportedBooleanExpressionOperator(op exp.BooleanOperation) error {
return errors.New("boolean operator '%+v' not supported", op)
}
func errUnsupportedBitwiseExpressionOperator(op exp.BitwiseOperation) error {
return errors.New("bitwise operator '%+v' not supported", op)
}
func errUnsupportedRangeExpressionOperator(op exp.RangeOperation) error {
return errors.New("range operator %+v not supported", op)
}
func errLateralNotSupported(dialect string) error {
return errors.New("dialect does not support lateral expressions [dialect=%s]", dialect)
}
func NewExpressionSQLGenerator(dialect string, do *SQLDialectOptions) ExpressionSQLGenerator {
return &expressionSQLGenerator{dialect: dialect, dialectOptions: do}
}
func (esg *expressionSQLGenerator) Dialect() string {
return esg.dialect
}
var valuerReflectType = reflect.TypeOf((*driver.Valuer)(nil)).Elem()
func (esg *expressionSQLGenerator) Generate(b sb.SQLBuilder, val interface{}) {
if b.Error() != nil {
return
}
if val == nil {
esg.literalNil(b)
return
}
switch v := val.(type) {
case exp.Expression:
esg.expressionSQL(b, v)
case int:
esg.literalInt(b, int64(v))
case int32:
esg.literalInt(b, int64(v))
case int64:
esg.literalInt(b, v)
case float32:
esg.literalFloat(b, float64(v))
case float64:
esg.literalFloat(b, v)
case string:
esg.literalString(b, v)
case bool:
esg.literalBool(b, v)
case time.Time:
esg.literalTime(b, v)
case *time.Time:
if v == nil {
esg.literalNil(b)
return
}
esg.literalTime(b, *v)
case driver.Valuer:
// See https://github.com/golang/go/commit/0ce1d79a6a771f7449ec493b993ed2a720917870
if rv := reflect.ValueOf(val); rv.Kind() == reflect.Ptr &&
rv.IsNil() &&
rv.Type().Elem().Implements(valuerReflectType) {
esg.literalNil(b)
return
}
dVal, err := v.Value()
if err != nil {
b.SetError(err)
return
}
esg.Generate(b, dVal)
default:
esg.reflectSQL(b, val)
}
}
func (esg *expressionSQLGenerator) reflectSQL(b sb.SQLBuilder, val interface{}) {
v := reflect.Indirect(reflect.ValueOf(val))
valKind := v.Kind()
switch {
case util.IsInvalid(valKind):
esg.literalNil(b)
case util.IsSlice(valKind):
switch t := val.(type) {
case []byte:
esg.literalBytes(b, t)
case []exp.CommonTableExpression:
esg.commonTablesSliceSQL(b, t)
default:
esg.sliceValueSQL(b, v)
}
case util.IsInt(valKind):
esg.Generate(b, v.Int())
case util.IsUint(valKind):
esg.Generate(b, int64(v.Uint()))
case util.IsFloat(valKind):
esg.Generate(b, v.Float())
case util.IsString(valKind):
esg.Generate(b, v.String())
case util.IsBool(valKind):
esg.Generate(b, v.Bool())
default:
b.SetError(errors.NewEncodeError(val))
}
}
//nolint:gocyclo // not complex just long
func (esg *expressionSQLGenerator) expressionSQL(b sb.SQLBuilder, expression exp.Expression) {
switch e := expression.(type) {
case exp.ColumnListExpression:
esg.columnListSQL(b, e)
case exp.ExpressionList:
esg.expressionListSQL(b, e)
case exp.LiteralExpression:
esg.literalExpressionSQL(b, e)
case exp.IdentifierExpression:
esg.identifierExpressionSQL(b, e)
case exp.LateralExpression:
esg.lateralExpressionSQL(b, e)
case exp.AliasedExpression:
esg.aliasedExpressionSQL(b, e)
case exp.BooleanExpression:
esg.booleanExpressionSQL(b, e)
case exp.BitwiseExpression:
esg.bitwiseExpressionSQL(b, e)
case exp.RangeExpression:
esg.rangeExpressionSQL(b, e)
case exp.OrderedExpression:
esg.orderedExpressionSQL(b, e)
case exp.UpdateExpression:
esg.updateExpressionSQL(b, e)
case exp.SQLFunctionExpression:
esg.sqlFunctionExpressionSQL(b, e)
case exp.SQLWindowFunctionExpression:
esg.sqlWindowFunctionExpression(b, e)
case exp.WindowExpression:
esg.windowExpressionSQL(b, e)
case exp.CastExpression:
esg.castExpressionSQL(b, e)
case exp.AppendableExpression:
esg.appendableExpressionSQL(b, e)
case exp.CommonTableExpression:
esg.commonTableExpressionSQL(b, e)
case exp.CompoundExpression:
esg.compoundExpressionSQL(b, e)
case exp.CaseExpression:
esg.caseExpressionSQL(b, e)
case exp.Ex:
esg.expressionMapSQL(b, e)
case exp.ExOr:
esg.expressionOrMapSQL(b, e)
default:
b.SetError(errUnsupportedExpressionType(e))
}
}
// Generates a placeholder (e.g. ?, $1)
func (esg *expressionSQLGenerator) placeHolderSQL(b sb.SQLBuilder, i interface{}) {
b.Write(esg.dialectOptions.PlaceHolderFragment)
if esg.dialectOptions.IncludePlaceholderNum {
b.WriteStrings(strconv.FormatInt(int64(b.CurrentArgPosition()), 10))
}
b.WriteArg(i)
}
// Generates creates the sql for a sub select on a Dataset
func (esg *expressionSQLGenerator) appendableExpressionSQL(b sb.SQLBuilder, a exp.AppendableExpression) {
b.WriteRunes(esg.dialectOptions.LeftParenRune)
a.AppendSQL(b)
b.WriteRunes(esg.dialectOptions.RightParenRune)
if a.GetAs() != nil {
b.Write(esg.dialectOptions.AsFragment)
esg.Generate(b, a.GetAs())
}
}
// Quotes an identifier (e.g. "col", "table"."col"
func (esg *expressionSQLGenerator) identifierExpressionSQL(b sb.SQLBuilder, ident exp.IdentifierExpression) {
if ident.IsEmpty() {
b.SetError(ErrEmptyIdentifier)
return
}
schema, table, col := ident.GetSchema(), ident.GetTable(), ident.GetCol()
if schema != esg.dialectOptions.EmptyString {
b.WriteRunes(esg.dialectOptions.QuoteRune).
WriteStrings(schema).
WriteRunes(esg.dialectOptions.QuoteRune)
}
if table != esg.dialectOptions.EmptyString {
if schema != esg.dialectOptions.EmptyString {
b.WriteRunes(esg.dialectOptions.PeriodRune)
}
b.WriteRunes(esg.dialectOptions.QuoteRune).
WriteStrings(table).
WriteRunes(esg.dialectOptions.QuoteRune)
}
switch t := col.(type) {
case nil:
case string:
if col != esg.dialectOptions.EmptyString {
if table != esg.dialectOptions.EmptyString || schema != esg.dialectOptions.EmptyString {
b.WriteRunes(esg.dialectOptions.PeriodRune)
}
b.WriteRunes(esg.dialectOptions.QuoteRune).
WriteStrings(t).
WriteRunes(esg.dialectOptions.QuoteRune)
}
case exp.LiteralExpression:
if table != esg.dialectOptions.EmptyString || schema != esg.dialectOptions.EmptyString {
b.WriteRunes(esg.dialectOptions.PeriodRune)
}
esg.Generate(b, t)
default:
b.SetError(errUnsupportedIdentifierExpression(col))
}
}
func (esg *expressionSQLGenerator) lateralExpressionSQL(b sb.SQLBuilder, le exp.LateralExpression) {
if !esg.dialectOptions.SupportsLateral {
b.SetError(errLateralNotSupported(esg.dialect))
return
}
b.Write(esg.dialectOptions.LateralFragment)
esg.Generate(b, le.Table())
}
// Generates SQL NULL value
func (esg *expressionSQLGenerator) literalNil(b sb.SQLBuilder) {
if b.IsPrepared() {
esg.placeHolderSQL(b, nil)
return
}
b.Write(esg.dialectOptions.Null)
}
// Generates SQL bool literal, (e.g. TRUE, FALSE, mysql 1, 0, sqlite3 1, 0)
func (esg *expressionSQLGenerator) literalBool(b sb.SQLBuilder, bl bool) {
if b.IsPrepared() {
esg.placeHolderSQL(b, bl)
return
}
if bl {
b.Write(esg.dialectOptions.True)
} else {
b.Write(esg.dialectOptions.False)
}
}
// Generates SQL for a time.Time value
func (esg *expressionSQLGenerator) literalTime(b sb.SQLBuilder, t time.Time) {
if b.IsPrepared() {
esg.placeHolderSQL(b, t)
return
}
esg.Generate(b, t.In(timeLocation).Format(esg.dialectOptions.TimeFormat))
}
// Generates SQL for a Float Value
func (esg *expressionSQLGenerator) literalFloat(b sb.SQLBuilder, f float64) {
if b.IsPrepared() {
esg.placeHolderSQL(b, f)
return
}
b.WriteStrings(strconv.FormatFloat(f, 'f', -1, 64))
}
// Generates SQL for an int value
func (esg *expressionSQLGenerator) literalInt(b sb.SQLBuilder, i int64) {
if b.IsPrepared() {
esg.placeHolderSQL(b, i)
return
}
b.WriteStrings(strconv.FormatInt(i, 10))
}
// Generates SQL for a string
func (esg *expressionSQLGenerator) literalString(b sb.SQLBuilder, s string) {
if b.IsPrepared() {
esg.placeHolderSQL(b, s)
return
}
b.WriteRunes(esg.dialectOptions.StringQuote)
for _, char := range s {
if e, ok := esg.dialectOptions.EscapedRunes[char]; ok {
b.Write(e)
} else {
b.WriteRunes(char)
}
}
b.WriteRunes(esg.dialectOptions.StringQuote)
}
// Generates SQL for a slice of bytes
func (esg *expressionSQLGenerator) literalBytes(b sb.SQLBuilder, bs []byte) {
if b.IsPrepared() {
esg.placeHolderSQL(b, bs)
return
}
b.WriteRunes(esg.dialectOptions.StringQuote)
i := 0
for len(bs) > 0 {
char, l := utf8.DecodeRune(bs)
if e, ok := esg.dialectOptions.EscapedRunes[char]; ok {
b.Write(e)
} else {
b.WriteRunes(char)
}
i++
bs = bs[l:]
}
b.WriteRunes(esg.dialectOptions.StringQuote)
}
// Generates SQL for a slice of values (e.g. []int64{1,2,3,4} -> (1,2,3,4)
func (esg *expressionSQLGenerator) sliceValueSQL(b sb.SQLBuilder, slice reflect.Value) {
b.WriteRunes(esg.dialectOptions.LeftParenRune)
for i, l := 0, slice.Len(); i < l; i++ {
esg.Generate(b, slice.Index(i).Interface())
if i < l-1 {
b.WriteRunes(esg.dialectOptions.CommaRune, esg.dialectOptions.SpaceRune)
}
}
b.WriteRunes(esg.dialectOptions.RightParenRune)
}
// Generates SQL for an AliasedExpression (e.g. I("a").As("b") -> "a" AS "b")
func (esg *expressionSQLGenerator) aliasedExpressionSQL(b sb.SQLBuilder, aliased exp.AliasedExpression) {
esg.Generate(b, aliased.Aliased())
b.Write(esg.dialectOptions.AsFragment)
esg.Generate(b, aliased.GetAs())
}
// Generates SQL for a BooleanExpresion (e.g. I("a").Eq(2) -> "a" = 2)
func (esg *expressionSQLGenerator) booleanExpressionSQL(b sb.SQLBuilder, operator exp.BooleanExpression) {
operatorOp := operator.Op()
// 函数式操作符:生成 FUNC(LHS, RHS)
if fn, ok := esg.dialectOptions.BooleanFunctionLookup[operatorOp]; ok {
b.Write(fn)
b.WriteRunes(esg.dialectOptions.LeftParenRune)
esg.Generate(b, operator.LHS())
b.WriteRunes(esg.dialectOptions.CommaRune, esg.dialectOptions.SpaceRune)
esg.Generate(b, operator.RHS())
b.WriteRunes(esg.dialectOptions.RightParenRune)
return
}
b.WriteRunes(esg.dialectOptions.LeftParenRune)
esg.Generate(b, operator.LHS())
b.WriteRunes(esg.dialectOptions.SpaceRune)
if val, ok := esg.dialectOptions.BooleanOperatorLookup[operatorOp]; ok {
b.Write(val)
} else {
b.SetError(errUnsupportedBooleanExpressionOperator(operatorOp))
return
}
rhs := operator.RHS()
if (operatorOp == exp.IsOp || operatorOp == exp.IsNotOp) && rhs != nil && !esg.dialectOptions.BooleanDataTypeSupported {
b.SetError(errors.New("boolean data type is not supported by dialect %q", esg.dialect))
return
}
if (operatorOp == exp.IsOp || operatorOp == exp.IsNotOp) && esg.dialectOptions.UseLiteralIsBools {
// these values must be interpolated because preparing them generates invalid SQL
switch rhs {
case true:
rhs = TrueLiteral
case false:
rhs = FalseLiteral
case nil:
rhs = exp.NewLiteralExpression(string(esg.dialectOptions.Null))
}
}
b.WriteRunes(esg.dialectOptions.SpaceRune)
if (operatorOp == exp.IsOp || operatorOp == exp.IsNotOp) && rhs == nil && !esg.dialectOptions.BooleanDataTypeSupported {
// e.g. for SQL server dialect which does not support "IS @p1" for "IS NULL"
b.Write(esg.dialectOptions.Null)
} else {
esg.Generate(b, rhs)
}
b.WriteRunes(esg.dialectOptions.RightParenRune)
}
// Generates SQL for a BitwiseExpresion (e.g. I("a").BitwiseOr(2) - > "a" | 2)
func (esg *expressionSQLGenerator) bitwiseExpressionSQL(b sb.SQLBuilder, operator exp.BitwiseExpression) {
b.WriteRunes(esg.dialectOptions.LeftParenRune)
if operator.LHS() != nil {
esg.Generate(b, operator.LHS())
b.WriteRunes(esg.dialectOptions.SpaceRune)
}
operatorOp := operator.Op()
if val, ok := esg.dialectOptions.BitwiseOperatorLookup[operatorOp]; ok {
b.Write(val)
} else {
b.SetError(errUnsupportedBitwiseExpressionOperator(operatorOp))
return
}
b.WriteRunes(esg.dialectOptions.SpaceRune)
esg.Generate(b, operator.RHS())
b.WriteRunes(esg.dialectOptions.RightParenRune)
}
// Generates SQL for a RangeExpresion (e.g. I("a").Between(RangeVal{Start:2,End:5}) -> "a" BETWEEN 2 AND 5)
func (esg *expressionSQLGenerator) rangeExpressionSQL(b sb.SQLBuilder, operator exp.RangeExpression) {
b.WriteRunes(esg.dialectOptions.LeftParenRune)
esg.Generate(b, operator.LHS())
b.WriteRunes(esg.dialectOptions.SpaceRune)
operatorOp := operator.Op()
if val, ok := esg.dialectOptions.RangeOperatorLookup[operatorOp]; ok {
b.Write(val)
} else {
b.SetError(errUnsupportedRangeExpressionOperator(operatorOp))
return
}
rhs := operator.RHS()
b.WriteRunes(esg.dialectOptions.SpaceRune)
esg.Generate(b, rhs.Start())
b.Write(esg.dialectOptions.AndFragment)
esg.Generate(b, rhs.End())
b.WriteRunes(esg.dialectOptions.RightParenRune)
}
// Generates SQL for an OrderedExpression (e.g. I("a").Asc() -> "a" ASC)
func (esg *expressionSQLGenerator) orderedExpressionSQL(b sb.SQLBuilder, order exp.OrderedExpression) {
esg.Generate(b, order.SortExpression())
if order.IsAsc() {
b.Write(esg.dialectOptions.AscFragment)
} else {
b.Write(esg.dialectOptions.DescFragment)
}
switch order.NullSortType() {
case exp.NoNullsSortType:
return
case exp.NullsFirstSortType:
b.Write(esg.dialectOptions.NullsFirstFragment)
case exp.NullsLastSortType:
b.Write(esg.dialectOptions.NullsLastFragment)
}
}
// Generates SQL for an ExpressionList (e.g. And(I("a").Eq("a"), I("b").Eq("b")) -> (("a" = 'a') AND ("b" = 'b')))
func (esg *expressionSQLGenerator) expressionListSQL(b sb.SQLBuilder, expressionList exp.ExpressionList) {
if expressionList.IsEmpty() {
return
}
var op []byte
if expressionList.Type() == exp.AndType {
op = esg.dialectOptions.AndFragment
} else {
op = esg.dialectOptions.OrFragment
}
exps := expressionList.Expressions()
expLen := len(exps) - 1
if expLen > 0 {
b.WriteRunes(esg.dialectOptions.LeftParenRune)
} else {
esg.Generate(b, exps[0])
return
}
for i, e := range exps {
esg.Generate(b, e)
if i < expLen {
b.Write(op)
}
}
b.WriteRunes(esg.dialectOptions.RightParenRune)
}
// Generates SQL for a ColumnListExpression
func (esg *expressionSQLGenerator) columnListSQL(b sb.SQLBuilder, columnList exp.ColumnListExpression) {
cols := columnList.Columns()
colLen := len(cols)
for i, col := range cols {
esg.Generate(b, col)
if i < colLen-1 {
b.WriteRunes(esg.dialectOptions.CommaRune, esg.dialectOptions.SpaceRune)
}
}
}
// Generates SQL for an UpdateEpxresion
func (esg *expressionSQLGenerator) updateExpressionSQL(b sb.SQLBuilder, update exp.UpdateExpression) {
esg.Generate(b, update.Col())
b.WriteRunes(esg.dialectOptions.SetOperatorRune)
esg.Generate(b, update.Val())
}
// Generates SQL for a LiteralExpression
//
// L("a + b") -> a + b
// L("a = ?", 1) -> a = 1
func (esg *expressionSQLGenerator) literalExpressionSQL(b sb.SQLBuilder, literal exp.LiteralExpression) {
l := literal.Literal()
args := literal.Args()
if argsLen := len(args); argsLen > 0 {
currIndex := 0
for _, char := range l {
if char == replacementRune && currIndex < argsLen {
esg.Generate(b, args[currIndex])
currIndex++
} else {
b.WriteRunes(char)
}
}
return
}
b.WriteStrings(l)
}
// Generates SQL for a SQLFunctionExpression
//
// COUNT(I("a")) -> COUNT("a")
func (esg *expressionSQLGenerator) sqlFunctionExpressionSQL(b sb.SQLBuilder, sqlFunc exp.SQLFunctionExpression) {
if sqlFunc.Name() == "DISTINCT" && !esg.dialectOptions.SupportsDistinct {
for i, arg := range sqlFunc.Args() {
if i > 0 {
b.WriteRunes(esg.dialectOptions.CommaRune, esg.dialectOptions.SpaceRune)
}
esg.Generate(b, arg)
}
return
}
b.WriteStrings(sqlFunc.Name())
esg.Generate(b, sqlFunc.Args())
}
func (esg *expressionSQLGenerator) sqlWindowFunctionExpression(b sb.SQLBuilder, sqlWinFunc exp.SQLWindowFunctionExpression) {
if !esg.dialectOptions.SupportsWindowFunction {
b.SetError(ErrWindowNotSupported(esg.dialect))
return
}
esg.Generate(b, sqlWinFunc.Func())
b.Write(esg.dialectOptions.WindowOverFragment)
switch {
case sqlWinFunc.HasWindowName():
esg.Generate(b, sqlWinFunc.WindowName())
case sqlWinFunc.HasWindow():
if sqlWinFunc.Window().HasName() {
b.SetError(ErrUnexpectedNamedWindow)
return
}
esg.Generate(b, sqlWinFunc.Window())
default:
esg.Generate(b, exp.NewWindowExpression(nil, nil, nil, nil))
}
}
func (esg *expressionSQLGenerator) windowExpressionSQL(b sb.SQLBuilder, we exp.WindowExpression) {
if !esg.dialectOptions.SupportsWindowFunction {
b.SetError(ErrWindowNotSupported(esg.dialect))
return
}
if we.HasName() {
esg.Generate(b, we.Name())
b.Write(esg.dialectOptions.AsFragment)
}
b.WriteRunes(esg.dialectOptions.LeftParenRune)
hasPartition := we.HasPartitionBy()
hasOrder := we.HasOrder()
if we.HasParent() {
esg.Generate(b, we.Parent())
if hasPartition || hasOrder {
b.WriteRunes(esg.dialectOptions.SpaceRune)
}
}
if hasPartition {
b.Write(esg.dialectOptions.WindowPartitionByFragment)
esg.Generate(b, we.PartitionCols())
if hasOrder {
b.WriteRunes(esg.dialectOptions.SpaceRune)
}
}
if hasOrder {
b.Write(esg.dialectOptions.WindowOrderByFragment)
esg.Generate(b, we.OrderCols())
}
b.WriteRunes(esg.dialectOptions.RightParenRune)
}
// Generates SQL for a CastExpression
//
// I("a").Cast("NUMERIC") -> CAST("a" AS NUMERIC)
func (esg *expressionSQLGenerator) castExpressionSQL(b sb.SQLBuilder, cast exp.CastExpression) {
b.Write(esg.dialectOptions.CastFragment).WriteRunes(esg.dialectOptions.LeftParenRune)
esg.Generate(b, cast.Casted())
b.Write(esg.dialectOptions.AsFragment)
esg.Generate(b, cast.Type())
b.WriteRunes(esg.dialectOptions.RightParenRune)
}
// Generates the sql for the WITH clauses for common table expressions (CTE)
func (esg *expressionSQLGenerator) commonTablesSliceSQL(b sb.SQLBuilder, ctes []exp.CommonTableExpression) {
l := len(ctes)
if l == 0 {
return
}
if !esg.dialectOptions.SupportsWithCTE {
b.SetError(ErrCTENotSupported(esg.dialect))
return
}
b.Write(esg.dialectOptions.WithFragment)
anyRecursive := false
for _, cte := range ctes {
anyRecursive = anyRecursive || cte.IsRecursive()
}
if anyRecursive {
if !esg.dialectOptions.SupportsWithCTERecursive {
b.SetError(ErrRecursiveCTENotSupported(esg.dialect))
return
}
b.Write(esg.dialectOptions.RecursiveFragment)
}
for i, cte := range ctes {
esg.Generate(b, cte)
if i < l-1 {
b.WriteRunes(esg.dialectOptions.CommaRune, esg.dialectOptions.SpaceRune)
}
}
b.WriteRunes(esg.dialectOptions.SpaceRune)
}
// Generates SQL for a CommonTableExpression
func (esg *expressionSQLGenerator) commonTableExpressionSQL(b sb.SQLBuilder, cte exp.CommonTableExpression) {
esg.Generate(b, cte.Name())
b.Write(esg.dialectOptions.AsFragment)
esg.Generate(b, cte.SubQuery())
}
// Generates SQL for a CompoundExpression
func (esg *expressionSQLGenerator) compoundExpressionSQL(b sb.SQLBuilder, compound exp.CompoundExpression) {
switch compound.Type() {
case exp.UnionCompoundType:
b.Write(esg.dialectOptions.UnionFragment)
case exp.UnionAllCompoundType:
b.Write(esg.dialectOptions.UnionAllFragment)
case exp.IntersectCompoundType:
b.Write(esg.dialectOptions.IntersectFragment)
case exp.IntersectAllCompoundType:
b.Write(esg.dialectOptions.IntersectAllFragment)
}
if esg.dialectOptions.WrapCompoundsInParens {
b.WriteRunes(esg.dialectOptions.LeftParenRune)
compound.RHS().AppendSQL(b)
b.WriteRunes(esg.dialectOptions.RightParenRune)
} else {
compound.RHS().AppendSQL(b)
}
}
// Generates SQL for a CaseExpression
func (esg *expressionSQLGenerator) caseExpressionSQL(b sb.SQLBuilder, caseExpression exp.CaseExpression) {
caseVal := caseExpression.GetValue()
whens := caseExpression.GetWhens()
elseResult := caseExpression.GetElse()
if len(whens) == 0 {
b.SetError(ErrEmptyCaseWhens)
return
}
b.Write(esg.dialectOptions.CaseFragment)
if caseVal != nil {
esg.Generate(b, caseVal)
}
for _, when := range whens {
b.Write(esg.dialectOptions.WhenFragment)
esg.Generate(b, when.Condition())
b.Write(esg.dialectOptions.ThenFragment)
esg.Generate(b, when.Result())
}
if elseResult != nil {
b.Write(esg.dialectOptions.ElseFragment)
esg.Generate(b, elseResult.Result())
}
b.Write(esg.dialectOptions.EndFragment)
}
func (esg *expressionSQLGenerator) expressionMapSQL(b sb.SQLBuilder, ex exp.Ex) {
expressionList, err := ex.ToExpressions()
if err != nil {
b.SetError(err)
return
}
esg.Generate(b, expressionList)
}
func (esg *expressionSQLGenerator) expressionOrMapSQL(b sb.SQLBuilder, ex exp.ExOr) {
expressionList, err := ex.ToExpressions()
if err != nil {
b.SetError(err)
return
}
esg.Generate(b, expressionList)
}