1package exql 2 3import ( 4 "strings" 5) 6 7type innerJoinT struct { 8 Type string 9 Table string 10 On string 11 Using string 12} 13 14// Joins represents the union of different join conditions. 15type Joins struct { 16 Conditions []Fragment 17 hash hash 18} 19 20var _ = Fragment(&Joins{}) 21 22// Hash returns a unique identifier for the struct. 23func (j *Joins) Hash() string { 24 return j.hash.Hash(j) 25} 26 27// Compile transforms the Where into an equivalent SQL representation. 28func (j *Joins) Compile(layout *Template) (compiled string, err error) { 29 if c, ok := layout.Read(j); ok { 30 return c, nil 31 } 32 33 l := len(j.Conditions) 34 35 chunks := make([]string, 0, l) 36 37 if l > 0 { 38 for i := 0; i < l; i++ { 39 chunk, err := j.Conditions[i].Compile(layout) 40 if err != nil { 41 return "", err 42 } 43 chunks = append(chunks, chunk) 44 } 45 } 46 47 compiled = strings.Join(chunks, " ") 48 49 layout.Write(j, compiled) 50 51 return 52} 53 54// JoinConditions creates a Joins object. 55func JoinConditions(joins ...*Join) *Joins { 56 fragments := make([]Fragment, len(joins)) 57 for i := range fragments { 58 fragments[i] = joins[i] 59 } 60 return &Joins{Conditions: fragments} 61} 62 63// Join represents a generic JOIN statement. 64type Join struct { 65 Type string 66 Table Fragment 67 On Fragment 68 Using Fragment 69 hash hash 70} 71 72var _ = Fragment(&Join{}) 73 74// Hash returns a unique identifier for the struct. 75func (j *Join) Hash() string { 76 return j.hash.Hash(j) 77} 78 79// Compile transforms the Join into its equivalent SQL representation. 80func (j *Join) Compile(layout *Template) (compiled string, err error) { 81 if c, ok := layout.Read(j); ok { 82 return c, nil 83 } 84 85 if j.Table == nil { 86 return "", nil 87 } 88 89 table, err := j.Table.Compile(layout) 90 if err != nil { 91 return "", err 92 } 93 94 on, err := layout.doCompile(j.On) 95 if err != nil { 96 return "", err 97 } 98 99 using, err := layout.doCompile(j.Using) 100 if err != nil { 101 return "", err 102 } 103 104 data := innerJoinT{ 105 Type: j.Type, 106 Table: table, 107 On: on, 108 Using: using, 109 } 110 111 compiled = layout.MustCompile(layout.JoinLayout, data) 112 layout.Write(j, compiled) 113 return 114} 115 116// On represents JOIN conditions. 117type On Where 118 119var _ = Fragment(&On{}) 120 121// Hash returns a unique identifier. 122func (o *On) Hash() string { 123 return o.hash.Hash(o) 124} 125 126// Compile transforms the On into an equivalent SQL representation. 127func (o *On) Compile(layout *Template) (compiled string, err error) { 128 if c, ok := layout.Read(o); ok { 129 return c, nil 130 } 131 132 grouped, err := groupCondition(layout, o.Conditions, layout.MustCompile(layout.ClauseOperator, layout.AndKeyword)) 133 if err != nil { 134 return "", err 135 } 136 137 if grouped != "" { 138 compiled = layout.MustCompile(layout.OnLayout, conds{grouped}) 139 } 140 141 layout.Write(o, compiled) 142 return 143} 144 145// Using represents a USING function. 146type Using Columns 147 148var _ = Fragment(&Using{}) 149 150type usingT struct { 151 Columns string 152} 153 154// Hash returns a unique identifier. 155func (u *Using) Hash() string { 156 return u.hash.Hash(u) 157} 158 159// Compile transforms the Using into an equivalent SQL representation. 160func (u *Using) Compile(layout *Template) (compiled string, err error) { 161 if u == nil { 162 return "", nil 163 } 164 165 if c, ok := layout.Read(u); ok { 166 return c, nil 167 } 168 169 if len(u.Columns) > 0 { 170 c := Columns(*u) 171 columns, err := c.Compile(layout) 172 if err != nil { 173 return "", err 174 } 175 data := usingT{Columns: columns} 176 compiled = layout.MustCompile(layout.UsingLayout, data) 177 } 178 179 layout.Write(u, compiled) 180 return 181} 182