Files

167 lines
4.6 KiB
Go

// Copyright 2024 Huan Du. All rights reserved.
// Licensed under the MIT license that can be found in the LICENSE file.
package sqlbuilder
import (
"reflect"
"github.com/huandu/go-clone"
)
const (
cteQueryMarkerInit injectionMarker = iota
cteQueryMarkerAfterTable
cteQueryMarkerAfterAs
)
// CTETable creates a new CTE query builder with default flavor, marking it as a table.
//
// The resulting CTE query can be used in a `SelectBuilder“, where its table name will be
// automatically included in the FROM clause.
func CTETable(name string, cols ...string) *CTEQueryBuilder {
return DefaultFlavor.NewCTEQueryBuilder().AddToTableList().Table(name, cols...)
}
// CTEQuery creates a new CTE query builder with default flavor.
func CTEQuery(name string, cols ...string) *CTEQueryBuilder {
return DefaultFlavor.NewCTEQueryBuilder().Table(name, cols...)
}
func newCTEQueryBuilder() *CTEQueryBuilder {
return &CTEQueryBuilder{
args: &Args{},
injection: newInjection(),
}
}
// Clone returns a deep copy of CTEQueryBuilder.
// It's useful when you want to create a base builder and clone it to build similar queries.
func (ctetb *CTEQueryBuilder) Clone() *CTEQueryBuilder {
return clone.Clone(ctetb).(*CTEQueryBuilder)
}
func init() {
t := reflect.TypeOf(CTEQueryBuilder{})
clone.SetCustomFunc(t, func(allocator *clone.Allocator, old, new reflect.Value) {
cloned := allocator.CloneSlowly(old)
new.Set(cloned)
ctetb := cloned.Addr().Interface().(*CTEQueryBuilder)
ctetb.args.Replace(ctetb.builderVar, ctetb.builder)
})
}
// CTEQueryBuilder is a builder to build one table in CTE (Common Table Expression).
type CTEQueryBuilder struct {
name string
cols []string
builder Builder
builderVar string
// if true, this query's table name will be automatically added to the table list
// in FROM clause of SELECT statement.
autoAddToTableList bool
args *Args
injection *injection
marker injectionMarker
}
var _ Builder = new(CTEQueryBuilder)
// CTETableBuilder is an alias of CTEQueryBuilder for backward compatibility.
//
// Deprecated: use CTEQueryBuilder instead.
type CTETableBuilder = CTEQueryBuilder
// Table sets the table name and columns in a CTE table.
func (ctetb *CTEQueryBuilder) Table(name string, cols ...string) *CTEQueryBuilder {
ctetb.name = name
ctetb.cols = cols
ctetb.marker = cteQueryMarkerAfterTable
return ctetb
}
// As sets the builder to select data.
func (ctetb *CTEQueryBuilder) As(builder Builder) *CTEQueryBuilder {
ctetb.builder = builder
ctetb.builderVar = ctetb.args.Add(builder)
ctetb.marker = cteQueryMarkerAfterAs
return ctetb
}
// AddToTableList sets flag to add table name to table list in FROM clause of SELECT statement.
func (ctetb *CTEQueryBuilder) AddToTableList() *CTEQueryBuilder {
ctetb.autoAddToTableList = true
return ctetb
}
// ShouldAddToTableList returns flag to add table name to table list in FROM clause of SELECT statement.
func (ctetb *CTEQueryBuilder) ShouldAddToTableList() bool {
return ctetb.autoAddToTableList
}
// String returns the compiled CTE string.
func (ctetb *CTEQueryBuilder) String() string {
sql, _ := ctetb.Build()
return sql
}
// Build returns compiled CTE string and args.
func (ctetb *CTEQueryBuilder) Build() (sql string, args []interface{}) {
return ctetb.BuildWithFlavor(ctetb.args.Flavor)
}
// BuildWithFlavor builds a CTE with the specified flavor and initial arguments.
func (ctetb *CTEQueryBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{}) (sql string, args []interface{}) {
buf := newStringBuilder()
ctetb.injection.WriteTo(buf, cteQueryMarkerInit)
if ctetb.name != "" {
buf.WriteLeadingString(ctetb.name)
if len(ctetb.cols) > 0 {
buf.WriteLeadingString("(")
buf.WriteStrings(ctetb.cols, ", ")
buf.WriteString(")")
}
ctetb.injection.WriteTo(buf, cteQueryMarkerAfterTable)
}
if ctetb.builderVar != "" {
buf.WriteLeadingString("AS (")
buf.WriteString(ctetb.builderVar)
buf.WriteRune(')')
ctetb.injection.WriteTo(buf, cteQueryMarkerAfterAs)
}
return ctetb.args.CompileWithFlavor(buf.String(), flavor, initialArg...)
}
// SetFlavor sets the flavor of compiled sql.
func (ctetb *CTEQueryBuilder) SetFlavor(flavor Flavor) (old Flavor) {
old = ctetb.args.Flavor
ctetb.args.Flavor = flavor
return
}
// Flavor returns flavor of builder
func (ctetb *CTEQueryBuilder) Flavor() Flavor {
return ctetb.args.Flavor
}
// SQL adds an arbitrary sql to current position.
func (ctetb *CTEQueryBuilder) SQL(sql string) *CTEQueryBuilder {
ctetb.injection.SQL(ctetb.marker, sql)
return ctetb
}
// TableName returns the CTE table name.
func (ctetb *CTEQueryBuilder) TableName() string {
return ctetb.name
}