@@ -61,9 +61,10 @@ type Referencer struct {
61
61
// Default QuoteNoop.
62
62
IdentifierQuoter func (tableAndColumn ... string ) string
63
63
64
- refs map [interface {}]Quoted
65
- columnNames map [interface {}]string
66
- structColumns map [interface {}][]string
64
+ refs map [interface {}]Quoted
65
+ quotedCols map [interface {}]Quoted
66
+ columnNames map [interface {}]string
67
+ structRefs map [interface {}][]string
67
68
}
68
69
69
70
// ColumnsOf makes a Mapper option to prefix columns with table alias.
@@ -93,6 +94,36 @@ func (r *Referencer) ColumnsOf(rowStructPtr interface{}) func(o *Options) {
93
94
}
94
95
}
95
96
97
+ // QuotedNoTable is a container of field pointer that should be referenced without table.
98
+ type QuotedNoTable struct {
99
+ ptr interface {}
100
+ }
101
+
102
+ // NoTable enables references without table prefix.
103
+ // So that `my_table`.`my_column` would be rendered as `my_column`.
104
+ //
105
+ // r.Ref(sqluct.NoTable(&row.MyColumn))
106
+ // r.Fmt("%s = 1", sqluct.NoTable(&row.MyColumn))
107
+ //
108
+ // Such references may be useful for INSERT/UPDATE column expressions.
109
+ func NoTable (ptr interface {}) QuotedNoTable {
110
+ return QuotedNoTable {ptr : ptr }
111
+ }
112
+
113
+ // NoTableAll enables references without table prefix for all field pointers.
114
+ // It can be useful to prepare multiple variadic arguments.
115
+ //
116
+ // r.Fmt("ON CONFLICT(%s) DO UPDATE SET %s = excluded.%s, %s = excluded.%s",
117
+ // sqluct.NoTableAll(&row.ID, &row.F1, &row.F1, &row.F2, &row.F3)...)
118
+ func NoTableAll (ptrs ... interface {}) []interface {} {
119
+ res := make ([]interface {}, 0 , len (ptrs ))
120
+ for _ , ptr := range ptrs {
121
+ res = append (res , NoTable (ptr ))
122
+ }
123
+
124
+ return res
125
+ }
126
+
96
127
// AddTableAlias creates string references for row pointer and all suitable field pointers in it.
97
128
//
98
129
// Empty alias is not added to column reference.
@@ -106,37 +137,42 @@ func (r *Referencer) AddTableAlias(rowStructPtr interface{}, alias string) {
106
137
r .refs = make (map [interface {}]Quoted , len (f )+ 1 )
107
138
}
108
139
140
+ if r .quotedCols == nil {
141
+ r .quotedCols = make (map [interface {}]Quoted , len (f )+ 1 )
142
+ }
143
+
109
144
if r .columnNames == nil {
110
145
r .columnNames = make (map [interface {}]string , len (f ))
111
146
}
112
147
113
- if r .structColumns == nil {
114
- r .structColumns = make (map [interface {}][]string )
148
+ if r .structRefs == nil {
149
+ r .structRefs = make (map [interface {}][]string )
115
150
}
116
151
117
152
if alias != "" {
118
153
r .refs [rowStructPtr ] = r .Q (alias )
119
154
}
120
155
121
- columns := make ([]string , 0 , len (f ))
156
+ refs := make ([]string , 0 , len (f ))
122
157
123
158
for ptr , fieldName := range f {
124
- var col string
159
+ var ref Quoted
125
160
126
161
if alias == "" {
127
- col = string ( r .Q (fieldName ) )
162
+ ref = r .Q (fieldName )
128
163
} else {
129
- col = string ( r .Q (alias , fieldName ) )
164
+ ref = r .Q (alias , fieldName )
130
165
}
131
166
132
- columns = append (columns , col )
133
- r .refs [ptr ] = Quoted (col )
167
+ refs = append (refs , string (ref ))
168
+ r .refs [ptr ] = ref
169
+ r .quotedCols [ptr ] = r .Q (fieldName )
134
170
r .columnNames [ptr ] = fieldName
135
171
}
136
172
137
- sort .Strings (columns )
173
+ sort .Strings (refs )
138
174
139
- r .structColumns [rowStructPtr ] = columns
175
+ r .structRefs [rowStructPtr ] = refs
140
176
}
141
177
142
178
// Quoted is a string that can be interpolated into an SQL statement as is.
@@ -155,11 +191,49 @@ func (r *Referencer) Q(tableAndColumn ...string) Quoted {
155
191
//
156
192
// It panics if pointer is unknown.
157
193
func (r * Referencer ) Ref (ptr interface {}) string {
158
- if ref , found := r .refs [ptr ]; found {
159
- return string (ref )
194
+ s , err := r .ref (ptr )
195
+ if err != nil {
196
+ panic (err )
160
197
}
161
198
162
- panic (errUnknownFieldOrRow )
199
+ return s
200
+ }
201
+
202
+ func (r * Referencer ) ref (ptr interface {}) (string , error ) {
203
+ if q , ok := ptr .(Quoted ); ok {
204
+ return string (q ), nil
205
+ }
206
+
207
+ refs := r .refs
208
+
209
+ if nt , ok := ptr .(QuotedNoTable ); ok {
210
+ ptr = nt .ptr
211
+ refs = r .quotedCols
212
+ }
213
+
214
+ if ref , found := refs [ptr ]; found {
215
+ return string (ref ), nil
216
+ }
217
+
218
+ return "" , errUnknownFieldOrRow
219
+ }
220
+
221
+ // Refs returns reference strings for multiple field pointers.
222
+ //
223
+ // It panics if pointer is unknown.
224
+ func (r * Referencer ) Refs (ptrs ... interface {}) []string {
225
+ args := make ([]string , 0 , len (ptrs ))
226
+
227
+ for i , fieldPtr := range ptrs {
228
+ ref , err := r .ref (fieldPtr )
229
+ if err != nil {
230
+ panic (fmt .Errorf ("%w at position %d" , err , i ))
231
+ }
232
+
233
+ args = append (args , ref )
234
+ }
235
+
236
+ return args
163
237
}
164
238
165
239
// Col returns unescaped column name for field pointer that was previously added with AddTableAlias.
@@ -181,25 +255,20 @@ func (r *Referencer) Fmt(format string, ptrs ...interface{}) string {
181
255
args := make ([]interface {}, 0 , len (ptrs ))
182
256
183
257
for i , fieldPtr := range ptrs {
184
- if q , ok := fieldPtr .(Quoted ); ok {
185
- args = append (args , string (q ))
186
-
187
- continue
258
+ ref , err := r .ref (fieldPtr )
259
+ if err != nil {
260
+ panic (fmt .Errorf ("%w at position %d" , err , i ))
188
261
}
189
262
190
- if ref , found := r .refs [fieldPtr ]; found {
191
- args = append (args , ref )
192
- } else {
193
- panic (fmt .Errorf ("%w at position %d" , errUnknownFieldOrRow , i ))
194
- }
263
+ args = append (args , ref )
195
264
}
196
265
197
266
return fmt .Sprintf (format , args ... )
198
267
}
199
268
200
269
// Cols returns column references of a row structure.
201
270
func (r * Referencer ) Cols (ptr interface {}) []string {
202
- if cols , found := r .structColumns [ptr ]; found {
271
+ if cols , found := r .structRefs [ptr ]; found {
203
272
return cols
204
273
}
205
274
0 commit comments