@an5/adapters 0.2.5 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.md +189 -305
  2. package/dist/an5Adapter.d.ts +85 -7
  3. package/dist/an5Adapter.js +736 -64
  4. package/dist/base/metadata.d.ts +10 -0
  5. package/dist/base/metadata.js +107 -0
  6. package/dist/base/sql.d.ts +19 -1
  7. package/dist/base/sql.js +152 -24
  8. package/dist/base/types.d.ts +4 -1
  9. package/dist/browser.d.ts +4 -1
  10. package/dist/browser.js +9 -2
  11. package/dist/googlesheets/adapter.d.ts +1 -0
  12. package/dist/googlesheets/adapter.js +9 -2
  13. package/dist/googlesheets/config.d.ts +4 -4
  14. package/dist/googlesheets/helpers.js +2 -0
  15. package/dist/googlesheets/parseConnectionString.js +5 -2
  16. package/dist/googlesheets/sqlExecutor.js +29 -24
  17. package/dist/googlesheets/tableClient.js +14 -5
  18. package/dist/index.d.ts +3 -1
  19. package/dist/index.js +7 -1
  20. package/dist/mssql/engine.js +8 -4
  21. package/dist/mysql/engine.js +1 -1
  22. package/dist/postgres/engine.js +1 -1
  23. package/dist/sqlite/browserEngine.d.ts +27 -0
  24. package/dist/sqlite/browserEngine.js +161 -0
  25. package/dist/sqlite/engine.js +15 -3
  26. package/dist/sqlite/index.d.ts +1 -0
  27. package/dist/sqlite/index.js +1 -0
  28. package/dist/typed.d.ts +5 -0
  29. package/dist/typed.js +2 -0
  30. package/dotnet/An5Adapter.cs +1089 -275
  31. package/dotnet/Base/Core.cs +340 -82
  32. package/dotnet/Mssql/MssqlEngine.cs +145 -144
  33. package/dotnet/Postgres/PostgresEngine.cs +121 -121
  34. package/golang/an5_adapter.go +314 -0
  35. package/golang/base/base.go +100 -0
  36. package/golang/base/metadata.go +116 -0
  37. package/golang/base/where.go +311 -0
  38. package/golang/go.mod +3 -0
  39. package/golang/mssql/mssql.go +32 -0
  40. package/golang/postgres/postgres.go +37 -0
  41. package/golang/sqlite/sqlite.go +14 -0
  42. package/golang/sqlite_test.go +42 -0
  43. package/golang/table_client.go +1329 -0
  44. package/package.json +17 -7
  45. package/python/an5_adapter.py +119 -97
  46. package/python/base/__init__.py +3 -3
  47. package/python/base/dialects.py +11 -8
  48. package/python/base/metadata.py +35 -10
  49. package/python/base/sql.py +172 -121
  50. package/python/mssql/__init__.py +1 -1
  51. package/python/mssql/provider.py +62 -62
  52. package/python/postgres/__init__.py +1 -1
  53. package/python/postgres/provider.py +15 -15
  54. package/python/table_client.py +619 -216
  55. package/typescript/src/an5Adapter.ts +1220 -505
  56. package/typescript/src/base/index.ts +3 -3
  57. package/typescript/src/base/metadata.ts +148 -28
  58. package/typescript/src/base/sql.ts +264 -98
  59. package/typescript/src/base/types.ts +27 -24
  60. package/typescript/src/browser.ts +10 -6
  61. package/typescript/src/config.ts +95 -95
  62. package/typescript/src/googlesheets/adapter.ts +269 -259
  63. package/typescript/src/googlesheets/an5SheetsAdapter.ts +3 -3
  64. package/typescript/src/googlesheets/config.ts +47 -47
  65. package/typescript/src/googlesheets/helpers.ts +144 -143
  66. package/typescript/src/googlesheets/index.ts +3 -3
  67. package/typescript/src/googlesheets/parseConnectionString.ts +48 -46
  68. package/typescript/src/googlesheets/retry.ts +126 -126
  69. package/typescript/src/googlesheets/sqlExecutor.ts +304 -301
  70. package/typescript/src/googlesheets/tableClient.ts +461 -455
  71. package/typescript/src/googlesheets/types.ts +6 -6
  72. package/typescript/src/index.ts +31 -27
  73. package/typescript/src/mssql/engine.ts +134 -132
  74. package/typescript/src/mssql/index.ts +1 -1
  75. package/typescript/src/mysql/engine.ts +80 -80
  76. package/typescript/src/mysql/index.ts +1 -1
  77. package/typescript/src/postgres/engine.ts +87 -87
  78. package/typescript/src/postgres/index.ts +1 -1
  79. package/typescript/src/sqlite/browserEngine.ts +192 -0
  80. package/typescript/src/sqlite/engine.ts +66 -55
  81. package/typescript/src/sqlite/index.ts +3 -1
  82. package/typescript/src/typed.ts +52 -0
