mydb/internal/sqladapter/exql/join.go

196 lines
3.7 KiB
Go
Raw Normal View History

2023-09-18 15:15:42 +08:00
package exql
import (
"strings"
"git.hexq.cn/tiglog/mydb/internal/cache"
)
type innerJoinT struct {
Type string
Table string
On string
Using string
}
// Joins represents the union of different join conditions.
type Joins struct {
Conditions []Fragment
}
var _ = Fragment(&Joins{})
// Hash returns a unique identifier for the struct.
func (j *Joins) Hash() uint64 {
if j == nil {
return cache.NewHash(FragmentType_Joins, nil)
}
h := cache.InitHash(FragmentType_Joins)
for i := range j.Conditions {
h = cache.AddToHash(h, j.Conditions[i])
}
return h
}
// Compile transforms the Where into an equivalent SQL representation.
func (j *Joins) Compile(layout *Template) (compiled string, err error) {
if c, ok := layout.Read(j); ok {
return c, nil
}
l := len(j.Conditions)
chunks := make([]string, 0, l)
if l > 0 {
for i := 0; i < l; i++ {
chunk, err := j.Conditions[i].Compile(layout)
if err != nil {
return "", err
}
chunks = append(chunks, chunk)
}
}
compiled = strings.Join(chunks, " ")
layout.Write(j, compiled)
return
}
// JoinConditions creates a Joins object.
func JoinConditions(joins ...*Join) *Joins {
fragments := make([]Fragment, len(joins))
for i := range fragments {
fragments[i] = joins[i]
}
return &Joins{Conditions: fragments}
}
// Join represents a generic JOIN statement.
type Join struct {
Type string
Table Fragment
On Fragment
Using Fragment
}
var _ = Fragment(&Join{})
// Hash returns a unique identifier for the struct.
func (j *Join) Hash() uint64 {
if j == nil {
return cache.NewHash(FragmentType_Join, nil)
}
return cache.NewHash(FragmentType_Join, j.Type, j.Table, j.On, j.Using)
}
// Compile transforms the Join into its equivalent SQL representation.
func (j *Join) Compile(layout *Template) (compiled string, err error) {
if c, ok := layout.Read(j); ok {
return c, nil
}
if j.Table == nil {
return "", nil
}
table, err := j.Table.Compile(layout)
if err != nil {
return "", err
}
on, err := layout.doCompile(j.On)
if err != nil {
return "", err
}
using, err := layout.doCompile(j.Using)
if err != nil {
return "", err
}
data := innerJoinT{
Type: j.Type,
Table: table,
On: on,
Using: using,
}
compiled = layout.MustCompile(layout.JoinLayout, data)
layout.Write(j, compiled)
return
}
// On represents JOIN conditions.
type On Where
var _ = Fragment(&On{})
func (o *On) Hash() uint64 {
if o == nil {
return cache.NewHash(FragmentType_On, nil)
}
return cache.NewHash(FragmentType_On, (*Where)(o))
}
// Compile transforms the On into an equivalent SQL representation.
func (o *On) Compile(layout *Template) (compiled string, err error) {
if c, ok := layout.Read(o); ok {
return c, nil
}
grouped, err := groupCondition(layout, o.Conditions, layout.MustCompile(layout.ClauseOperator, layout.AndKeyword))
if err != nil {
return "", err
}
if grouped != "" {
compiled = layout.MustCompile(layout.OnLayout, conds{grouped})
}
layout.Write(o, compiled)
return
}
// Using represents a USING function.
type Using Columns
var _ = Fragment(&Using{})
type usingT struct {
Columns string
}
func (u *Using) Hash() uint64 {
if u == nil {
return cache.NewHash(FragmentType_Using, nil)
}
return cache.NewHash(FragmentType_Using, (*Columns)(u))
}
// Compile transforms the Using into an equivalent SQL representation.
func (u *Using) Compile(layout *Template) (compiled string, err error) {
if u == nil {
return "", nil
}
if c, ok := layout.Read(u); ok {
return c, nil
}
if len(u.Columns) > 0 {
c := Columns(*u)
columns, err := c.Compile(layout)
if err != nil {
return "", err
}
data := usingT{Columns: columns}
compiled = layout.MustCompile(layout.UsingLayout, data)
}
layout.Write(u, compiled)
return
}