@pineliner/odb-client 1.0.8 → 1.0.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.
- package/dist/database/adapters/bun-sqlite.d.ts.map +1 -1
- package/dist/database/adapters/libsql.d.ts.map +1 -1
- package/dist/database/adapters/odblite.d.ts.map +1 -1
- package/dist/database/types.d.ts +26 -2
- package/dist/database/types.d.ts.map +1 -1
- package/dist/index.cjs +106 -28
- package/dist/index.js +107 -29
- package/package.json +1 -1
- package/src/database/adapters/bun-sqlite.ts +59 -5
- package/src/database/adapters/libsql.ts +66 -6
- package/src/database/adapters/odblite.ts +79 -16
- package/src/database/types.ts +28 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bun-sqlite.d.ts","sourceRoot":"","sources":["../../../src/database/adapters/bun-sqlite.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,UAAU,EAGV,eAAe,
|
|
1
|
+
{"version":3,"file":"bun-sqlite.d.ts","sourceRoot":"","sources":["../../../src/database/adapters/bun-sqlite.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,UAAU,EAGV,eAAe,EAEhB,MAAM,UAAU,CAAA;AAsDjB;;;GAGG;AACH,qBAAa,gBAAiB,YAAW,eAAe;IACtD,QAAQ,CAAC,IAAI,EAAG,YAAY,CAAS;IACrC,OAAO,CAAC,MAAM,CAAiB;gBAEnB,MAAM,EAAE,eAAe;IAI7B,OAAO,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC;IASrD,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK5C,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAapC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"libsql.d.ts","sourceRoot":"","sources":["../../../src/database/adapters/libsql.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,UAAU,EAGV,YAAY,
|
|
1
|
+
{"version":3,"file":"libsql.d.ts","sourceRoot":"","sources":["../../../src/database/adapters/libsql.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,UAAU,EAGV,YAAY,EAEb,MAAM,UAAU,CAAA;AAsDjB;;;GAGG;AACH,qBAAa,aAAc,YAAW,eAAe;IACnD,QAAQ,CAAC,IAAI,EAAG,QAAQ,CAAS;IACjC,OAAO,CAAC,MAAM,CAAc;gBAEhB,MAAM,EAAE,YAAY;IAI1B,OAAO,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC;IAUlD,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK5C,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAYpC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"odblite.d.ts","sourceRoot":"","sources":["../../../src/database/adapters/odblite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAA;AAE5D,OAAO,KAAK,EACV,eAAe,EACf,UAAU,EAGV,aAAa,
|
|
1
|
+
{"version":3,"file":"odblite.d.ts","sourceRoot":"","sources":["../../../src/database/adapters/odblite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAA;AAE5D,OAAO,KAAK,EACV,eAAe,EACf,UAAU,EAGV,aAAa,EAEd,MAAM,UAAU,CAAA;AAsDjB;;;GAGG;AACH,qBAAa,cAAe,YAAW,eAAe;IACpD,QAAQ,CAAC,IAAI,EAAG,SAAS,CAAS;IAClC,OAAO,CAAC,MAAM,CAAe;IACtB,aAAa,EAAE,aAAa,CAAA;gBAEvB,MAAM,EAAE,aAAa;IAQ3B,OAAO,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC;IAgCzC,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa5C,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAQpC"}
|
package/dist/database/types.d.ts
CHANGED
|
@@ -14,6 +14,30 @@ export interface QueryResult<T = any> {
|
|
|
14
14
|
rowsAffected?: number;
|
|
15
15
|
lastInsertRowid?: number | bigint;
|
|
16
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Query options for fine-grained control
|
|
19
|
+
*/
|
|
20
|
+
export interface QueryOptions {
|
|
21
|
+
/**
|
|
22
|
+
* List of column names that should be auto-parsed as JSON when reading
|
|
23
|
+
* Only applies to columns that contain stringified JSON in SELECT queries
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* await conn.query('SELECT * FROM suppliers', [], { jsonColumns: ['connections', 'config'] })
|
|
27
|
+
*/
|
|
28
|
+
jsonColumns?: string[];
|
|
29
|
+
/**
|
|
30
|
+
* Automatically stringify parameters for JSON columns in UPDATE/INSERT queries
|
|
31
|
+
* Maps column names to parameter indices (0-based)
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* // UPDATE suppliers SET connections = ?, updated_at = ? WHERE id = ?
|
|
35
|
+
* await conn.execute(query, [connections, now, id], {
|
|
36
|
+
* stringifyParams: { connections: 0 } // Auto-stringify first parameter
|
|
37
|
+
* })
|
|
38
|
+
*/
|
|
39
|
+
stringifyParams?: Record<string, number>;
|
|
40
|
+
}
|
|
17
41
|
/**
|
|
18
42
|
* Unified database connection interface
|
|
19
43
|
* All backends implement this through adapters
|
|
@@ -26,11 +50,11 @@ export interface QueryResult<T = any> {
|
|
|
26
50
|
export interface Connection {
|
|
27
51
|
sql<T = any>(strings: TemplateStringsArray, ...values: any[]): Promise<T[]>;
|
|
28
52
|
sql(value: any, ...keys: string[]): any;
|
|
29
|
-
query<T = any>(sql: string, params?: any[]): Promise<QueryResult<T>>;
|
|
53
|
+
query<T = any>(sql: string, params?: any[], options?: QueryOptions): Promise<QueryResult<T>>;
|
|
30
54
|
execute(sql: string | {
|
|
31
55
|
sql: string;
|
|
32
56
|
args?: any[];
|
|
33
|
-
}, params?: any[]): Promise<QueryResult>;
|
|
57
|
+
}, params?: any[], options?: QueryOptions): Promise<QueryResult>;
|
|
34
58
|
prepare(sql: string): PreparedStatement;
|
|
35
59
|
transaction<T>(fn: (tx: Connection) => Promise<T>): Promise<T>;
|
|
36
60
|
begin<T>(fn: (tx: Connection) => Promise<T>): Promise<T>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/database/types.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,MAAM,WAAW,GACnB,WAAW,GACX,cAAc,GACd,WAAW,GACX,WAAW,CAAA;AAEf;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,QAAQ,GAAG,SAAS,CAAA;AAE7D;;GAEG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,GAAG;IAClC,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAClC;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,UAAU;IAGzB,GAAG,CAAC,CAAC,GAAG,GAAG,EAAE,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAA;IAG3E,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,GAAG,CAAA;IAGvC,KAAK,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/database/types.ts"],"names":[],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -1350,6 +1350,26 @@ var __webpack_exports__ = {};
|
|
|
1350
1350
|
args: values
|
|
1351
1351
|
};
|
|
1352
1352
|
}
|
|
1353
|
+
function parseJsonColumns(rows, jsonColumns) {
|
|
1354
|
+
if (!jsonColumns || 0 === jsonColumns.length) return rows;
|
|
1355
|
+
return rows.map((row)=>{
|
|
1356
|
+
const parsed = {
|
|
1357
|
+
...row
|
|
1358
|
+
};
|
|
1359
|
+
for (const col of jsonColumns)if (col in parsed && 'string' == typeof parsed[col]) try {
|
|
1360
|
+
parsed[col] = JSON.parse(parsed[col]);
|
|
1361
|
+
} catch {}
|
|
1362
|
+
return parsed;
|
|
1363
|
+
});
|
|
1364
|
+
}
|
|
1365
|
+
function stringifyJsonParams(params, stringifyParams) {
|
|
1366
|
+
if (!stringifyParams || 0 === Object.keys(stringifyParams).length) return params;
|
|
1367
|
+
const result = [
|
|
1368
|
+
...params
|
|
1369
|
+
];
|
|
1370
|
+
for (const [_columnName, index] of Object.entries(stringifyParams))if (index < result.length && null != result[index] && 'object' == typeof result[index]) result[index] = JSON.stringify(result[index]);
|
|
1371
|
+
return result;
|
|
1372
|
+
}
|
|
1353
1373
|
class BunSQLiteAdapter {
|
|
1354
1374
|
type = 'bun-sqlite';
|
|
1355
1375
|
config;
|
|
@@ -1407,10 +1427,10 @@ var __webpack_exports__ = {};
|
|
|
1407
1427
|
this.sql.set = set;
|
|
1408
1428
|
this.sql.where = sql_template_where;
|
|
1409
1429
|
}
|
|
1410
|
-
async query(sql, params = []) {
|
|
1411
|
-
return this.execute(sql, params);
|
|
1430
|
+
async query(sql, params = [], options) {
|
|
1431
|
+
return this.execute(sql, params, options);
|
|
1412
1432
|
}
|
|
1413
|
-
async execute(sql, params = []) {
|
|
1433
|
+
async execute(sql, params = [], options) {
|
|
1414
1434
|
try {
|
|
1415
1435
|
let sqlStr;
|
|
1416
1436
|
let sqlParams;
|
|
@@ -1429,12 +1449,14 @@ var __webpack_exports__ = {};
|
|
|
1429
1449
|
sqlStr = sql;
|
|
1430
1450
|
sqlParams = params;
|
|
1431
1451
|
}
|
|
1452
|
+
if (options?.stringifyParams) sqlParams = stringifyJsonParams(sqlParams, options.stringifyParams);
|
|
1432
1453
|
const stmt = this.db.query(sqlStr);
|
|
1433
1454
|
const isSelect = sqlStr.trim().toUpperCase().startsWith('SELECT') || sqlStr.trim().toUpperCase().includes('RETURNING');
|
|
1434
1455
|
if (isSelect) {
|
|
1435
|
-
|
|
1456
|
+
let rows = stmt.all(...sqlParams);
|
|
1457
|
+
if (options?.jsonColumns) rows = parseJsonColumns(rows, options.jsonColumns);
|
|
1436
1458
|
return {
|
|
1437
|
-
rows
|
|
1459
|
+
rows,
|
|
1438
1460
|
rowsAffected: 0
|
|
1439
1461
|
};
|
|
1440
1462
|
}
|
|
@@ -1502,6 +1524,26 @@ var __webpack_exports__ = {};
|
|
|
1502
1524
|
}
|
|
1503
1525
|
}
|
|
1504
1526
|
const external_libsql_client_namespaceObject = require("@libsql/client");
|
|
1527
|
+
function libsql_parseJsonColumns(rows, jsonColumns) {
|
|
1528
|
+
if (!jsonColumns || 0 === jsonColumns.length) return rows;
|
|
1529
|
+
return rows.map((row)=>{
|
|
1530
|
+
const parsed = {
|
|
1531
|
+
...row
|
|
1532
|
+
};
|
|
1533
|
+
for (const col of jsonColumns)if (col in parsed && 'string' == typeof parsed[col]) try {
|
|
1534
|
+
parsed[col] = JSON.parse(parsed[col]);
|
|
1535
|
+
} catch {}
|
|
1536
|
+
return parsed;
|
|
1537
|
+
});
|
|
1538
|
+
}
|
|
1539
|
+
function libsql_stringifyJsonParams(params, stringifyParams) {
|
|
1540
|
+
if (!stringifyParams || 0 === Object.keys(stringifyParams).length) return params;
|
|
1541
|
+
const result = [
|
|
1542
|
+
...params
|
|
1543
|
+
];
|
|
1544
|
+
for (const [_columnName, index] of Object.entries(stringifyParams))if (index < result.length && null != result[index] && 'object' == typeof result[index]) result[index] = JSON.stringify(result[index]);
|
|
1545
|
+
return result;
|
|
1546
|
+
}
|
|
1505
1547
|
class LibSQLAdapter {
|
|
1506
1548
|
type = 'libsql';
|
|
1507
1549
|
config;
|
|
@@ -1559,10 +1601,10 @@ var __webpack_exports__ = {};
|
|
|
1559
1601
|
this.sql.set = set;
|
|
1560
1602
|
this.sql.where = sql_template_where;
|
|
1561
1603
|
}
|
|
1562
|
-
async query(sql, params = []) {
|
|
1563
|
-
return this.execute(sql, params);
|
|
1604
|
+
async query(sql, params = [], options) {
|
|
1605
|
+
return this.execute(sql, params, options);
|
|
1564
1606
|
}
|
|
1565
|
-
async execute(sql, params = []) {
|
|
1607
|
+
async execute(sql, params = [], options) {
|
|
1566
1608
|
try {
|
|
1567
1609
|
const target = this.txClient || this.client;
|
|
1568
1610
|
let query;
|
|
@@ -1571,23 +1613,29 @@ var __webpack_exports__ = {};
|
|
|
1571
1613
|
console.log('[LibSQL] Executing SQL:', sql);
|
|
1572
1614
|
console.log('[LibSQL] With params:', params);
|
|
1573
1615
|
}
|
|
1616
|
+
let args = params;
|
|
1617
|
+
if (options?.stringifyParams) args = libsql_stringifyJsonParams(args, options.stringifyParams);
|
|
1574
1618
|
query = {
|
|
1575
1619
|
sql,
|
|
1576
|
-
args
|
|
1620
|
+
args
|
|
1577
1621
|
};
|
|
1578
1622
|
} else {
|
|
1579
1623
|
if (process.env.DEBUG_SQL) {
|
|
1580
1624
|
console.log('[LibSQL] Executing SQL:', sql.sql);
|
|
1581
1625
|
console.log('[LibSQL] With args:', sql.args);
|
|
1582
1626
|
}
|
|
1627
|
+
let args = sql.args || [];
|
|
1628
|
+
if (options?.stringifyParams) args = libsql_stringifyJsonParams(args, options.stringifyParams);
|
|
1583
1629
|
query = {
|
|
1584
1630
|
sql: sql.sql,
|
|
1585
|
-
args
|
|
1631
|
+
args
|
|
1586
1632
|
};
|
|
1587
1633
|
}
|
|
1588
1634
|
const result = await target.execute(query);
|
|
1635
|
+
let rows = result.rows;
|
|
1636
|
+
if (options?.jsonColumns) rows = libsql_parseJsonColumns(rows, options.jsonColumns);
|
|
1589
1637
|
return {
|
|
1590
|
-
rows
|
|
1638
|
+
rows,
|
|
1591
1639
|
rowsAffected: Number(result.rowsAffected),
|
|
1592
1640
|
lastInsertRowid: result.lastInsertRowid ? BigInt(result.lastInsertRowid.toString()) : void 0
|
|
1593
1641
|
};
|
|
@@ -1629,6 +1677,26 @@ var __webpack_exports__ = {};
|
|
|
1629
1677
|
return createORM(this);
|
|
1630
1678
|
}
|
|
1631
1679
|
}
|
|
1680
|
+
function odblite_parseJsonColumns(rows, jsonColumns) {
|
|
1681
|
+
if (!jsonColumns || 0 === jsonColumns.length) return rows;
|
|
1682
|
+
return rows.map((row)=>{
|
|
1683
|
+
const parsed = {
|
|
1684
|
+
...row
|
|
1685
|
+
};
|
|
1686
|
+
for (const col of jsonColumns)if (col in parsed && 'string' == typeof parsed[col]) try {
|
|
1687
|
+
parsed[col] = JSON.parse(parsed[col]);
|
|
1688
|
+
} catch {}
|
|
1689
|
+
return parsed;
|
|
1690
|
+
});
|
|
1691
|
+
}
|
|
1692
|
+
function odblite_stringifyJsonParams(params, stringifyParams) {
|
|
1693
|
+
if (!stringifyParams || 0 === Object.keys(stringifyParams).length) return params;
|
|
1694
|
+
const result = [
|
|
1695
|
+
...params
|
|
1696
|
+
];
|
|
1697
|
+
for (const [_columnName, index] of Object.entries(stringifyParams))if (index < result.length && null != result[index] && 'object' == typeof result[index]) result[index] = JSON.stringify(result[index]);
|
|
1698
|
+
return result;
|
|
1699
|
+
}
|
|
1632
1700
|
class ODBLiteAdapter {
|
|
1633
1701
|
type = 'odblite';
|
|
1634
1702
|
config;
|
|
@@ -1715,32 +1783,42 @@ var __webpack_exports__ = {};
|
|
|
1715
1783
|
this.sql.set = set;
|
|
1716
1784
|
this.sql.where = sql_template_where;
|
|
1717
1785
|
}
|
|
1718
|
-
async query(sql, params = []) {
|
|
1719
|
-
return this.execute(sql, params);
|
|
1786
|
+
async query(sql, params = [], options) {
|
|
1787
|
+
return this.execute(sql, params, options);
|
|
1720
1788
|
}
|
|
1721
|
-
async execute(sql, params = []) {
|
|
1789
|
+
async execute(sql, params = [], options) {
|
|
1722
1790
|
try {
|
|
1791
|
+
let rows;
|
|
1792
|
+
let rowsAffected;
|
|
1793
|
+
let lastInsertRowid;
|
|
1723
1794
|
if ('object' == typeof sql) {
|
|
1724
1795
|
if (process.env.DEBUG_SQL) {
|
|
1725
1796
|
console.log('[ODBLite] Executing SQL:', sql.sql);
|
|
1726
1797
|
console.log('[ODBLite] With args:', sql.args);
|
|
1727
1798
|
}
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
}
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1799
|
+
let args = sql.args || [];
|
|
1800
|
+
if (options?.stringifyParams) args = odblite_stringifyJsonParams(args, options.stringifyParams);
|
|
1801
|
+
const result = await this.client.sql.execute(sql.sql, args);
|
|
1802
|
+
rows = result.rows;
|
|
1803
|
+
rowsAffected = result.rowsAffected || 0;
|
|
1804
|
+
lastInsertRowid = result.lastInsertRowid;
|
|
1805
|
+
} else {
|
|
1806
|
+
if (process.env.DEBUG_SQL) {
|
|
1807
|
+
console.log('[ODBLite] Executing SQL:', sql);
|
|
1808
|
+
console.log('[ODBLite] With params:', params);
|
|
1809
|
+
}
|
|
1810
|
+
let args = params;
|
|
1811
|
+
if (options?.stringifyParams) args = odblite_stringifyJsonParams(args, options.stringifyParams);
|
|
1812
|
+
const result = await this.client.sql.execute(sql, args);
|
|
1813
|
+
rows = result.rows;
|
|
1814
|
+
rowsAffected = result.rowsAffected || 0;
|
|
1815
|
+
lastInsertRowid = result.lastInsertRowid;
|
|
1738
1816
|
}
|
|
1739
|
-
|
|
1817
|
+
if (options?.jsonColumns) rows = odblite_parseJsonColumns(rows, options.jsonColumns);
|
|
1740
1818
|
return {
|
|
1741
|
-
rows
|
|
1742
|
-
rowsAffected
|
|
1743
|
-
lastInsertRowid
|
|
1819
|
+
rows,
|
|
1820
|
+
rowsAffected,
|
|
1821
|
+
lastInsertRowid
|
|
1744
1822
|
};
|
|
1745
1823
|
} catch (error) {
|
|
1746
1824
|
throw error;
|
package/dist/index.js
CHANGED
|
@@ -1289,6 +1289,26 @@ function sql_template_where(conditions) {
|
|
|
1289
1289
|
args: values
|
|
1290
1290
|
};
|
|
1291
1291
|
}
|
|
1292
|
+
function parseJsonColumns(rows, jsonColumns) {
|
|
1293
|
+
if (!jsonColumns || 0 === jsonColumns.length) return rows;
|
|
1294
|
+
return rows.map((row)=>{
|
|
1295
|
+
const parsed = {
|
|
1296
|
+
...row
|
|
1297
|
+
};
|
|
1298
|
+
for (const col of jsonColumns)if (col in parsed && 'string' == typeof parsed[col]) try {
|
|
1299
|
+
parsed[col] = JSON.parse(parsed[col]);
|
|
1300
|
+
} catch {}
|
|
1301
|
+
return parsed;
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
function stringifyJsonParams(params, stringifyParams) {
|
|
1305
|
+
if (!stringifyParams || 0 === Object.keys(stringifyParams).length) return params;
|
|
1306
|
+
const result = [
|
|
1307
|
+
...params
|
|
1308
|
+
];
|
|
1309
|
+
for (const [_columnName, index] of Object.entries(stringifyParams))if (index < result.length && null != result[index] && 'object' == typeof result[index]) result[index] = JSON.stringify(result[index]);
|
|
1310
|
+
return result;
|
|
1311
|
+
}
|
|
1292
1312
|
class BunSQLiteAdapter {
|
|
1293
1313
|
type = 'bun-sqlite';
|
|
1294
1314
|
config;
|
|
@@ -1346,10 +1366,10 @@ class BunSQLiteConnection {
|
|
|
1346
1366
|
this.sql.set = set;
|
|
1347
1367
|
this.sql.where = sql_template_where;
|
|
1348
1368
|
}
|
|
1349
|
-
async query(sql, params = []) {
|
|
1350
|
-
return this.execute(sql, params);
|
|
1369
|
+
async query(sql, params = [], options) {
|
|
1370
|
+
return this.execute(sql, params, options);
|
|
1351
1371
|
}
|
|
1352
|
-
async execute(sql, params = []) {
|
|
1372
|
+
async execute(sql, params = [], options) {
|
|
1353
1373
|
try {
|
|
1354
1374
|
let sqlStr;
|
|
1355
1375
|
let sqlParams;
|
|
@@ -1368,12 +1388,14 @@ class BunSQLiteConnection {
|
|
|
1368
1388
|
sqlStr = sql;
|
|
1369
1389
|
sqlParams = params;
|
|
1370
1390
|
}
|
|
1391
|
+
if (options?.stringifyParams) sqlParams = stringifyJsonParams(sqlParams, options.stringifyParams);
|
|
1371
1392
|
const stmt = this.db.query(sqlStr);
|
|
1372
1393
|
const isSelect = sqlStr.trim().toUpperCase().startsWith('SELECT') || sqlStr.trim().toUpperCase().includes('RETURNING');
|
|
1373
1394
|
if (isSelect) {
|
|
1374
|
-
|
|
1395
|
+
let rows = stmt.all(...sqlParams);
|
|
1396
|
+
if (options?.jsonColumns) rows = parseJsonColumns(rows, options.jsonColumns);
|
|
1375
1397
|
return {
|
|
1376
|
-
rows
|
|
1398
|
+
rows,
|
|
1377
1399
|
rowsAffected: 0
|
|
1378
1400
|
};
|
|
1379
1401
|
}
|
|
@@ -1440,6 +1462,26 @@ class BunSQLiteConnection {
|
|
|
1440
1462
|
return createORM(this);
|
|
1441
1463
|
}
|
|
1442
1464
|
}
|
|
1465
|
+
function libsql_parseJsonColumns(rows, jsonColumns) {
|
|
1466
|
+
if (!jsonColumns || 0 === jsonColumns.length) return rows;
|
|
1467
|
+
return rows.map((row)=>{
|
|
1468
|
+
const parsed = {
|
|
1469
|
+
...row
|
|
1470
|
+
};
|
|
1471
|
+
for (const col of jsonColumns)if (col in parsed && 'string' == typeof parsed[col]) try {
|
|
1472
|
+
parsed[col] = JSON.parse(parsed[col]);
|
|
1473
|
+
} catch {}
|
|
1474
|
+
return parsed;
|
|
1475
|
+
});
|
|
1476
|
+
}
|
|
1477
|
+
function libsql_stringifyJsonParams(params, stringifyParams) {
|
|
1478
|
+
if (!stringifyParams || 0 === Object.keys(stringifyParams).length) return params;
|
|
1479
|
+
const result = [
|
|
1480
|
+
...params
|
|
1481
|
+
];
|
|
1482
|
+
for (const [_columnName, index] of Object.entries(stringifyParams))if (index < result.length && null != result[index] && 'object' == typeof result[index]) result[index] = JSON.stringify(result[index]);
|
|
1483
|
+
return result;
|
|
1484
|
+
}
|
|
1443
1485
|
class LibSQLAdapter {
|
|
1444
1486
|
type = 'libsql';
|
|
1445
1487
|
config;
|
|
@@ -1497,10 +1539,10 @@ class LibSQLConnection {
|
|
|
1497
1539
|
this.sql.set = set;
|
|
1498
1540
|
this.sql.where = sql_template_where;
|
|
1499
1541
|
}
|
|
1500
|
-
async query(sql, params = []) {
|
|
1501
|
-
return this.execute(sql, params);
|
|
1542
|
+
async query(sql, params = [], options) {
|
|
1543
|
+
return this.execute(sql, params, options);
|
|
1502
1544
|
}
|
|
1503
|
-
async execute(sql, params = []) {
|
|
1545
|
+
async execute(sql, params = [], options) {
|
|
1504
1546
|
try {
|
|
1505
1547
|
const target = this.txClient || this.client;
|
|
1506
1548
|
let query;
|
|
@@ -1509,23 +1551,29 @@ class LibSQLConnection {
|
|
|
1509
1551
|
console.log('[LibSQL] Executing SQL:', sql);
|
|
1510
1552
|
console.log('[LibSQL] With params:', params);
|
|
1511
1553
|
}
|
|
1554
|
+
let args = params;
|
|
1555
|
+
if (options?.stringifyParams) args = libsql_stringifyJsonParams(args, options.stringifyParams);
|
|
1512
1556
|
query = {
|
|
1513
1557
|
sql,
|
|
1514
|
-
args
|
|
1558
|
+
args
|
|
1515
1559
|
};
|
|
1516
1560
|
} else {
|
|
1517
1561
|
if (process.env.DEBUG_SQL) {
|
|
1518
1562
|
console.log('[LibSQL] Executing SQL:', sql.sql);
|
|
1519
1563
|
console.log('[LibSQL] With args:', sql.args);
|
|
1520
1564
|
}
|
|
1565
|
+
let args = sql.args || [];
|
|
1566
|
+
if (options?.stringifyParams) args = libsql_stringifyJsonParams(args, options.stringifyParams);
|
|
1521
1567
|
query = {
|
|
1522
1568
|
sql: sql.sql,
|
|
1523
|
-
args
|
|
1569
|
+
args
|
|
1524
1570
|
};
|
|
1525
1571
|
}
|
|
1526
1572
|
const result = await target.execute(query);
|
|
1573
|
+
let rows = result.rows;
|
|
1574
|
+
if (options?.jsonColumns) rows = libsql_parseJsonColumns(rows, options.jsonColumns);
|
|
1527
1575
|
return {
|
|
1528
|
-
rows
|
|
1576
|
+
rows,
|
|
1529
1577
|
rowsAffected: Number(result.rowsAffected),
|
|
1530
1578
|
lastInsertRowid: result.lastInsertRowid ? BigInt(result.lastInsertRowid.toString()) : void 0
|
|
1531
1579
|
};
|
|
@@ -1567,6 +1615,26 @@ class LibSQLConnection {
|
|
|
1567
1615
|
return createORM(this);
|
|
1568
1616
|
}
|
|
1569
1617
|
}
|
|
1618
|
+
function odblite_parseJsonColumns(rows, jsonColumns) {
|
|
1619
|
+
if (!jsonColumns || 0 === jsonColumns.length) return rows;
|
|
1620
|
+
return rows.map((row)=>{
|
|
1621
|
+
const parsed = {
|
|
1622
|
+
...row
|
|
1623
|
+
};
|
|
1624
|
+
for (const col of jsonColumns)if (col in parsed && 'string' == typeof parsed[col]) try {
|
|
1625
|
+
parsed[col] = JSON.parse(parsed[col]);
|
|
1626
|
+
} catch {}
|
|
1627
|
+
return parsed;
|
|
1628
|
+
});
|
|
1629
|
+
}
|
|
1630
|
+
function odblite_stringifyJsonParams(params, stringifyParams) {
|
|
1631
|
+
if (!stringifyParams || 0 === Object.keys(stringifyParams).length) return params;
|
|
1632
|
+
const result = [
|
|
1633
|
+
...params
|
|
1634
|
+
];
|
|
1635
|
+
for (const [_columnName, index] of Object.entries(stringifyParams))if (index < result.length && null != result[index] && 'object' == typeof result[index]) result[index] = JSON.stringify(result[index]);
|
|
1636
|
+
return result;
|
|
1637
|
+
}
|
|
1570
1638
|
class ODBLiteAdapter {
|
|
1571
1639
|
type = 'odblite';
|
|
1572
1640
|
config;
|
|
@@ -1653,32 +1721,42 @@ class ODBLiteConnection {
|
|
|
1653
1721
|
this.sql.set = set;
|
|
1654
1722
|
this.sql.where = sql_template_where;
|
|
1655
1723
|
}
|
|
1656
|
-
async query(sql, params = []) {
|
|
1657
|
-
return this.execute(sql, params);
|
|
1724
|
+
async query(sql, params = [], options) {
|
|
1725
|
+
return this.execute(sql, params, options);
|
|
1658
1726
|
}
|
|
1659
|
-
async execute(sql, params = []) {
|
|
1727
|
+
async execute(sql, params = [], options) {
|
|
1660
1728
|
try {
|
|
1729
|
+
let rows;
|
|
1730
|
+
let rowsAffected;
|
|
1731
|
+
let lastInsertRowid;
|
|
1661
1732
|
if ('object' == typeof sql) {
|
|
1662
1733
|
if (process.env.DEBUG_SQL) {
|
|
1663
1734
|
console.log('[ODBLite] Executing SQL:', sql.sql);
|
|
1664
1735
|
console.log('[ODBLite] With args:', sql.args);
|
|
1665
1736
|
}
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
}
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1737
|
+
let args = sql.args || [];
|
|
1738
|
+
if (options?.stringifyParams) args = odblite_stringifyJsonParams(args, options.stringifyParams);
|
|
1739
|
+
const result = await this.client.sql.execute(sql.sql, args);
|
|
1740
|
+
rows = result.rows;
|
|
1741
|
+
rowsAffected = result.rowsAffected || 0;
|
|
1742
|
+
lastInsertRowid = result.lastInsertRowid;
|
|
1743
|
+
} else {
|
|
1744
|
+
if (process.env.DEBUG_SQL) {
|
|
1745
|
+
console.log('[ODBLite] Executing SQL:', sql);
|
|
1746
|
+
console.log('[ODBLite] With params:', params);
|
|
1747
|
+
}
|
|
1748
|
+
let args = params;
|
|
1749
|
+
if (options?.stringifyParams) args = odblite_stringifyJsonParams(args, options.stringifyParams);
|
|
1750
|
+
const result = await this.client.sql.execute(sql, args);
|
|
1751
|
+
rows = result.rows;
|
|
1752
|
+
rowsAffected = result.rowsAffected || 0;
|
|
1753
|
+
lastInsertRowid = result.lastInsertRowid;
|
|
1754
|
+
}
|
|
1755
|
+
if (options?.jsonColumns) rows = odblite_parseJsonColumns(rows, options.jsonColumns);
|
|
1678
1756
|
return {
|
|
1679
|
-
rows
|
|
1680
|
-
rowsAffected
|
|
1681
|
-
lastInsertRowid
|
|
1757
|
+
rows,
|
|
1758
|
+
rowsAffected,
|
|
1759
|
+
lastInsertRowid
|
|
1682
1760
|
};
|
|
1683
1761
|
} catch (error) {
|
|
1684
1762
|
throw error;
|
package/package.json
CHANGED
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
QueryResult,
|
|
6
6
|
PreparedStatement,
|
|
7
7
|
BunSQLiteConfig,
|
|
8
|
+
QueryOptions,
|
|
8
9
|
} from '../types'
|
|
9
10
|
import {
|
|
10
11
|
convertTemplateToQuery,
|
|
@@ -17,6 +18,48 @@ import {
|
|
|
17
18
|
where
|
|
18
19
|
} from '../sql-template'
|
|
19
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Parse JSON columns in query results
|
|
23
|
+
* Only parses if the value is a string (to avoid double-parsing)
|
|
24
|
+
*/
|
|
25
|
+
function parseJsonColumns<T = any>(rows: any[], jsonColumns?: string[]): T[] {
|
|
26
|
+
if (!jsonColumns || jsonColumns.length === 0) {
|
|
27
|
+
return rows
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return rows.map(row => {
|
|
31
|
+
const parsed = { ...row }
|
|
32
|
+
for (const col of jsonColumns) {
|
|
33
|
+
if (col in parsed && typeof parsed[col] === 'string') {
|
|
34
|
+
try {
|
|
35
|
+
parsed[col] = JSON.parse(parsed[col])
|
|
36
|
+
} catch {
|
|
37
|
+
// Keep original value if parsing fails
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return parsed
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Stringify JSON parameters for INSERT/UPDATE queries
|
|
47
|
+
* Only stringifies if the value is an object/array (not already a string)
|
|
48
|
+
*/
|
|
49
|
+
function stringifyJsonParams(params: any[], stringifyParams?: Record<string, number>): any[] {
|
|
50
|
+
if (!stringifyParams || Object.keys(stringifyParams).length === 0) {
|
|
51
|
+
return params
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const result = [...params]
|
|
55
|
+
for (const [_columnName, index] of Object.entries(stringifyParams)) {
|
|
56
|
+
if (index < result.length && result[index] != null && typeof result[index] === 'object') {
|
|
57
|
+
result[index] = JSON.stringify(result[index])
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return result
|
|
61
|
+
}
|
|
62
|
+
|
|
20
63
|
/**
|
|
21
64
|
* Bun SQLite adapter for DatabaseManager
|
|
22
65
|
* Wraps bun:sqlite with Connection interface
|
|
@@ -117,14 +160,14 @@ class BunSQLiteConnection implements Connection {
|
|
|
117
160
|
/**
|
|
118
161
|
* Query with SQL string and parameters (alias for execute)
|
|
119
162
|
*/
|
|
120
|
-
async query<T = any>(sql: string, params: any[] = []): Promise<QueryResult<T>> {
|
|
121
|
-
return this.execute(sql, params) as Promise<QueryResult<T>>
|
|
163
|
+
async query<T = any>(sql: string, params: any[] = [], options?: QueryOptions): Promise<QueryResult<T>> {
|
|
164
|
+
return this.execute(sql, params, options) as Promise<QueryResult<T>>
|
|
122
165
|
}
|
|
123
166
|
|
|
124
167
|
/**
|
|
125
168
|
* Execute SQL with parameters
|
|
126
169
|
*/
|
|
127
|
-
async execute(sql: string | { sql: string; args?: any[] }, params: any[] = []): Promise<QueryResult> {
|
|
170
|
+
async execute(sql: string | { sql: string; args?: any[] }, params: any[] = [], options?: QueryOptions): Promise<QueryResult> {
|
|
128
171
|
try {
|
|
129
172
|
// Handle object format { sql, args }
|
|
130
173
|
let sqlStr: string
|
|
@@ -147,14 +190,25 @@ class BunSQLiteConnection implements Connection {
|
|
|
147
190
|
sqlParams = params
|
|
148
191
|
}
|
|
149
192
|
|
|
193
|
+
// Stringify JSON parameters if specified
|
|
194
|
+
if (options?.stringifyParams) {
|
|
195
|
+
sqlParams = stringifyJsonParams(sqlParams, options.stringifyParams)
|
|
196
|
+
}
|
|
197
|
+
|
|
150
198
|
const stmt = this.db.query(sqlStr)
|
|
151
199
|
const isSelect = sqlStr.trim().toUpperCase().startsWith('SELECT') ||
|
|
152
200
|
sqlStr.trim().toUpperCase().includes('RETURNING')
|
|
153
201
|
|
|
154
202
|
if (isSelect) {
|
|
155
|
-
|
|
203
|
+
let rows = stmt.all(...sqlParams) as any[]
|
|
204
|
+
|
|
205
|
+
// Parse JSON columns if specified
|
|
206
|
+
if (options?.jsonColumns) {
|
|
207
|
+
rows = parseJsonColumns(rows, options.jsonColumns)
|
|
208
|
+
}
|
|
209
|
+
|
|
156
210
|
return {
|
|
157
|
-
rows
|
|
211
|
+
rows,
|
|
158
212
|
rowsAffected: 0,
|
|
159
213
|
}
|
|
160
214
|
} else {
|
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
QueryResult,
|
|
6
6
|
PreparedStatement,
|
|
7
7
|
LibSQLConfig,
|
|
8
|
+
QueryOptions,
|
|
8
9
|
} from '../types'
|
|
9
10
|
import {
|
|
10
11
|
convertTemplateToQuery,
|
|
@@ -17,6 +18,48 @@ import {
|
|
|
17
18
|
where
|
|
18
19
|
} from '../sql-template'
|
|
19
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Parse JSON columns in query results
|
|
23
|
+
* Only parses if the value is a string (to avoid double-parsing)
|
|
24
|
+
*/
|
|
25
|
+
function parseJsonColumns<T = any>(rows: any[], jsonColumns?: string[]): T[] {
|
|
26
|
+
if (!jsonColumns || jsonColumns.length === 0) {
|
|
27
|
+
return rows
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return rows.map(row => {
|
|
31
|
+
const parsed = { ...row }
|
|
32
|
+
for (const col of jsonColumns) {
|
|
33
|
+
if (col in parsed && typeof parsed[col] === 'string') {
|
|
34
|
+
try {
|
|
35
|
+
parsed[col] = JSON.parse(parsed[col])
|
|
36
|
+
} catch {
|
|
37
|
+
// Keep original value if parsing fails
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return parsed
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Stringify JSON parameters for INSERT/UPDATE queries
|
|
47
|
+
* Only stringifies if the value is an object/array (not already a string)
|
|
48
|
+
*/
|
|
49
|
+
function stringifyJsonParams(params: any[], stringifyParams?: Record<string, number>): any[] {
|
|
50
|
+
if (!stringifyParams || Object.keys(stringifyParams).length === 0) {
|
|
51
|
+
return params
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const result = [...params]
|
|
55
|
+
for (const [_columnName, index] of Object.entries(stringifyParams)) {
|
|
56
|
+
if (index < result.length && result[index] != null && typeof result[index] === 'object') {
|
|
57
|
+
result[index] = JSON.stringify(result[index])
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return result
|
|
61
|
+
}
|
|
62
|
+
|
|
20
63
|
/**
|
|
21
64
|
* LibSQL adapter for DatabaseManager
|
|
22
65
|
* Wraps @libsql/client with Connection interface
|
|
@@ -117,15 +160,15 @@ class LibSQLConnection implements Connection {
|
|
|
117
160
|
/**
|
|
118
161
|
* Query with SQL string and parameters (alias for execute)
|
|
119
162
|
*/
|
|
120
|
-
async query<T = any>(sql: string, params: any[] = []): Promise<QueryResult<T>> {
|
|
121
|
-
return this.execute(sql, params) as Promise<QueryResult<T>>
|
|
163
|
+
async query<T = any>(sql: string, params: any[] = [], options?: QueryOptions): Promise<QueryResult<T>> {
|
|
164
|
+
return this.execute(sql, params, options) as Promise<QueryResult<T>>
|
|
122
165
|
}
|
|
123
166
|
|
|
124
167
|
/**
|
|
125
168
|
* Execute SQL with parameters
|
|
126
169
|
* Supports both formats: execute(sql, params) and execute({sql, args})
|
|
127
170
|
*/
|
|
128
|
-
async execute(sql: string | { sql: string; args?: any[] }, params: any[] = []): Promise<QueryResult> {
|
|
171
|
+
async execute(sql: string | { sql: string; args?: any[] }, params: any[] = [], options?: QueryOptions): Promise<QueryResult> {
|
|
129
172
|
try {
|
|
130
173
|
const target = this.txClient || this.client
|
|
131
174
|
|
|
@@ -137,19 +180,36 @@ class LibSQLConnection implements Connection {
|
|
|
137
180
|
console.log('[LibSQL] Executing SQL:', sql)
|
|
138
181
|
console.log('[LibSQL] With params:', params)
|
|
139
182
|
}
|
|
140
|
-
|
|
183
|
+
let args = params
|
|
184
|
+
// Stringify JSON parameters if specified
|
|
185
|
+
if (options?.stringifyParams) {
|
|
186
|
+
args = stringifyJsonParams(args, options.stringifyParams)
|
|
187
|
+
}
|
|
188
|
+
query = { sql, args }
|
|
141
189
|
} else {
|
|
142
190
|
if (process.env.DEBUG_SQL) {
|
|
143
191
|
console.log('[LibSQL] Executing SQL:', sql.sql)
|
|
144
192
|
console.log('[LibSQL] With args:', sql.args)
|
|
145
193
|
}
|
|
146
|
-
|
|
194
|
+
let args = sql.args || []
|
|
195
|
+
// Stringify JSON parameters if specified
|
|
196
|
+
if (options?.stringifyParams) {
|
|
197
|
+
args = stringifyJsonParams(args, options.stringifyParams)
|
|
198
|
+
}
|
|
199
|
+
query = { sql: sql.sql, args }
|
|
147
200
|
}
|
|
148
201
|
|
|
149
202
|
const result = await target.execute(query)
|
|
150
203
|
|
|
204
|
+
let rows = result.rows as any[]
|
|
205
|
+
|
|
206
|
+
// Parse JSON columns if specified
|
|
207
|
+
if (options?.jsonColumns) {
|
|
208
|
+
rows = parseJsonColumns(rows, options.jsonColumns)
|
|
209
|
+
}
|
|
210
|
+
|
|
151
211
|
return {
|
|
152
|
-
rows
|
|
212
|
+
rows,
|
|
153
213
|
rowsAffected: Number(result.rowsAffected),
|
|
154
214
|
lastInsertRowid: result.lastInsertRowid
|
|
155
215
|
? BigInt(result.lastInsertRowid.toString())
|
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
QueryResult,
|
|
7
7
|
PreparedStatement,
|
|
8
8
|
ODBLiteConfig,
|
|
9
|
+
QueryOptions,
|
|
9
10
|
} from '../types'
|
|
10
11
|
import {
|
|
11
12
|
convertTemplateToQuery,
|
|
@@ -18,6 +19,48 @@ import {
|
|
|
18
19
|
where
|
|
19
20
|
} from '../sql-template'
|
|
20
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Parse JSON columns in query results
|
|
24
|
+
* Only parses if the value is a string (to avoid double-parsing)
|
|
25
|
+
*/
|
|
26
|
+
function parseJsonColumns<T = any>(rows: any[], jsonColumns?: string[]): T[] {
|
|
27
|
+
if (!jsonColumns || jsonColumns.length === 0) {
|
|
28
|
+
return rows
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return rows.map(row => {
|
|
32
|
+
const parsed = { ...row }
|
|
33
|
+
for (const col of jsonColumns) {
|
|
34
|
+
if (col in parsed && typeof parsed[col] === 'string') {
|
|
35
|
+
try {
|
|
36
|
+
parsed[col] = JSON.parse(parsed[col])
|
|
37
|
+
} catch {
|
|
38
|
+
// Keep original value if parsing fails
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return parsed
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Stringify JSON parameters for INSERT/UPDATE queries
|
|
48
|
+
* Only stringifies if the value is an object/array (not already a string)
|
|
49
|
+
*/
|
|
50
|
+
function stringifyJsonParams(params: any[], stringifyParams?: Record<string, number>): any[] {
|
|
51
|
+
if (!stringifyParams || Object.keys(stringifyParams).length === 0) {
|
|
52
|
+
return params
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const result = [...params]
|
|
56
|
+
for (const [_columnName, index] of Object.entries(stringifyParams)) {
|
|
57
|
+
if (index < result.length && result[index] != null && typeof result[index] === 'object') {
|
|
58
|
+
result[index] = JSON.stringify(result[index])
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return result
|
|
62
|
+
}
|
|
63
|
+
|
|
21
64
|
/**
|
|
22
65
|
* ODB-Lite adapter for DatabaseManager
|
|
23
66
|
* Wraps ServiceClient and ODBLiteClient with Connection interface
|
|
@@ -162,15 +205,19 @@ class ODBLiteConnection implements Connection {
|
|
|
162
205
|
/**
|
|
163
206
|
* Query with SQL string and parameters (alias for execute)
|
|
164
207
|
*/
|
|
165
|
-
async query<T = any>(sql: string, params: any[] = []): Promise<QueryResult<T>> {
|
|
166
|
-
return this.execute(sql, params) as Promise<QueryResult<T>>
|
|
208
|
+
async query<T = any>(sql: string, params: any[] = [], options?: QueryOptions): Promise<QueryResult<T>> {
|
|
209
|
+
return this.execute(sql, params, options) as Promise<QueryResult<T>>
|
|
167
210
|
}
|
|
168
211
|
|
|
169
212
|
/**
|
|
170
213
|
* Execute SQL with parameters
|
|
171
214
|
*/
|
|
172
|
-
async execute(sql: string | { sql: string; args?: any[] }, params: any[] = []): Promise<QueryResult> {
|
|
215
|
+
async execute(sql: string | { sql: string; args?: any[] }, params: any[] = [], options?: QueryOptions): Promise<QueryResult> {
|
|
173
216
|
try {
|
|
217
|
+
let rows: any[]
|
|
218
|
+
let rowsAffected: number
|
|
219
|
+
let lastInsertRowid: any
|
|
220
|
+
|
|
174
221
|
// Handle object format { sql, args }
|
|
175
222
|
if (typeof sql === 'object') {
|
|
176
223
|
// Debug logging for SQL errors
|
|
@@ -178,25 +225,41 @@ class ODBLiteConnection implements Connection {
|
|
|
178
225
|
console.log('[ODBLite] Executing SQL:', sql.sql)
|
|
179
226
|
console.log('[ODBLite] With args:', sql.args)
|
|
180
227
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
228
|
+
let args = sql.args || []
|
|
229
|
+
// Stringify JSON parameters if specified
|
|
230
|
+
if (options?.stringifyParams) {
|
|
231
|
+
args = stringifyJsonParams(args, options.stringifyParams)
|
|
232
|
+
}
|
|
233
|
+
const result = await this.client.sql.execute(sql.sql, args)
|
|
234
|
+
rows = result.rows
|
|
235
|
+
rowsAffected = result.rowsAffected || 0
|
|
236
|
+
lastInsertRowid = (result as any).lastInsertRowid
|
|
237
|
+
} else {
|
|
238
|
+
// Handle string format
|
|
239
|
+
if (process.env.DEBUG_SQL) {
|
|
240
|
+
console.log('[ODBLite] Executing SQL:', sql)
|
|
241
|
+
console.log('[ODBLite] With params:', params)
|
|
242
|
+
}
|
|
243
|
+
let args = params
|
|
244
|
+
// Stringify JSON parameters if specified
|
|
245
|
+
if (options?.stringifyParams) {
|
|
246
|
+
args = stringifyJsonParams(args, options.stringifyParams)
|
|
186
247
|
}
|
|
248
|
+
const result = await this.client.sql.execute(sql, args)
|
|
249
|
+
rows = result.rows
|
|
250
|
+
rowsAffected = result.rowsAffected || 0
|
|
251
|
+
lastInsertRowid = (result as any).lastInsertRowid
|
|
187
252
|
}
|
|
188
253
|
|
|
189
|
-
//
|
|
190
|
-
if (
|
|
191
|
-
|
|
192
|
-
console.log('[ODBLite] With params:', params)
|
|
254
|
+
// Parse JSON columns if specified
|
|
255
|
+
if (options?.jsonColumns) {
|
|
256
|
+
rows = parseJsonColumns(rows, options.jsonColumns)
|
|
193
257
|
}
|
|
194
|
-
const result = await this.client.sql.execute(sql, params)
|
|
195
258
|
|
|
196
259
|
return {
|
|
197
|
-
rows
|
|
198
|
-
rowsAffected
|
|
199
|
-
lastInsertRowid
|
|
260
|
+
rows,
|
|
261
|
+
rowsAffected,
|
|
262
|
+
lastInsertRowid,
|
|
200
263
|
}
|
|
201
264
|
} catch (error: any) {
|
|
202
265
|
// Re-throw the original error to let the application handle it
|
package/src/database/types.ts
CHANGED
|
@@ -25,6 +25,32 @@ export interface QueryResult<T = any> {
|
|
|
25
25
|
lastInsertRowid?: number | bigint
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Query options for fine-grained control
|
|
30
|
+
*/
|
|
31
|
+
export interface QueryOptions {
|
|
32
|
+
/**
|
|
33
|
+
* List of column names that should be auto-parsed as JSON when reading
|
|
34
|
+
* Only applies to columns that contain stringified JSON in SELECT queries
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* await conn.query('SELECT * FROM suppliers', [], { jsonColumns: ['connections', 'config'] })
|
|
38
|
+
*/
|
|
39
|
+
jsonColumns?: string[]
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Automatically stringify parameters for JSON columns in UPDATE/INSERT queries
|
|
43
|
+
* Maps column names to parameter indices (0-based)
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* // UPDATE suppliers SET connections = ?, updated_at = ? WHERE id = ?
|
|
47
|
+
* await conn.execute(query, [connections, now, id], {
|
|
48
|
+
* stringifyParams: { connections: 0 } // Auto-stringify first parameter
|
|
49
|
+
* })
|
|
50
|
+
*/
|
|
51
|
+
stringifyParams?: Record<string, number>
|
|
52
|
+
}
|
|
53
|
+
|
|
28
54
|
/**
|
|
29
55
|
* Unified database connection interface
|
|
30
56
|
* All backends implement this through adapters
|
|
@@ -43,8 +69,8 @@ export interface Connection {
|
|
|
43
69
|
sql(value: any, ...keys: string[]): any
|
|
44
70
|
|
|
45
71
|
// Standard query methods (return QueryResult)
|
|
46
|
-
query<T = any>(sql: string, params?: any[]): Promise<QueryResult<T>>
|
|
47
|
-
execute(sql: string | { sql: string; args?: any[] }, params?: any[]): Promise<QueryResult>
|
|
72
|
+
query<T = any>(sql: string, params?: any[], options?: QueryOptions): Promise<QueryResult<T>>
|
|
73
|
+
execute(sql: string | { sql: string; args?: any[] }, params?: any[], options?: QueryOptions): Promise<QueryResult>
|
|
48
74
|
prepare(sql: string): PreparedStatement
|
|
49
75
|
|
|
50
76
|
// Transaction support
|