@@ -0,0 +1,1329 @@
1
+ // Package an5adapters provides TableClient for dynamic map-based table queries.
2
+ package an5adapters
3
+
4
+ import (
5
+ "context"
6
+ "crypto/rand"
7
+ "encoding/hex"
8
+ "fmt"
9
+ "sort"
10
+ "strings"
11
+
12
+ "an5adapters/base"
13
+ )
14
+
15
+ // TableClient provides table-scoped CRUD, aggregate, and vector search operations for dynamic map rows.
16
+ type TableClient struct {
17
+ Adapter *An5Adapter
18
+ TableName string
19
+ }
20
+
21
+ // Table returns a TableClient for the specified table name.
22
+ func (a *An5Adapter) Table(tableName string) *TableClient {
23
+ return &TableClient{
24
+ Adapter: a,
25
+ TableName: tableName,
26
+ }
27
+ }
28
+
29
+ // ViewClient provides read-only view operations.
30
+ type ViewClient struct {
31
+ table *TableClient
32
+ }
33
+
34
+ // View returns a ViewClient for the specified view name.
35
+ func (a *An5Adapter) View(viewName string) *ViewClient {
36
+ return &ViewClient{
37
+ table: a.Table(viewName),
38
+ }
39
+ }
40
+
41
+ func (v *ViewClient) FindMany(ctx context.Context, args *FindManyArgs) ([]map[string]interface{}, error) {
42
+ return v.table.FindMany(ctx, args)
43
+ }
44
+
45
+ func (v *ViewClient) FindFirst(ctx context.Context, args *FindManyArgs) (map[string]interface{}, error) {
46
+ return v.table.FindFirst(ctx, args)
47
+ }
48
+
49
+ func (v *ViewClient) FindUnique(ctx context.Context, args *FindManyArgs) (map[string]interface{}, error) {
50
+ return v.table.FindUnique(ctx, args)
51
+ }
52
+
53
+ func (v *ViewClient) Count(ctx context.Context, args *CountArgs) (int64, error) {
54
+ return v.table.Count(ctx, args)
55
+ }
56
+
57
+ func (v *ViewClient) Aggregate(ctx context.Context, args *AggregateArgs) (map[string]interface{}, error) {
58
+ return v.table.Aggregate(ctx, args)
59
+ }
60
+
61
+ func (v *ViewClient) GroupBy(ctx context.Context, args *GroupByArgs) ([]map[string]interface{}, error) {
62
+ return v.table.GroupBy(ctx, args)
63
+ }
64
+
65
+ func (v *ViewClient) VectorSearch(ctx context.Context, args *VectorSearchArgs) ([]map[string]interface{}, error) {
66
+ return v.table.VectorSearch(ctx, args)
67
+ }
68
+
69
+ // ─── Args types (ORM-standard structured args, mirrors TypeScript AdapterTableClient) ──
70
+
71
+ // FindManyArgs configures a FindMany / FindFirst / FindUnique query.
72
+ type FindManyArgs struct {
73
+ Where interface{} // map[string]interface{} ORM-style where object
74
+ OrderBy interface{} // map[string]interface{}, []interface{}, []map[string]interface{} or string
75
+ Skip int
76
+ Take int
77
+ Select interface{} // []string or map[string]bool
78
+ Include interface{} // map[string]interface{} relation keys to eager-load
79
+ }
80
+
81
+ // CountArgs configures a Count query.
82
+ type CountArgs struct {
83
+ Where interface{}
84
+ }
85
+
86
+ // DeleteManyArgs configures a DeleteMany query.
87
+ type DeleteManyArgs struct {
88
+ Where interface{}
89
+ }
90
+
91
+ // CreateArgs configures a Create query.
92
+ type CreateArgs struct {
93
+ Data map[string]interface{}
94
+ Include interface{}
95
+ Select interface{}
96
+ }
97
+
98
+ // CreateManyArgs configures a CreateMany query.
99
+ type CreateManyArgs struct {
100
+ Data []map[string]interface{}
101
+ SkipDuplicates bool
102
+ }
103
+
104
+ // UpdateArgs configures an Update query.
105
+ type UpdateArgs struct {
106
+ Where interface{}
107
+ Data map[string]interface{} // supports {increment,decrement,multiply,divide,set} field ops
108
+ Include interface{}
109
+ Select interface{}
110
+ }
111
+
112
+ // UpdateManyArgs configures an UpdateMany query.
113
+ type UpdateManyArgs struct {
114
+ Where interface{}
115
+ Data map[string]interface{}
116
+ }
117
+
118
+ // DeleteArgs configures a Delete query.
119
+ type DeleteArgs struct {
120
+ Where interface{}
121
+ }
122
+
123
+ // UpsertArgs configures an Upsert query.
124
+ type UpsertArgs struct {
125
+ Where interface{}
126
+ Create map[string]interface{}
127
+ Update map[string]interface{}
128
+ }
129
+
130
+ // AggregateArgs configures an Aggregate query.
131
+ type AggregateArgs struct {
132
+ Where interface{}
133
+ Count interface{} // true, or object/list/array of fields to count
134
+ Sum interface{}
135
+ Avg interface{}
136
+ Min interface{}
137
+ Max interface{}
138
+ }
139
+
140
+ // GroupByArgs configures a GroupBy query.
141
+ type GroupByArgs struct {
142
+ By interface{} // string or []string
143
+ Where interface{}
144
+ OrderBy interface{}
145
+ Skip int
146
+ Take int
147
+ Sum interface{}
148
+ Avg interface{}
149
+ Min interface{}
150
+ Max interface{}
151
+ }
152
+
153
+ // VectorSearchArgs configures a VectorSearch query.
154
+ type VectorSearchArgs struct {
155
+ Vector []float64
156
+ Take int
157
+ Where interface{}
158
+ VectorField string
159
+ DistanceMetric string
160
+ }
161
+
162
+ func (t *TableClient) quotedTable() string {
163
+ return base.QuoteTable(t.TableName, t.Adapter.Dialect)
164
+ }
165
+
166
+ func (t *TableClient) nolock() string {
167
+ if t.Adapter.Dialect == base.DialectMssql {
168
+ return " WITH (NOLOCK)"
169
+ }
170
+ return ""
171
+ }
172
+
173
+ func (t *TableClient) placeholders(count int) string {
174
+ phs := make([]string, count)
175
+ offset := 0
176
+ for i := 0; i < count; i++ {
177
+ phs[i] = placeholder(t.Adapter.Dialect, i+offset+1)
178
+ }
179
+ return strings.Join(phs, ", ")
180
+ }
181
+
182
+ // buildWhere converts an ORM-style where object into SQL clause + bound args.
183
+ func (t *TableClient) buildWhere(where interface{}) (string, []interface{}) {
184
+ if where == nil {
185
+ return "", nil
186
+ }
187
+ if m, ok := where.(map[string]interface{}); ok && m != nil {
188
+ clause, args := base.BuildWhere(m, t.Adapter.Dialect, func(n int) string {
189
+ return placeholder(t.Adapter.Dialect, n)
190
+ })
191
+ return clause, args
192
+ }
193
+ return "", nil
194
+ }
195
+
196
+ func (t *TableClient) orderSql(orderBy interface{}) string {
197
+ return base.BuildOrderBy(orderBy, t.Adapter.Dialect)
198
+ }
199
+
200
+ // selectedFields extracts scalar column names from a select object ([]string or map with truthy values).
201
+ func selectedFields(selectVal interface{}) (keys []string, hasRelationSelect bool) {
202
+ switch v := selectVal.(type) {
203
+ case nil:
204
+ return nil, false
205
+ case []string:
206
+ return append([]string{}, v...), false
207
+ case []interface{}:
208
+ for _, item := range v {
209
+ if s, ok := item.(string); ok {
210
+ keys = append(keys, s)
211
+ }
212
+ }
213
+ return keys, false
214
+ case map[string]interface{}:
215
+ for k, val := range v {
216
+ if b, ok := val.(bool); ok && b {
217
+ if k == "_count" || strings.Contains(k, "_") {
218
+ hasRelationSelect = true
219
+ }
220
+ keys = append(keys, k)
221
+ }
222
+ }
223
+ return keys, hasRelationSelect
224
+ case map[string]bool:
225
+ for k, val := range v {
226
+ if val {
227
+ if k == "_count" || strings.Contains(k, "_") {
228
+ hasRelationSelect = true
229
+ }
230
+ keys = append(keys, k)
231
+ }
232
+ }
233
+ return keys, hasRelationSelect
234
+ }
235
+ return nil, false
236
+ }
237
+
238
+ // projectFields keeps only selected fields on a row (mirrors TypeScript projectFields).
239
+ func projectFields(row map[string]interface{}, selectVal interface{}) map[string]interface{} {
240
+ if row == nil || selectVal == nil {
241
+ return row
242
+ }
243
+ projected := map[string]interface{}{}
244
+ truthy := func(k string, v interface{}) bool {
245
+ switch b := v.(type) {
246
+ case bool:
247
+ return b
248
+ case interface{}:
249
+ return b != nil
250
+ }
251
+ return false
252
+ }
253
+ switch sel := selectVal.(type) {
254
+ case []string:
255
+ for _, k := range sel {
256
+ if v, ok := row[k]; ok {
257
+ projected[k] = v
258
+ }
259
+ }
260
+ case []interface{}:
261
+ for _, item := range sel {
262
+ if k, ok := item.(string); ok {
263
+ if v, had := row[k]; had {
264
+ projected[k] = v
265
+ }
266
+ }
267
+ }
268
+ case map[string]interface{}:
269
+ for k, v := range sel {
270
+ if truthy(k, v) {
271
+ projected[k] = row[k]
272
+ }
273
+ }
274
+ case map[string]bool:
275
+ for k, v := range sel {
276
+ if v {
277
+ projected[k] = row[k]
278
+ }
279
+ }
280
+ default:
281
+ return row
282
+ }
283
+ if rcount, ok := row["_count"]; ok {
284
+ projected["_count"] = rcount
285
+ }
286
+ return projected
287
+ }
288
+
289
+ // FindMany fetches rows matching structured ORM args (where/orderBy/skip/take/select/include).
290
+ func (t *TableClient) FindMany(ctx context.Context, args *FindManyArgs) ([]map[string]interface{}, error) {
291
+ whereCond, queryArgs := t.buildWhere(args.Where)
292
+ order := t.orderSql(args.OrderBy)
293
+ take := args.Take
294
+ skip := args.Skip
295
+ hasSkip := skip != 0
296
+
297
+ cols := "*"
298
+ if keys, hasRel := selectedFields(args.Select); len(keys) > 0 && !hasRel {
299
+ quoted := make([]string, len(keys))
300
+ for i, k := range keys {
301
+ quoted[i] = base.QuoteIdentifier(k, t.Adapter.Dialect)
302
+ }
303
+ cols = strings.Join(quoted, ", ")
304
+ }
305
+
306
+ var query string
307
+ switch {
308
+ case take > 0 && !hasSkip:
309
+ if t.Adapter.Dialect == base.DialectPostgres || t.Adapter.Dialect == base.DialectSqlite {
310
+ query = fmt.Sprintf("SELECT %s FROM %s", cols, t.quotedTable())
311
+ if whereCond != "" {
312
+ query += " WHERE " + whereCond
313
+ }
314
+ if order != "" {
315
+ query += " " + order
316
+ }
317
+ query += fmt.Sprintf(" LIMIT %d", take)
318
+ } else {
319
+ query = fmt.Sprintf("SELECT TOP (%d) %s FROM %s%s", take, cols, t.quotedTable(), t.nolock())
320
+ if whereCond != "" {
321
+ query += " WHERE " + whereCond
322
+ }
323
+ if order != "" {
324
+ query += " " + order
325
+ }
326
+ }
327
+ case hasSkip:
328
+ if t.Adapter.Dialect == base.DialectPostgres || t.Adapter.Dialect == base.DialectSqlite {
329
+ query = fmt.Sprintf("SELECT %s FROM %s", cols, t.quotedTable())
330
+ if whereCond != "" {
331
+ query += " WHERE " + whereCond
332
+ }
333
+ if order != "" {
334
+ query += " " + order
335
+ }
336
+ if take > 0 {
337
+ query += fmt.Sprintf(" LIMIT %d", take)
338
+ } else {
339
+ query += " LIMIT ALL"
340
+ }
341
+ query += fmt.Sprintf(" OFFSET %d", skip)
342
+ } else {
343
+ query = fmt.Sprintf("SELECT %s FROM %s%s", cols, t.quotedTable(), t.nolock())
344
+ if whereCond != "" {
345
+ query += " WHERE " + whereCond
346
+ }
347
+ if order != "" {
348
+ query += " " + order
349
+ } else {
350
+ query += " ORDER BY (SELECT NULL)"
351
+ }
352
+ query += fmt.Sprintf(" OFFSET %d ROWS", skip)
353
+ if take > 0 {
354
+ query += fmt.Sprintf(" FETCH NEXT %d ROWS ONLY", take)
355
+ }
356
+ }
357
+ default:
358
+ query = fmt.Sprintf("SELECT %s FROM %s%s", cols, t.quotedTable(), t.nolock())
359
+ if whereCond != "" {
360
+ query += " WHERE " + whereCond
361
+ }
362
+ if order != "" {
363
+ query += " " + order
364
+ }
365
+ }
366
+
367
+ rows, err := t.Adapter.QueryRaw(ctx, query, queryArgs...)
368
+ if err != nil {
369
+ return nil, err
370
+ }
371
+
372
+ if args.Include != nil {
373
+ if err := resolveIncludes(ctx, t.TableName, rows, args.Include, t.Adapter); err != nil {
374
+ return nil, err
375
+ }
376
+ }
377
+ if args.Select != nil {
378
+ if _, hasRel := selectedFields(args.Select); hasRel {
379
+ if err := resolveIncludes(ctx, t.TableName, rows, args.Select, t.Adapter); err != nil {
380
+ return nil, err
381
+ }
382
+ }
383
+ for i := range rows {
384
+ rows[i] = projectFields(rows[i], args.Select)
385
+ }
386
+ }
387
+ return rows, nil
388
+ }
389
+
390
+ // FindFirst fetches the first row matching structured ORM args.
391
+ func (t *TableClient) FindFirst(ctx context.Context, args *FindManyArgs) (map[string]interface{}, error) {
392
+ if args == nil {
393
+ args = &FindManyArgs{}
394
+ }
395
+ copied := *args
396
+ copied.Take = 1
397
+ rows, err := t.FindMany(ctx, &copied)
398
+ if err != nil || len(rows) == 0 {
399
+ return nil, err
400
+ }
401
+ return rows[0], nil
402
+ }
403
+
404
+ // FindUnique fetches a single row by its unique where criteria.
405
+ func (t *TableClient) FindUnique(ctx context.Context, args *FindManyArgs) (map[string]interface{}, error) {
406
+ return t.FindFirst(ctx, args)
407
+ }
408
+
409
+ // Count returns total row count matching structured where args.
410
+ func (t *TableClient) Count(ctx context.Context, args *CountArgs) (int64, error) {
411
+ var whereCond string
412
+ var queryArgs []interface{}
413
+ if args != nil {
414
+ whereCond, queryArgs = t.buildWhere(args.Where)
415
+ }
416
+ query := fmt.Sprintf("SELECT COUNT(*) AS cnt FROM %s%s", t.quotedTable(), t.nolock())
417
+ if whereCond != "" {
418
+ query += " WHERE " + whereCond
419
+ }
420
+ rows, err := t.Adapter.QueryRaw(ctx, query, queryArgs...)
421
+ if err != nil || len(rows) == 0 {
422
+ return 0, err
423
+ }
424
+ if cnt, ok := rows[0]["cnt"].(int64); ok {
425
+ return cnt, nil
426
+ }
427
+ if f, ok := rows[0]["cnt"].(float64); ok {
428
+ return int64(f), nil
429
+ }
430
+ return 0, nil
431
+ }
432
+
433
+ // generateUUID returns a random UUID v4 string.
434
+ func generateUUID() string {
435
+ b := make([]byte, 16)
436
+ _, _ = rand.Read(b)
437
+ b[6] = (b[6] & 0x0f) | 0x40
438
+ b[8] = (b[8] & 0x3f) | 0x80
439
+ h := hex.EncodeToString(b)
440
+ return h[0:8] + "-" + h[8:12] + "-" + h[12:16] + "-" + h[16:20] + "-" + h[20:32]
441
+ }
442
+
443
+ // splitRelationWrites separates relation-write objects from scalar data using model metadata.
444
+ func splitRelationWrites(modelName string, data map[string]interface{}) (scalars map[string]interface{}, rels map[string]interface{}) {
445
+ scalars = map[string]interface{}{}
446
+ rels = map[string]interface{}{}
447
+ relations := base.GetRelationsForModel(modelName)
448
+ for k, v := range data {
449
+ if _, isRel := relations[k]; isRel {
450
+ rels[k] = v
451
+ } else {
452
+ scalars[k] = v
453
+ }
454
+ }
455
+ return scalars, rels
456
+ }
457
+
458
+ // Create inserts one map row and returns the created record.
459
+ func (t *TableClient) Create(ctx context.Context, args *CreateArgs) (map[string]interface{}, error) {
460
+ if args == nil || len(args.Data) == 0 {
461
+ return nil, fmt.Errorf("create requires at least one field")
462
+ }
463
+
464
+ fields, _ := base.GetFieldsForModel(t.TableName).(map[string]interface{})
465
+ idFieldName := ""
466
+ if fields != nil {
467
+ if _, has := fields["id"]; has {
468
+ idFieldName = "id"
469
+ } else {
470
+ for name := range fields {
471
+ if strings.HasSuffix(name, "_id") || strings.HasSuffix(name, "Id") || strings.ToLower(name) == "id" {
472
+ idFieldName = name
473
+ break
474
+ }
475
+ }
476
+ }
477
+ }
478
+ if fdef, has := fields[idFieldName]; has && fields != nil {
479
+ rawType := ""
480
+ switch ft := fdef.(type) {
481
+ case string:
482
+ rawType = ft
483
+ case map[string]interface{}:
484
+ for _, key := range []string{"ts", "sql", "type"} {
485
+ if s, ok := ft[key].(string); ok {
486
+ rawType = s
487
+ break
488
+ }
489
+ }
490
+ }
491
+ normalized := strings.ToLower(rawType)
492
+ isStringType := normalized == "" || strings.Contains(normalized, "string") || strings.Contains(normalized, "uuid") || strings.Contains(normalized, "uniqueidentifier") || strings.Contains(normalized, "nvarchar") || strings.Contains(normalized, "varchar") || strings.Contains(normalized, "text")
493
+ if idFieldName != "" && isStringType {
494
+ if _, present := args.Data[idFieldName]; !present || args.Data[idFieldName] == nil {
495
+ args.Data[idFieldName] = generateUUID()
496
+ }
497
+ }
498
+ }
499
+
500
+ data, relationWrites := splitRelationWrites(t.TableName, args.Data)
501
+
502
+ var cols []string
503
+ var vals []string
504
+ var values []interface{}
505
+ for col, val := range data {
506
+ cols = append(cols, base.QuoteIdentifier(col, t.Adapter.Dialect))
507
+ vals = append(vals, placeholder(t.Adapter.Dialect, len(values)+1))
508
+ values = append(values, val)
509
+ }
510
+
511
+ if len(cols) > 0 {
512
+ query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)",
513
+ t.quotedTable(), strings.Join(cols, ", "), strings.Join(vals, ", "))
514
+ if _, err := t.Adapter.ExecuteRaw(ctx, query, values...); err != nil {
515
+ return nil, err
516
+ }
517
+ }
518
+
519
+ where := map[string]interface{}{}
520
+ if idFieldName != "" && args.Data[idFieldName] != nil {
521
+ where[idFieldName] = args.Data[idFieldName]
522
+ } else {
523
+ where = data
524
+ }
525
+ created, err := t.FindFirst(ctx, &FindManyArgs{Where: where})
526
+ if err != nil || created == nil {
527
+ return data, err
528
+ }
529
+
530
+ for relKey, relWrite := range relationWrites {
531
+ relations := base.GetRelationsForModel(t.TableName)
532
+ relation, ok := relations[relKey]
533
+ if !ok {
534
+ continue
535
+ }
536
+ rw, ok := relWrite.(map[string]interface{})
537
+ if !ok {
538
+ continue
539
+ }
540
+ if createVal, has := rw["create"]; has {
541
+ childClient := t.Adapter.Table(relation.ModelName)
542
+ items := normalizeItemList(createVal)
543
+ for _, item := range items {
544
+ if m, ok := item.(map[string]interface{}); ok && m != nil {
545
+ merged := map[string]interface{}{}
546
+ for k, v := range m {
547
+ merged[k] = v
548
+ }
549
+ if local, ok := created[relation.LocalKey]; ok {
550
+ merged[relation.ForeignKey] = local
551
+ }
552
+ if _, err := childClient.Create(ctx, &CreateArgs{Data: merged}); err != nil {
553
+ return nil, err
554
+ }
555
+ }
556
+ }
557
+ }
558
+ }
559
+
560
+ if args.Include != nil {
561
+ if err := resolveIncludes(ctx, t.TableName, []map[string]interface{}{created}, args.Include, t.Adapter); err != nil {
562
+ return nil, err
563
+ }
564
+ }
565
+ if args.Select != nil {
566
+ if err := resolveIncludes(ctx, t.TableName, []map[string]interface{}{created}, args.Select, t.Adapter); err != nil {
567
+ return nil, err
568
+ }
569
+ return projectFields(created, args.Select), nil
570
+ }
571
+ return created, nil
572
+ }
573
+
574
+ // normalizeItemList accepts a single map or []interface{} and returns a flat []interface{}.
575
+ func normalizeItemList(value interface{}) []interface{} {
576
+ switch v := value.(type) {
577
+ case []interface{}:
578
+ return v
579
+ case []map[string]interface{}:
580
+ out := make([]interface{}, len(v))
581
+ for i, m := range v {
582
+ out[i] = m
583
+ }
584
+ return out
585
+ case map[string]interface{}:
586
+ return []interface{}{v}
587
+ }
588
+ return nil
589
+ }
590
+
591
+ // CreateMany inserts rows one by one and optionally skips duplicate errors.
592
+ func (t *TableClient) CreateMany(ctx context.Context, args *CreateManyArgs) (map[string]interface{}, error) {
593
+ if args == nil {
594
+ args = &CreateManyArgs{}
595
+ }
596
+ var count int64
597
+ for _, row := range args.Data {
598
+ _, err := t.Create(ctx, &CreateArgs{Data: row})
599
+ if err != nil {
600
+ if args.SkipDuplicates {
601
+ continue
602
+ }
603
+ return map[string]interface{}{"count": count}, err
604
+ }
605
+ count++
606
+ }
607
+ return map[string]interface{}{"count": count}, nil
608
+ }
609
+
610
+ // appendUpdateSet handles set expressions including {increment,decrement,multiply,divide,set} field ops.
611
+ func appendUpdateSet(setParts *[]string, values *[]interface{}, col string, val interface{}, dialect base.Dialect) {
612
+ quoted := base.QuoteIdentifier(col, dialect)
613
+ if m, ok := val.(map[string]interface{}); ok {
614
+ for _, op := range []string{"increment", "decrement", "multiply", "divide", "set"} {
615
+ if opval, has := m[op]; has {
616
+ *values = append(*values, opval)
617
+ p := placeholder(dialect, len(*values))
618
+ switch op {
619
+ case "increment":
620
+ *setParts = append(*setParts, fmt.Sprintf("%s = %s + %s", quoted, quoted, p))
621
+ case "decrement":
622
+ *setParts = append(*setParts, fmt.Sprintf("%s = %s - %s", quoted, quoted, p))
623
+ case "multiply":
624
+ *setParts = append(*setParts, fmt.Sprintf("%s = %s * %s", quoted, quoted, p))
625
+ case "divide":
626
+ *setParts = append(*setParts, fmt.Sprintf("%s = %s / %s", quoted, quoted, p))
627
+ case "set":
628
+ *setParts = append(*setParts, fmt.Sprintf("%s = %s", quoted, p))
629
+ }
630
+ return
631
+ }
632
+ }
633
+ }
634
+ *values = append(*values, val)
635
+ *setParts = append(*setParts, fmt.Sprintf("%s = %s", quoted, placeholder(dialect, len(*values))))
636
+ }
637
+
638
+ // Update updates rows matching where criteria and returns the first updated record.
639
+ func (t *TableClient) Update(ctx context.Context, args *UpdateArgs) (map[string]interface{}, error) {
640
+ if args == nil {
641
+ return nil, fmt.Errorf("update requires args")
642
+ }
643
+ data, relationWrites := splitRelationWrites(t.TableName, args.Data)
644
+
645
+ whereCond, whereArgs := t.buildWhere(args.Where)
646
+ setParts := []string{}
647
+ setValues := []interface{}{}
648
+ for col, val := range data {
649
+ appendUpdateSet(&setParts, &setValues, col, val, t.Adapter.Dialect)
650
+ }
651
+
652
+ if len(setParts) > 0 {
653
+ query := fmt.Sprintf("UPDATE %s SET %s", t.quotedTable(), strings.Join(setParts, ", "))
654
+ if whereCond != "" {
655
+ query += " WHERE " + whereCond
656
+ }
657
+ allValues := append(setValues, whereArgs...)
658
+ if _, err := t.Adapter.ExecuteRaw(ctx, query, allValues...); err != nil {
659
+ return nil, err
660
+ }
661
+ }
662
+
663
+ updated, err := t.FindFirst(ctx, &FindManyArgs{Where: args.Where})
664
+ if err != nil || updated == nil {
665
+ return nil, err
666
+ }
667
+
668
+ for relKey, relWrite := range relationWrites {
669
+ relations := base.GetRelationsForModel(t.TableName)
670
+ relation, ok := relations[relKey]
671
+ if !ok {
672
+ continue
673
+ }
674
+ rw, ok := relWrite.(map[string]interface{})
675
+ if !ok {
676
+ continue
677
+ }
678
+ childClient := t.Adapter.Table(relation.ModelName)
679
+ if createVal, has := rw["create"]; has {
680
+ for _, item := range normalizeItemList(createVal) {
681
+ if m, ok := item.(map[string]interface{}); ok && m != nil {
682
+ merged := map[string]interface{}{}
683
+ for k, v := range m {
684
+ merged[k] = v
685
+ }
686
+ if local, ok := updated[relation.LocalKey]; ok {
687
+ merged[relation.ForeignKey] = local
688
+ }
689
+ if _, err := childClient.Create(ctx, &CreateArgs{Data: merged}); err != nil {
690
+ return nil, err
691
+ }
692
+ }
693
+ }
694
+ }
695
+ if updateVal, has := rw["update"]; has {
696
+ if m, ok := updateVal.(map[string]interface{}); ok && m != nil {
697
+ if _, err := childClient.Update(ctx, &UpdateArgs{Where: m["where"], Data: m["data"].(map[string]interface{})}); err != nil {
698
+ return nil, err
699
+ }
700
+ }
701
+ }
702
+ if disconnectVal, has := rw["disconnect"]; has {
703
+ if m, ok := disconnectVal.(map[string]interface{}); ok && m != nil {
704
+ if _, err := childClient.UpdateMany(ctx, &UpdateManyArgs{Where: m, Data: map[string]interface{}{relation.ForeignKey: nil}}); err != nil {
705
+ return nil, err
706
+ }
707
+ }
708
+ }
709
+ }
710
+
711
+ if args.Include != nil {
712
+ if err := resolveIncludes(ctx, t.TableName, []map[string]interface{}{updated}, args.Include, t.Adapter); err != nil {
713
+ return nil, err
714
+ }
715
+ }
716
+ if args.Select != nil {
717
+ if err := resolveIncludes(ctx, t.TableName, []map[string]interface{}{updated}, args.Select, t.Adapter); err != nil {
718
+ return nil, err
719
+ }
720
+ return projectFields(updated, args.Select), nil
721
+ }
722
+ return updated, nil
723
+ }
724
+
725
+ // UpdateMany updates rows matching where criteria and returns affected count.
726
+ func (t *TableClient) UpdateMany(ctx context.Context, args *UpdateManyArgs) (map[string]interface{}, error) {
727
+ if args == nil || len(args.Data) == 0 {
728
+ return map[string]interface{}{"count": 0}, nil
729
+ }
730
+ whereCond, whereArgs := t.buildWhere(args.Where)
731
+ setParts := []string{}
732
+ setValues := []interface{}{}
733
+ for col, val := range args.Data {
734
+ appendUpdateSet(&setParts, &setValues, col, val, t.Adapter.Dialect)
735
+ }
736
+ if len(setParts) == 0 {
737
+ return map[string]interface{}{"count": 0}, nil
738
+ }
739
+ query := fmt.Sprintf("UPDATE %s SET %s", t.quotedTable(), strings.Join(setParts, ", "))
740
+ if whereCond != "" {
741
+ query += " WHERE " + whereCond
742
+ }
743
+ allValues := append(setValues, whereArgs...)
744
+ affected, err := t.Adapter.ExecuteRaw(ctx, query, allValues...)
745
+ if err != nil {
746
+ return nil, err
747
+ }
748
+ return map[string]interface{}{"count": affected}, nil
749
+ }
750
+
751
+ // DeleteMany deletes rows matching where criteria and returns affected count.
752
+ func (t *TableClient) DeleteMany(ctx context.Context, args *DeleteManyArgs) (map[string]interface{}, error) {
753
+ whereCond, queryArgs := t.buildWhere(args.Where)
754
+ query := fmt.Sprintf("DELETE FROM %s", t.quotedTable())
755
+ if whereCond != "" {
756
+ query += " WHERE " + whereCond
757
+ }
758
+ affected, err := t.Adapter.ExecuteRaw(ctx, query, queryArgs...)
759
+ if err != nil {
760
+ return nil, err
761
+ }
762
+ return map[string]interface{}{"count": affected}, nil
763
+ }
764
+
765
+ // Delete deletes one record matching where criteria and returns the deleted record.
766
+ func (t *TableClient) Delete(ctx context.Context, args *DeleteArgs) (map[string]interface{}, error) {
767
+ if args == nil {
768
+ return nil, fmt.Errorf("delete requires where")
769
+ }
770
+ existing, err := t.FindFirst(ctx, &FindManyArgs{Where: args.Where})
771
+ if err != nil {
772
+ return nil, err
773
+ }
774
+ whereCond, queryArgs := t.buildWhere(args.Where)
775
+ query := fmt.Sprintf("DELETE FROM %s WHERE %s", t.quotedTable(), whereCond)
776
+ affected, err := t.Adapter.ExecuteRaw(ctx, query, queryArgs...)
777
+ if err != nil {
778
+ return nil, err
779
+ }
780
+ boolish := affected > 0
781
+ if !boolish {
782
+ return nil, nil
783
+ }
784
+ return existing, nil
785
+ }
786
+
787
+ // Upsert creates a record if none matches where, otherwise updates it.
788
+ func (t *TableClient) Upsert(ctx context.Context, args *UpsertArgs) (map[string]interface{}, error) {
789
+ if args == nil {
790
+ return nil, fmt.Errorf("upsert requires args")
791
+ }
792
+ existing, err := t.FindFirst(ctx, &FindManyArgs{Where: args.Where})
793
+ if err != nil {
794
+ return nil, err
795
+ }
796
+ if existing != nil {
797
+ return t.Update(ctx, &UpdateArgs{Where: args.Where, Data: args.Update})
798
+ }
799
+ return t.Create(ctx, &CreateArgs{Data: args.Create})
800
+ }
801
+
802
+ // AggregateOptions defines aggregation expressions keyed by output alias.
803
+ // Deprecated: use AggregateArgs with _count/_sum/_avg/_min/_max keys.
804
+ type AggregateOptions map[string]string
805
+
806
+ // Aggregate executes aggregation queries (SUM, AVG, MIN, MAX, COUNT) and returns a single row result.
807
+ func (t *TableClient) Aggregate(ctx context.Context, args *AggregateArgs) (map[string]interface{}, error) {
808
+ if args == nil {
809
+ args = &AggregateArgs{}
810
+ }
811
+ whereCond, queryArgs := t.buildWhere(args.Where)
812
+ var aggs []string
813
+
814
+ if args.Count != nil {
815
+ countFields := selectedAggregateFields(args.Count)
816
+ if len(countFields) == 0 {
817
+ aggs = append(aggs, "COUNT(*) AS cnt_all")
818
+ } else {
819
+ for _, f := range countFields {
820
+ if f != "_all" {
821
+ aggs = append(aggs, fmt.Sprintf("COUNT(%s) AS cnt_%s", base.QuoteIdentifier(f, t.Adapter.Dialect), sanitizeParamName(f)))
822
+ }
823
+ }
824
+ }
825
+ }
826
+ for _, aggTuple := range []struct {
827
+ kind string
828
+ value interface{}
829
+ }{
830
+ {"SUM", args.Sum},
831
+ {"AVG", args.Avg},
832
+ {"MIN", args.Min},
833
+ {"MAX", args.Max},
834
+ } {
835
+ for _, f := range selectedAggregateFields(aggTuple.value) {
836
+ aggs = append(aggs, fmt.Sprintf("%s(%s) AS %s_%s", aggTuple.kind, base.QuoteIdentifier(f, t.Adapter.Dialect), strings.ToLower(aggTuple.kind), sanitizeParamName(f)))
837
+ }
838
+ }
839
+
840
+ if len(aggs) == 0 {
841
+ return nil, fmt.Errorf("aggregate requires at least one aggregator field")
842
+ }
843
+
844
+ query := fmt.Sprintf("SELECT %s FROM %s%s", strings.Join(aggs, ", "), t.quotedTable(), t.nolock())
845
+ if whereCond != "" {
846
+ query += " WHERE " + whereCond
847
+ }
848
+ rows, err := t.Adapter.QueryRaw(ctx, query, queryArgs...)
849
+ if err != nil {
850
+ return nil, err
851
+ }
852
+ if len(rows) == 0 {
853
+ return map[string]interface{}{}, nil
854
+ }
855
+ row := rows[0]
856
+
857
+ result := map[string]interface{}{}
858
+ if args.Count != nil {
859
+ countFields := selectedAggregateFields(args.Count)
860
+ allCount := numberOrZero(row["cnt_all"], row["_count"])
861
+ result["_count"] = map[string]interface{}{"_all": allCount}
862
+ for _, f := range countFields {
863
+ if f == "_all" {
864
+ continue
865
+ }
866
+ key := "cnt_" + sanitizeParamName(f)
867
+ if v, ok := row[key]; ok && v != nil {
868
+ result["_count"].(map[string]interface{})[f] = numberOrZero(v)
869
+ } else {
870
+ result["_count"].(map[string]interface{})[f] = allCount
871
+ }
872
+ }
873
+ }
874
+ for _, aggTuple := range []struct {
875
+ kind string
876
+ value interface{}
877
+ }{
878
+ {"sum", args.Sum},
879
+ {"avg", args.Avg},
880
+ {"min", args.Min},
881
+ {"max", args.Max},
882
+ } {
883
+ fields := selectedAggregateFields(aggTuple.value)
884
+ if len(fields) == 0 {
885
+ continue
886
+ }
887
+ sub := map[string]interface{}{}
888
+ for _, f := range fields {
889
+ key := aggTuple.kind + "_" + sanitizeParamName(f)
890
+ if v, ok := row[key]; ok && v != nil {
891
+ sub[f] = numberOrNil(v)
892
+ } else {
893
+ sub[f] = nil
894
+ }
895
+ }
896
+ result["_"+aggTuple.kind] = sub
897
+ }
898
+ return result, nil
899
+ }
900
+
901
+ // GroupBy executes a GROUP BY query and returns grouped result rows.
902
+ func (t *TableClient) GroupBy(ctx context.Context, args *GroupByArgs) ([]map[string]interface{}, error) {
903
+ if args == nil {
904
+ args = &GroupByArgs{}
905
+ }
906
+ byFields := normalizeByFields(args.By)
907
+ if len(byFields) == 0 {
908
+ return nil, fmt.Errorf("groupBy requires at least one group field")
909
+ }
910
+ whereCond, queryArgs := t.buildWhere(args.Where)
911
+
912
+ quotedGroups := make([]string, len(byFields))
913
+ for i, f := range byFields {
914
+ quotedGroups[i] = base.QuoteIdentifier(f, t.Adapter.Dialect)
915
+ }
916
+
917
+ aggs := []string{"COUNT(*) AS _count"}
918
+ for _, aggTuple := range []struct {
919
+ kind string
920
+ value interface{}
921
+ }{
922
+ {"SUM", args.Sum},
923
+ {"AVG", args.Avg},
924
+ {"MIN", args.Min},
925
+ {"MAX", args.Max},
926
+ } {
927
+ for _, f := range selectedAggregateFields(aggTuple.value) {
928
+ aggs = append(aggs, fmt.Sprintf("%s(%s) AS %s_%s", aggTuple.kind, base.QuoteIdentifier(f, t.Adapter.Dialect), strings.ToLower(aggTuple.kind), sanitizeParamName(f)))
929
+ }
930
+ }
931
+
932
+ query := fmt.Sprintf("SELECT %s, %s FROM %s",
933
+ strings.Join(quotedGroups, ", "), strings.Join(aggs, ", "), t.quotedTable())
934
+ if whereCond != "" {
935
+ query += " WHERE " + whereCond
936
+ }
937
+ query += " GROUP BY " + strings.Join(quotedGroups, ", ")
938
+
939
+ order := t.orderSql(args.OrderBy)
940
+ hasSkip := args.Skip != 0
941
+ hasTake := args.Take > 0
942
+ if order != "" {
943
+ query += " " + order
944
+ } else if hasSkip || hasTake {
945
+ query += " ORDER BY " + strings.Join(quotedGroups, ", ")
946
+ }
947
+ if hasSkip || hasTake {
948
+ skip := args.Skip
949
+ take := args.Take
950
+ if take <= 0 {
951
+ take = 1
952
+ }
953
+ if t.Adapter.Dialect == base.DialectPostgres || t.Adapter.Dialect == base.DialectSqlite {
954
+ if hasTake {
955
+ query += fmt.Sprintf(" LIMIT %d", take)
956
+ }
957
+ query += fmt.Sprintf(" OFFSET %d", skip)
958
+ } else {
959
+ query += fmt.Sprintf(" OFFSET %d ROWS", skip)
960
+ if hasTake {
961
+ query += fmt.Sprintf(" FETCH NEXT %d ROWS ONLY", take)
962
+ }
963
+ }
964
+ }
965
+
966
+ return t.Adapter.QueryRaw(ctx, query, queryArgs...)
967
+ }
968
+
969
+ // VectorSearch ranks table rows by vector distance using native SQL or fallback.
970
+ func (t *TableClient) VectorSearch(ctx context.Context, args *VectorSearchArgs) ([]map[string]interface{}, error) {
971
+ if args == nil {
972
+ args = &VectorSearchArgs{}
973
+ }
974
+ take := args.Take
975
+ if take <= 0 {
976
+ take = 10
977
+ }
978
+ vectorField := args.VectorField
979
+ if vectorField == "" {
980
+ vectorField = "embedding"
981
+ }
982
+ distanceMetric := args.DistanceMetric
983
+ if distanceMetric == "" {
984
+ distanceMetric = "cosine"
985
+ }
986
+ whereCond, queryArgs := t.buildWhere(args.Where)
987
+ _ = queryArgs
988
+
989
+ clause := ""
990
+ if whereCond != "" {
991
+ clause = whereCond
992
+ }
993
+ return t.Adapter.VectorSearch(ctx, t.TableName, args.Vector, take, clause, vectorField, distanceMetric, queryArgs...)
994
+ }
995
+
996
+ // rename function resolves include object against a set of rows using relation metadata.
997
+ func resolveIncludes(ctx context.Context, modelName string, rows []map[string]interface{}, include interface{}, adapter *An5Adapter) error {
998
+ if len(rows) == 0 || include == nil {
999
+ return nil
1000
+ }
1001
+ modelRelations := base.GetRelationsForModel(modelName)
1002
+
1003
+ includeMap := include
1004
+ switch v := include.(type) {
1005
+ case map[string]interface{}:
1006
+ includeMap = v
1007
+ case map[string]bool:
1008
+ m := map[string]interface{}{}
1009
+ for k, b := range v {
1010
+ m[k] = b
1011
+ }
1012
+ includeMap = m
1013
+ default:
1014
+ return nil
1015
+ }
1016
+ im := includeMap.(map[string]interface{})
1017
+
1018
+ keys := make([]string, 0, len(im))
1019
+ for k := range im {
1020
+ keys = append(keys, k)
1021
+ }
1022
+ sort.Strings(keys)
1023
+
1024
+ for _, key := range keys {
1025
+ val := im[key]
1026
+ if val == nil {
1027
+ continue
1028
+ }
1029
+ if b, ok := val.(bool); ok && !b {
1030
+ continue
1031
+ }
1032
+
1033
+ if key == "_count" {
1034
+ for _, row := range rows {
1035
+ if row["_count"] == nil {
1036
+ row["_count"] = map[string]interface{}{}
1037
+ }
1038
+ }
1039
+ for relKey, relation := range modelRelations {
1040
+ if relation.RelationType != "many" {
1041
+ continue
1042
+ }
1043
+ uniqueKeys := uniqueSet(rows, relation.LocalKey)
1044
+ if len(uniqueKeys) > 0 {
1045
+ relClient := adapter.Table(relation.ModelName)
1046
+ related, err := relClient.FindMany(ctx, &FindManyArgs{
1047
+ Where: map[string]interface{}{relation.ForeignKey: map[string]interface{}{"in": uniqueKeys}},
1048
+ })
1049
+ if err != nil {
1050
+ return err
1051
+ }
1052
+ countMap := map[interface{}]int{}
1053
+ for _, r := range related {
1054
+ k := r[relation.ForeignKey]
1055
+ countMap[k]++
1056
+ }
1057
+ for _, row := range rows {
1058
+ k := row[relation.LocalKey]
1059
+ c := 0
1060
+ if n, ok := countMap[k]; ok {
1061
+ c = n
1062
+ }
1063
+ row["_count"].(map[string]interface{})[relKey] = c
1064
+ }
1065
+ } else {
1066
+ for _, row := range rows {
1067
+ row["_count"].(map[string]interface{})[relKey] = 0
1068
+ }
1069
+ }
1070
+ }
1071
+ continue
1072
+ }
1073
+
1074
+ relation, ok := modelRelations[key]
1075
+ if !ok {
1076
+ continue
1077
+ }
1078
+ isMany := relation.RelationType == "many"
1079
+ joinKey := relation.LocalKey
1080
+ matchKey := relation.ForeignKey
1081
+ if isMany {
1082
+ joinKey = relation.LocalKey
1083
+ matchKey = relation.ForeignKey
1084
+ } else {
1085
+ joinKey = relation.ForeignKey
1086
+ matchKey = relation.LocalKey
1087
+ }
1088
+ uniqueKeys := uniqueSet(rows, joinKey)
1089
+ if len(uniqueKeys) == 0 {
1090
+ for _, row := range rows {
1091
+ if isMany {
1092
+ row[key] = []map[string]interface{}{}
1093
+ } else {
1094
+ row[key] = nil
1095
+ }
1096
+ }
1097
+ continue
1098
+ }
1099
+
1100
+ subArgs := map[string]interface{}{}
1101
+ if m, ok := val.(map[string]interface{}); ok && m != nil {
1102
+ subArgs = m
1103
+ }
1104
+
1105
+ subWhere := map[string]interface{}{
1106
+ matchKey: map[string]interface{}{"in": uniqueKeys},
1107
+ }
1108
+ if w, has := subArgs["where"]; has {
1109
+ if wm, ok := w.(map[string]interface{}); ok {
1110
+ for k, v := range wm {
1111
+ subWhere[k] = v
1112
+ }
1113
+ }
1114
+ }
1115
+
1116
+ relClient := adapter.Table(relation.ModelName)
1117
+ related, err := relClient.FindMany(ctx, &FindManyArgs{
1118
+ Where: subWhere,
1119
+ OrderBy: subArgs["orderBy"],
1120
+ Take: toPositiveInt(subArgs["take"]),
1121
+ Skip: toPositiveInt(subArgs["skip"]),
1122
+ })
1123
+ if err != nil {
1124
+ return err
1125
+ }
1126
+
1127
+ if nested, has := subArgs["include"]; has {
1128
+ if err := resolveIncludes(ctx, relation.ModelName, related, nested, adapter); err != nil {
1129
+ return err
1130
+ }
1131
+ }
1132
+
1133
+ outputRows := related
1134
+ if sel, has := subArgs["select"]; has {
1135
+ outputRows = make([]map[string]interface{}, len(related))
1136
+ for i, r := range related {
1137
+ outputRows[i] = projectFields(r, sel)
1138
+ }
1139
+ }
1140
+
1141
+ groupMap := map[interface{}][]map[string]interface{}{}
1142
+ for i, r := range related {
1143
+ var k interface{}
1144
+ if isMany {
1145
+ k = r[relation.ForeignKey]
1146
+ } else {
1147
+ k = r[relation.LocalKey]
1148
+ }
1149
+ groupMap[k] = append(groupMap[k], outputRows[i])
1150
+ }
1151
+
1152
+ for _, row := range rows {
1153
+ var k interface{}
1154
+ if isMany {
1155
+ k = row[relation.LocalKey]
1156
+ } else {
1157
+ k = row[relation.ForeignKey]
1158
+ }
1159
+ matches := groupMap[k]
1160
+ if matches == nil {
1161
+ matches = []map[string]interface{}{}
1162
+ }
1163
+ if isMany {
1164
+ row[key] = matches
1165
+ } else if len(matches) > 0 {
1166
+ row[key] = matches[0]
1167
+ } else {
1168
+ row[key] = nil
1169
+ }
1170
+ }
1171
+ }
1172
+ return nil
1173
+ }
1174
+
1175
+ func uniqueSet(rows []map[string]interface{}, key string) []interface{} {
1176
+ seen := map[interface{}]bool{}
1177
+ var out []interface{}
1178
+ for _, r := range rows {
1179
+ v := r[key]
1180
+ if v == nil {
1181
+ continue
1182
+ }
1183
+ if !seen[v] {
1184
+ seen[v] = true
1185
+ out = append(out, v)
1186
+ }
1187
+ }
1188
+ return out
1189
+ }
1190
+
1191
+ func toPositiveInt(v interface{}) int {
1192
+ switch n := v.(type) {
1193
+ case int:
1194
+ if n > 0 {
1195
+ return n
1196
+ }
1197
+ case int64:
1198
+ if n > 0 {
1199
+ return int(n)
1200
+ }
1201
+ case float64:
1202
+ if n > 0 {
1203
+ return int(n)
1204
+ }
1205
+ }
1206
+ return 0
1207
+ }
1208
+
1209
+ func sanitizeParamName(name string) string {
1210
+ var b strings.Builder
1211
+ for _, r := range name {
1212
+ if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_' {
1213
+ b.WriteRune(r)
1214
+ } else {
1215
+ b.WriteRune('_')
1216
+ }
1217
+ }
1218
+ out := b.String()
1219
+ if !strings.HasPrefix(out, "_") && (out == "" || !(out[0] >= 'a' && out[0] <= 'z') && !(out[0] >= 'A' && out[0] <= 'Z')) {
1220
+ out = "p_" + out
1221
+ }
1222
+ return out
1223
+ }
1224
+
1225
+ func selectedAggregateFields(fields interface{}) []string {
1226
+ switch v := fields.(type) {
1227
+ case nil:
1228
+ return nil
1229
+ case string:
1230
+ if v != "" {
1231
+ return []string{v}
1232
+ }
1233
+ case bool:
1234
+ return nil
1235
+ case []string:
1236
+ return append([]string{}, v...)
1237
+ case []interface{}:
1238
+ var out []string
1239
+ for _, item := range v {
1240
+ if s, ok := item.(string); ok && s != "" {
1241
+ out = append(out, s)
1242
+ }
1243
+ }
1244
+ return out
1245
+ case map[string]interface{}:
1246
+ var out []string
1247
+ for f, enabled := range v {
1248
+ if b, ok := enabled.(bool); ok && b {
1249
+ out = append(out, f)
1250
+ }
1251
+ }
1252
+ sort.Strings(out)
1253
+ return out
1254
+ case map[string]bool:
1255
+ var out []string
1256
+ for f, enabled := range v {
1257
+ if enabled {
1258
+ out = append(out, f)
1259
+ }
1260
+ }
1261
+ sort.Strings(out)
1262
+ return out
1263
+ }
1264
+ return nil
1265
+ }
1266
+
1267
+ func normalizeByFields(by interface{}) []string {
1268
+ switch v := by.(type) {
1269
+ case string:
1270
+ if v != "" {
1271
+ return []string{v}
1272
+ }
1273
+ case []string:
1274
+ var out []string
1275
+ for _, f := range v {
1276
+ if f != "" {
1277
+ out = append(out, f)
1278
+ }
1279
+ }
1280
+ return out
1281
+ case []interface{}:
1282
+ var out []string
1283
+ for _, item := range v {
1284
+ if s, ok := item.(string); ok && s != "" {
1285
+ out = append(out, s)
1286
+ }
1287
+ }
1288
+ return out
1289
+ }
1290
+ return nil
1291
+ }
1292
+
1293
+ func numberOrZero(values ...interface{}) float64 {
1294
+ for _, v := range values {
1295
+ switch n := v.(type) {
1296
+ case int64:
1297
+ return float64(n)
1298
+ case int:
1299
+ return float64(n)
1300
+ case float64:
1301
+ return n
1302
+ case []byte:
1303
+ var f float64
1304
+ if _, err := fmt.Sscanf(string(n), "%f", &f); err == nil {
1305
+ return f
1306
+ }
1307
+ case string:
1308
+ var f float64
1309
+ if _, err := fmt.Sscanf(n, "%f", &f); err == nil {
1310
+ return f
1311
+ }
1312
+ }
1313
+ }
1314
+ return 0
1315
+ }
1316
+
1317
+ func numberOrNil(v interface{}) interface{} {
1318
+ if v == nil {
1319
+ return nil
1320
+ }
1321
+ return numberOrZero(v)
1322
+ }
1323
+
1324
+ func placeholder(dialect base.Dialect, index int) string {
1325
+ if dialect == base.DialectPostgres {
1326
+ return fmt.Sprintf("$%d", index)
1327
+ }
1328
+ return "?"
1329
+ }