@an5/adapters 0.2.5 → 0.2.9

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 (99) hide show
  1. package/README.md +283 -305
  2. package/dist/an5Adapter.d.ts +142 -7
  3. package/dist/an5Adapter.js +998 -68
  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 +10 -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 +5 -1
  19. package/dist/index.js +15 -1
  20. package/dist/mssql/engine.js +8 -4
  21. package/dist/mysql/engine.js +1 -1
  22. package/dist/nbase/client.d.ts +92 -0
  23. package/dist/nbase/client.js +147 -0
  24. package/dist/nbase/index.d.ts +47 -0
  25. package/dist/nbase/index.js +80 -0
  26. package/dist/postgres/engine.js +1 -1
  27. package/dist/sqlite/browserEngine.d.ts +27 -0
  28. package/dist/sqlite/browserEngine.js +161 -0
  29. package/dist/sqlite/engine.js +15 -3
  30. package/dist/sqlite/index.d.ts +1 -0
  31. package/dist/sqlite/index.js +1 -0
  32. package/dist/typed.d.ts +5 -0
  33. package/dist/typed.js +2 -0
  34. package/dotnet/An5Adapter.cs +1112 -275
  35. package/dotnet/Base/Core.cs +380 -82
  36. package/dotnet/Mssql/MssqlEngine.cs +145 -144
  37. package/dotnet/Postgres/PostgresEngine.cs +149 -121
  38. package/dotnet/Sqlite/SqliteEngine.cs +214 -0
  39. package/golang/an5_adapter.go +314 -0
  40. package/golang/base/base.go +100 -0
  41. package/golang/base/metadata.go +116 -0
  42. package/golang/base/where.go +311 -0
  43. package/golang/go.mod +3 -0
  44. package/golang/mssql/mssql.go +32 -0
  45. package/golang/postgres/postgres.go +37 -0
  46. package/golang/sqlite/sqlite.go +14 -0
  47. package/golang/sqlite_test.go +42 -0
  48. package/golang/table_client.go +1329 -0
  49. package/package.json +26 -9
  50. package/python/an5_adapter.py +184 -97
  51. package/python/base/__init__.py +3 -3
  52. package/python/base/dialects.py +11 -8
  53. package/python/base/metadata.py +59 -10
  54. package/python/base/sql.py +202 -121
  55. package/python/mssql/__init__.py +1 -1
  56. package/python/mssql/provider.py +62 -62
  57. package/python/postgres/__init__.py +1 -1
  58. package/python/postgres/provider.py +15 -15
  59. package/python/sqlite/__init__.py +3 -0
  60. package/python/sqlite/provider.py +83 -0
  61. package/python/table_client.py +636 -216
  62. package/rust/Cargo.toml +31 -0
  63. package/rust/README.md +71 -0
  64. package/rust/src/adapter.rs +1312 -0
  65. package/rust/src/base/dialect.rs +172 -0
  66. package/rust/src/base/metadata.rs +134 -0
  67. package/rust/src/base/mod.rs +17 -0
  68. package/rust/src/base/where_builder.rs +405 -0
  69. package/rust/src/lib.rs +75 -0
  70. package/typescript/src/an5Adapter.ts +1515 -505
  71. package/typescript/src/base/index.ts +3 -3
  72. package/typescript/src/base/metadata.ts +148 -28
  73. package/typescript/src/base/sql.ts +264 -98
  74. package/typescript/src/base/types.ts +33 -24
  75. package/typescript/src/browser.ts +10 -6
  76. package/typescript/src/config.ts +95 -95
  77. package/typescript/src/googlesheets/adapter.ts +269 -259
  78. package/typescript/src/googlesheets/an5SheetsAdapter.ts +3 -3
  79. package/typescript/src/googlesheets/config.ts +47 -47
  80. package/typescript/src/googlesheets/helpers.ts +144 -143
  81. package/typescript/src/googlesheets/index.ts +3 -3
  82. package/typescript/src/googlesheets/parseConnectionString.ts +48 -46
  83. package/typescript/src/googlesheets/retry.ts +126 -126
  84. package/typescript/src/googlesheets/sqlExecutor.ts +304 -301
  85. package/typescript/src/googlesheets/tableClient.ts +461 -455
  86. package/typescript/src/googlesheets/types.ts +6 -6
  87. package/typescript/src/index.ts +48 -27
  88. package/typescript/src/mssql/engine.ts +134 -132
  89. package/typescript/src/mssql/index.ts +1 -1
  90. package/typescript/src/mysql/engine.ts +80 -80
  91. package/typescript/src/mysql/index.ts +1 -1
  92. package/typescript/src/nbase/client.ts +214 -0
  93. package/typescript/src/nbase/index.ts +104 -0
  94. package/typescript/src/postgres/engine.ts +87 -87
  95. package/typescript/src/postgres/index.ts +1 -1
  96. package/typescript/src/sqlite/browserEngine.ts +192 -0
  97. package/typescript/src/sqlite/engine.ts +66 -55
  98. package/typescript/src/sqlite/index.ts +3 -1
  99. package/typescript/src/typed.ts +52 -0
@@ -0,0 +1,214 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using System.Data;
4
+ using System.Reflection;
5
+
6
+ namespace An5Orm
7
+ {
8
+ // ─── SQLite Engine ──────────────────────────────────────────────────────────
9
+ //
10
+ // Microsoft.Data.Sqlite is a different type family from the other two
11
+ // providers (SqliteConnection/SqliteCommand, not DbConnection subclasses you
12
+ // can reuse), so it gets its own engine rather than a shared abstraction.
13
+ //
14
+ // Notable differences from MSSQL, all handled by the SQL builder in
15
+ // An5Adapter rather than here: no WITH (NOLOCK) table hint, no SELECT TOP,
16
+ // LIMIT/OFFSET instead of OFFSET/FETCH NEXT, and no stored procedures.
17
+
18
+ internal class SqliteEngine : IQueryEngine
19
+ {
20
+ public Dialect Dialect => Dialect.Sqlite;
21
+ private readonly string _connectionString;
22
+ private readonly int _commandTimeout;
23
+
24
+ public SqliteEngine(string connectionString, int commandTimeout)
25
+ {
26
+ _connectionString = SqliteConnectionString.Normalize(connectionString);
27
+ _commandTimeout = commandTimeout;
28
+ }
29
+
30
+ // Set while a transaction is open; statements then join it instead of
31
+ // opening a second connection, which SQLite would refuse.
32
+ private Microsoft.Data.Sqlite.SqliteConnection _txnConn;
33
+
34
+ private Microsoft.Data.Sqlite.SqliteConnection OpenNewConnection()
35
+ {
36
+ var conn = new Microsoft.Data.Sqlite.SqliteConnection(_connectionString);
37
+ conn.Open();
38
+ // WAL keeps readers from blocking the writer, and foreign keys are
39
+ // off by default in SQLite even when the schema declares them.
40
+ using var pragma = conn.CreateCommand();
41
+ pragma.CommandText = "PRAGMA journal_mode=WAL; PRAGMA foreign_keys=ON;";
42
+ pragma.ExecuteNonQuery();
43
+ return conn;
44
+ }
45
+
46
+ private ConnectionScope<Microsoft.Data.Sqlite.SqliteConnection> Acquire()
47
+ => _txnConn != null
48
+ ? new ConnectionScope<Microsoft.Data.Sqlite.SqliteConnection>(_txnConn, owned: false)
49
+ : new ConnectionScope<Microsoft.Data.Sqlite.SqliteConnection>(OpenNewConnection(), owned: true);
50
+
51
+ private Microsoft.Data.Sqlite.SqliteCommand BuildCommand(
52
+ Microsoft.Data.Sqlite.SqliteConnection conn, string sql, Dictionary<string, object> parameters)
53
+ {
54
+ var cmd = conn.CreateCommand();
55
+ cmd.CommandText = sql;
56
+ cmd.CommandTimeout = _commandTimeout;
57
+ if (parameters != null)
58
+ {
59
+ foreach (var kv in parameters)
60
+ {
61
+ // SqliteParameter names must not carry the prefix; the SQL
62
+ // keeps @name and the parameter is registered as name.
63
+ var paramName = kv.Key.TrimStart('@');
64
+ var value = kv.Value ?? DBNull.Value;
65
+
66
+ var param = cmd.CreateParameter();
67
+ param.ParameterName = paramName;
68
+ // A JSON vector or a bool needs a type SQLite will store in a
69
+ // column rather than coerce to an integer.
70
+ if (value is bool flag) param.Value = flag ? 1 : 0;
71
+ else param.Value = value;
72
+ cmd.Parameters.Add(param);
73
+ }
74
+ }
75
+ return cmd;
76
+ }
77
+
78
+ public List<Dictionary<string, object>> QueryRaw(string sql, Dictionary<string, object> parameters)
79
+ {
80
+ var results = new List<Dictionary<string, object>>();
81
+ using var scope = Acquire();
82
+ using var cmd = BuildCommand(scope.Connection, sql, parameters);
83
+ using var reader = cmd.ExecuteReader();
84
+ while (reader.Read())
85
+ {
86
+ var row = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
87
+ for (int i = 0; i < reader.FieldCount; i++)
88
+ {
89
+ row[reader.GetName(i)] = reader.IsDBNull(i) ? null : reader.GetValue(i);
90
+ }
91
+ results.Add(row);
92
+ }
93
+ return results;
94
+ }
95
+
96
+ public List<T> QueryRaw<T>(string sql, Dictionary<string, object> parameters) where T : new()
97
+ {
98
+ var results = new List<T>();
99
+ using var scope = Acquire();
100
+ using var cmd = BuildCommand(scope.Connection, sql, parameters);
101
+ using var reader = cmd.ExecuteReader();
102
+ var props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
103
+ while (reader.Read())
104
+ {
105
+ var item = new T();
106
+ foreach (var prop in props)
107
+ {
108
+ if (!HasColumn(reader, prop.Name)) continue;
109
+ var val = reader[prop.Name];
110
+ if (val == DBNull.Value) continue;
111
+ try { prop.SetValue(item, Convert.ChangeType(val, prop.PropertyType)); } catch { }
112
+ }
113
+ results.Add(item);
114
+ }
115
+ return results;
116
+ }
117
+
118
+ public int ExecuteRaw(string sql, Dictionary<string, object> parameters)
119
+ {
120
+ using var scope = Acquire();
121
+ using var cmd = BuildCommand(scope.Connection, sql, parameters);
122
+ return cmd.ExecuteNonQuery();
123
+ }
124
+
125
+ private static bool HasColumn(Microsoft.Data.Sqlite.SqliteDataReader reader, string name)
126
+ {
127
+ for (int i = 0; i < reader.FieldCount; i++)
128
+ if (reader.GetName(i).Equals(name, StringComparison.OrdinalIgnoreCase)) return true;
129
+ return false;
130
+ }
131
+
132
+ /// <summary>Detaches the transaction connection, so later statements open their own.</summary>
133
+ internal void ReleaseTransactionConnection() => _txnConn = null;
134
+
135
+ public An5TransactionBase BeginTransaction()
136
+ {
137
+ if (_txnConn != null)
138
+ throw new InvalidOperationException("A transaction is already open on this adapter.");
139
+ var conn = OpenNewConnection();
140
+ var tx = conn.BeginTransaction();
141
+ _txnConn = conn;
142
+ return new SqliteTransaction(this, conn, tx);
143
+ }
144
+ }
145
+
146
+ internal class SqliteTransaction : An5TransactionBase
147
+ {
148
+ private readonly SqliteEngine _engine;
149
+ private readonly Microsoft.Data.Sqlite.SqliteConnection _conn;
150
+ private readonly Microsoft.Data.Sqlite.SqliteTransaction _tx;
151
+
152
+ public SqliteTransaction(SqliteEngine engine, Microsoft.Data.Sqlite.SqliteConnection conn, Microsoft.Data.Sqlite.SqliteTransaction tx)
153
+ {
154
+ _engine = engine;
155
+ _conn = conn;
156
+ _tx = tx;
157
+ }
158
+
159
+ public override void Commit() { _tx.Commit(); }
160
+
161
+ public override void Rollback() { _tx.Rollback(); }
162
+
163
+ public override void Dispose()
164
+ {
165
+ _engine.ReleaseTransactionConnection();
166
+ _tx.Dispose();
167
+ _conn.Dispose();
168
+ }
169
+ }
170
+
171
+ // ─── Connection string normalisation ──────────────────────────────────────
172
+
173
+ internal static class SqliteConnectionString
174
+ {
175
+ /// <summary>
176
+ /// Turns the accepted spellings into a Data Source string.
177
+ ///
178
+ /// Microsoft.Data.Sqlite only understands `Data Source=`, so the bare
179
+ /// paths and `sqlite:` URLs the rest of the ORM accepts have to be
180
+ /// rewritten. An in-memory database has to stay open for the life of the
181
+ /// connection, which is why callers keep one connection per operation
182
+ /// open only for the duration of a statement.
183
+ /// </summary>
184
+ public static string Normalize(string connectionString)
185
+ {
186
+ var cs = (connectionString ?? "").Trim();
187
+ if (cs.Length == 0) return "Data Source=:memory:";
188
+
189
+ // Already a Microsoft.Data.Sqlite connection string.
190
+ if (cs.IndexOf("Data Source", StringComparison.OrdinalIgnoreCase) >= 0
191
+ || cs.IndexOf("DataSource", StringComparison.OrdinalIgnoreCase) >= 0
192
+ || cs.IndexOf("Mode", StringComparison.OrdinalIgnoreCase) >= 0)
193
+ return cs;
194
+
195
+ if (cs == ":memory:")
196
+ return "Data Source=:memory:";
197
+
198
+ if (cs.StartsWith("sqlite://", StringComparison.OrdinalIgnoreCase))
199
+ return "Data Source=" + cs.Substring("sqlite://".Length);
200
+
201
+ if (cs.StartsWith("sqlite:", StringComparison.OrdinalIgnoreCase))
202
+ {
203
+ // `sqlite:app.db` and `sqlite::memory:`
204
+ return "Data Source=" + cs.Substring("sqlite:".Length);
205
+ }
206
+
207
+ if (cs.StartsWith("file:", StringComparison.OrdinalIgnoreCase))
208
+ return cs; // SQLite URI, understood as-is.
209
+
210
+ // A bare path.
211
+ return "Data Source=" + cs;
212
+ }
213
+ }
214
+ }
@@ -0,0 +1,314 @@
1
+ // Package an5adapters provides standalone database adapter engines for Go applications.
2
+ package an5adapters
3
+
4
+ import (
5
+ "context"
6
+ "database/sql"
7
+ "encoding/json"
8
+ "sort"
9
+ "strings"
10
+
11
+ "an5adapters/base"
12
+ "an5adapters/mssql"
13
+ "an5adapters/postgres"
14
+ "an5adapters/sqlite"
15
+ )
16
+
17
+ // Re-export core base types and functions for package root convenience.
18
+ type Dialect = base.Dialect
19
+ type AdapterMetadata = base.AdapterMetadata
20
+ type RelationDef = base.RelationDef
21
+
22
+ const (
23
+ DialectMssql = base.DialectMssql
24
+ DialectPostgres = base.DialectPostgres
25
+ DialectSqlite = base.DialectSqlite
26
+ )
27
+
28
+ func DetectDialect(connStr string) Dialect {
29
+ return base.DetectDialect(connStr)
30
+ }
31
+
32
+ func QuoteIdentifier(name string, dialect Dialect) string {
33
+ return base.QuoteIdentifier(name, dialect)
34
+ }
35
+
36
+ func QuoteTable(tableName string, dialect Dialect) string {
37
+ return base.QuoteTable(tableName, dialect)
38
+ }
39
+
40
+ func CosineSimilarity(v1, v2 []float64) float64 {
41
+ return base.CosineSimilarity(v1, v2)
42
+ }
43
+
44
+ func EuclideanDistance(v1, v2 []float64) float64 {
45
+ return base.EuclideanDistance(v1, v2)
46
+ }
47
+
48
+ func DotProduct(v1, v2 []float64) float64 {
49
+ return base.DotProduct(v1, v2)
50
+ }
51
+
52
+ // SetAdapterMetadata injects model-to-table metadata from the generated client.
53
+ func SetAdapterMetadata(meta AdapterMetadata) {
54
+ base.SetAdapterMetadata(meta)
55
+ }
56
+
57
+ // GetModelToTable returns the current model-to-table mapping.
58
+ func GetModelToTable() map[string]string {
59
+ return base.GetModelToTable()
60
+ }
61
+
62
+ // GetRelationsForModel returns the relation definitions for the given model name.
63
+ func GetRelationsForModel(modelName string) map[string]RelationDef {
64
+ return base.GetRelationsForModel(modelName)
65
+ }
66
+
67
+ // An5Adapter manages runtime database execution for Go applications.
68
+ type An5Adapter struct {
69
+ DB *sql.DB
70
+ Dialect Dialect
71
+ }
72
+
73
+ // NewAn5Adapter constructs an An5Adapter instance.
74
+ func NewAn5Adapter(db *sql.DB, connStr string) *An5Adapter {
75
+ return &An5Adapter{
76
+ DB: db,
77
+ Dialect: base.DetectDialect(connStr),
78
+ }
79
+ }
80
+
81
+ // Connect validates the database connection (ping).
82
+ func (a *An5Adapter) Connect(ctx context.Context) error {
83
+ return a.DB.PingContext(ctx)
84
+ }
85
+
86
+ // Disconnect closes the underlying database connection pool.
87
+ func (a *An5Adapter) Disconnect() error {
88
+ return a.DB.Close()
89
+ }
90
+
91
+ // QueryRaw executes a SELECT query and returns rows as maps.
92
+ func (a *An5Adapter) QueryRaw(ctx context.Context, query string, args ...interface{}) ([]map[string]interface{}, error) {
93
+ rows, err := a.DB.QueryContext(ctx, query, args...)
94
+ if err != nil {
95
+ return nil, err
96
+ }
97
+ defer rows.Close()
98
+
99
+ cols, err := rows.Columns()
100
+ if err != nil {
101
+ return nil, err
102
+ }
103
+
104
+ var results []map[string]interface{}
105
+ for rows.Next() {
106
+ columns := make([]interface{}, len(cols))
107
+ columnPointers := make([]interface{}, len(cols))
108
+ for i := range columns {
109
+ columnPointers[i] = &columns[i]
110
+ }
111
+
112
+ if err := rows.Scan(columnPointers...); err != nil {
113
+ return nil, err
114
+ }
115
+
116
+ m := make(map[string]interface{})
117
+ for i, colName := range cols {
118
+ val := columnPointers[i].(*interface{})
119
+ m[colName] = *val
120
+ }
121
+ results = append(results, m)
122
+ }
123
+ if err := rows.Err(); err != nil {
124
+ return nil, err
125
+ }
126
+ return results, nil
127
+ }
128
+
129
+ // ExecuteRaw executes an INSERT, UPDATE, or DELETE query and returns affected rows count.
130
+ func (a *An5Adapter) ExecuteRaw(ctx context.Context, query string, args ...interface{}) (int64, error) {
131
+ res, err := a.DB.ExecContext(ctx, query, args...)
132
+ if err != nil {
133
+ return 0, err
134
+ }
135
+ return res.RowsAffected()
136
+ }
137
+
138
+ // Transaction executes a function within a database transaction.
139
+ func (a *An5Adapter) Transaction(ctx context.Context, fn func(tx *sql.Tx) error) error {
140
+ tx, err := a.DB.BeginTx(ctx, nil)
141
+ if err != nil {
142
+ return err
143
+ }
144
+ defer func() {
145
+ if p := recover(); p != nil {
146
+ _ = tx.Rollback()
147
+ panic(p)
148
+ }
149
+ }()
150
+
151
+ if err := fn(tx); err != nil {
152
+ _ = tx.Rollback()
153
+ return err
154
+ }
155
+ return tx.Commit()
156
+ }
157
+
158
+ // VectorSearch ranks rows by vector distance using native SQL execution with in-memory fallback.
159
+ func (a *An5Adapter) VectorSearch(ctx context.Context, tableName string, targetVector []float64, take int, whereClause string, vectorField string, distanceMetric string, args ...interface{}) ([]map[string]interface{}, error) {
160
+ if vectorField == "" {
161
+ vectorField = "embedding"
162
+ }
163
+ if distanceMetric == "" {
164
+ distanceMetric = "cosine"
165
+ }
166
+ dim := len(targetVector)
167
+ vecBytes, _ := json.Marshal(targetVector)
168
+ vecStr := string(vecBytes)
169
+
170
+ // 1. Primary path: Native database SQL vector query execution via dialect provider (mssql / postgres / sqlite)
171
+ var sqlQuery string
172
+ if a.Dialect == base.DialectPostgres {
173
+ sqlQuery = postgres.BuildVectorSearchQuery(tableName, vectorField, distanceMetric, dim, take, whereClause)
174
+ } else if a.Dialect == base.DialectMssql {
175
+ sqlQuery = mssql.BuildVectorSearchQuery(tableName, vectorField, distanceMetric, dim, take, whereClause)
176
+ } else if a.Dialect == base.DialectSqlite {
177
+ sqlQuery = sqlite.BuildVectorSearchQuery(tableName, vectorField, distanceMetric, dim, take, whereClause)
178
+ }
179
+
180
+ if sqlQuery != "" {
181
+ queryArgs := append([]interface{}{vecStr}, args...)
182
+ nativeRows, err := a.QueryRaw(ctx, sqlQuery, queryArgs...)
183
+ if err == nil {
184
+ return nativeRows, nil
185
+ }
186
+ }
187
+
188
+ // 2. Secondary fallback: Fetch rows and compute vector distance in-memory if DB lacks native vector extensions
189
+ fallbackQuery := "SELECT * FROM " + base.QuoteTable(tableName, a.Dialect)
190
+ if strings.TrimSpace(whereClause) != "" {
191
+ fallbackQuery += " WHERE " + whereClause
192
+ }
193
+
194
+ rows, err := a.QueryRaw(ctx, fallbackQuery, args...)
195
+ if err != nil {
196
+ return nil, err
197
+ }
198
+
199
+ return VectorSearchFallback(rows, targetVector, take, vectorField, distanceMetric), nil
200
+ }
201
+
202
+ // VectorSearchFallback ranks rows in-memory.
203
+ func VectorSearchFallback(rows []map[string]interface{}, targetVector []float64, take int, vectorField string, distanceMetric string) []map[string]interface{} {
204
+ type scored struct {
205
+ row map[string]interface{}
206
+ distance float64
207
+ }
208
+ var list []scored
209
+
210
+ for _, r := range rows {
211
+ raw, ok := r[vectorField]
212
+ if !ok || raw == nil {
213
+ continue
214
+ }
215
+
216
+ var rowVec []float64
217
+ switch v := raw.(type) {
218
+ case string:
219
+ _ = json.Unmarshal([]byte(v), &rowVec)
220
+ case []byte:
221
+ _ = json.Unmarshal(v, &rowVec)
222
+ case []float64:
223
+ rowVec = v
224
+ case []interface{}:
225
+ for _, item := range v {
226
+ if f, ok := item.(float64); ok {
227
+ rowVec = append(rowVec, f)
228
+ }
229
+ }
230
+ }
231
+
232
+ if len(rowVec) != len(targetVector) {
233
+ continue
234
+ }
235
+
236
+ var dist float64
237
+ switch strings.ToLower(distanceMetric) {
238
+ case "euclidean":
239
+ dist = base.EuclideanDistance(targetVector, rowVec)
240
+ case "dot":
241
+ dist = -base.DotProduct(targetVector, rowVec)
242
+ default: // cosine
243
+ dist = 1.0 - base.CosineSimilarity(targetVector, rowVec)
244
+ }
245
+
246
+ rCopy := make(map[string]interface{})
247
+ for k, val := range r {
248
+ rCopy[k] = val
249
+ }
250
+ rCopy["distance"] = dist
251
+ list = append(list, scored{row: rCopy, distance: dist})
252
+ }
253
+
254
+ sort.Slice(list, func(i, j int) bool {
255
+ return list[i].distance < list[j].distance
256
+ })
257
+
258
+ var result []map[string]interface{}
259
+ limit := take
260
+ if limit <= 0 || limit > len(list) {
261
+ limit = len(list)
262
+ }
263
+ for i := 0; i < limit; i++ {
264
+ result = append(result, list[i].row)
265
+ }
266
+ return result
267
+ }
268
+
269
+ // QueryProc executes a stored procedure and returns the result rows.
270
+ func (a *An5Adapter) QueryProc(ctx context.Context, procName string, args ...interface{}) ([]map[string]interface{}, error) {
271
+ var sql string
272
+ if a.Dialect == base.DialectPostgres {
273
+ placeholders := make([]string, len(args))
274
+ for i := range args {
275
+ placeholders[i] = placeholder(a.Dialect, i+1)
276
+ }
277
+ sql = "CALL " + procName + "(" + strings.Join(placeholders, ", ") + ")"
278
+ } else {
279
+ placeholders := make([]string, len(args))
280
+ for i := range args {
281
+ placeholders[i] = "?"
282
+ }
283
+ if len(placeholders) > 0 {
284
+ sql = "EXEC " + procName + " " + strings.Join(placeholders, ", ")
285
+ } else {
286
+ sql = "EXEC " + procName
287
+ }
288
+ }
289
+ return a.QueryRaw(ctx, sql, args...)
290
+ }
291
+
292
+ // ExecuteProc executes a stored procedure and returns affected rows.
293
+ func (a *An5Adapter) ExecuteProc(ctx context.Context, procName string, args ...interface{}) (int64, error) {
294
+ var sql string
295
+ if a.Dialect == base.DialectPostgres {
296
+ placeholders := make([]string, len(args))
297
+ for i := range args {
298
+ placeholders[i] = placeholder(a.Dialect, i+1)
299
+ }
300
+ sql = "CALL " + procName + "(" + strings.Join(placeholders, ", ") + ")"
301
+ } else {
302
+ placeholders := make([]string, len(args))
303
+ for i := range args {
304
+ placeholders[i] = "?"
305
+ }
306
+ if len(placeholders) > 0 {
307
+ sql = "EXEC " + procName + " " + strings.Join(placeholders, ", ")
308
+ } else {
309
+ sql = "EXEC " + procName
310
+ }
311
+ }
312
+ return a.ExecuteRaw(ctx, sql, args...)
313
+ }
314
+
@@ -0,0 +1,100 @@
1
+ // Package base provides core dialect types, quoting utilities, and vector math for AN5 Go Adapters.
2
+ package base
3
+
4
+ import (
5
+ "math"
6
+ "strings"
7
+ )
8
+
9
+ // Dialect represents supported database engines.
10
+ type Dialect string
11
+
12
+ const (
13
+ DialectMssql Dialect = "mssql"
14
+ DialectPostgres Dialect = "postgres"
15
+ DialectSqlite Dialect = "sqlite"
16
+ )
17
+
18
+ // DetectDialect parses connection string to determine database dialect.
19
+ func DetectDialect(connStr string) Dialect {
20
+ lower := strings.ToLower(strings.TrimSpace(connStr))
21
+ if strings.HasPrefix(lower, "postgres://") || strings.HasPrefix(lower, "postgresql://") || strings.Contains(lower, "port=5432") {
22
+ return DialectPostgres
23
+ }
24
+ if strings.HasPrefix(lower, "sqlite://") || strings.HasPrefix(lower, "sqlite:") || strings.HasPrefix(lower, "file:") || strings.HasSuffix(lower, ".db") || strings.HasSuffix(lower, ".sqlite") || strings.HasSuffix(lower, ".sqlite3") || lower == ":memory:" {
25
+ return DialectSqlite
26
+ }
27
+ return DialectMssql
28
+ }
29
+
30
+ // QuoteIdentifier quotes column or identifier names according to dialect.
31
+ func QuoteIdentifier(name string, dialect Dialect) string {
32
+ raw := stripWrapping(name, "[", "]")
33
+ if dialect == DialectPostgres || dialect == DialectSqlite {
34
+ raw = stripWrapping(raw, `"`, `"`)
35
+ return `"` + strings.ReplaceAll(raw, `"`, `""`) + `"`
36
+ }
37
+ return "[" + strings.ReplaceAll(raw, "]", "]]") + "]"
38
+ }
39
+
40
+ // QuoteTable quotes schema-qualified table names (e.g. "dbo.users" -> "[dbo].[users]").
41
+ func QuoteTable(tableName string, dialect Dialect) string {
42
+ if strings.HasPrefix(tableName, "[") || strings.HasPrefix(tableName, `"`) {
43
+ return tableName
44
+ }
45
+ parts := strings.Split(tableName, ".")
46
+ var quoted []string
47
+ for _, p := range parts {
48
+ quoted = append(quoted, QuoteIdentifier(p, dialect))
49
+ }
50
+ return strings.Join(quoted, ".")
51
+ }
52
+
53
+ func stripWrapping(name, left, right string) string {
54
+ if strings.HasPrefix(name, left) && strings.HasSuffix(name, right) {
55
+ return strings.TrimSuffix(strings.TrimPrefix(name, left), right)
56
+ }
57
+ return name
58
+ }
59
+
60
+ // CosineSimilarity computes cosine similarity between two float64 vectors.
61
+ func CosineSimilarity(v1, v2 []float64) float64 {
62
+ if len(v1) != len(v2) || len(v1) == 0 {
63
+ return 0
64
+ }
65
+ var dot, m1, m2 float64
66
+ for i := 0; i < len(v1); i++ {
67
+ dot += v1[i] * v2[i]
68
+ m1 += v1[i] * v1[i]
69
+ m2 += v2[i] * v2[i]
70
+ }
71
+ if m1 == 0 || m2 == 0 {
72
+ return 0
73
+ }
74
+ return dot / (math.Sqrt(m1) * math.Sqrt(m2))
75
+ }
76
+
77
+ // EuclideanDistance computes L2 distance between two float64 vectors.
78
+ func EuclideanDistance(v1, v2 []float64) float64 {
79
+ if len(v1) != len(v2) || len(v1) == 0 {
80
+ return 0
81
+ }
82
+ var sum float64
83
+ for i := 0; i < len(v1); i++ {
84
+ diff := v1[i] - v2[i]
85
+ sum += diff * diff
86
+ }
87
+ return math.Sqrt(sum)
88
+ }
89
+
90
+ // DotProduct computes inner product of two float64 vectors.
91
+ func DotProduct(v1, v2 []float64) float64 {
92
+ if len(v1) != len(v2) || len(v1) == 0 {
93
+ return 0
94
+ }
95
+ var dot float64
96
+ for i := 0; i < len(v1); i++ {
97
+ dot += v1[i] * v2[i]
98
+ }
99
+ return dot
100
+ }