@openfn/language-motherduck 1.0.0

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/index.js ADDED
@@ -0,0 +1,512 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // src/Adaptor.js
8
+ var Adaptor_exports = {};
9
+ __export(Adaptor_exports, {
10
+ alterState: () => alterState,
11
+ arrayToString: () => arrayToString,
12
+ as: () => as,
13
+ combine: () => combine,
14
+ cursor: () => cursor,
15
+ dataPath: () => dataPath,
16
+ dataValue: () => dataValue,
17
+ dateFns: () => dateFns,
18
+ each: () => each,
19
+ execute: () => execute,
20
+ field: () => field,
21
+ fields: () => fields,
22
+ fn: () => fn,
23
+ fnIf: () => fnIf,
24
+ group: () => group,
25
+ insert: () => insert,
26
+ lastReferenceValue: () => lastReferenceValue,
27
+ map: () => map,
28
+ merge: () => merge,
29
+ query: () => query,
30
+ sourceValue: () => sourceValue,
31
+ util: () => util_exports
32
+ });
33
+ import {
34
+ execute as commonExecute,
35
+ composeNextState
36
+ } from "@openfn/language-common";
37
+ import { expandReferences } from "@openfn/language-common/util";
38
+ import { DuckDBInstance } from "@duckdb/node-api";
39
+ import _ from "lodash";
40
+
41
+ // src/util.js
42
+ var util_exports = {};
43
+ __export(util_exports, {
44
+ convertBigIntToNumber: () => convertBigIntToNumber,
45
+ escapeSqlString: () => escapeSqlString,
46
+ formatSqlValue: () => formatSqlValue,
47
+ queryHandler: () => queryHandler,
48
+ validateSqlIdentifier: () => validateSqlIdentifier
49
+ });
50
+ function validateSqlIdentifier(identifier) {
51
+ if (typeof identifier !== "string") {
52
+ throw new Error("SQL identifier must be a string");
53
+ }
54
+ if (!/^[a-zA-Z_][a-zA-Z0-9_.]*$/.test(identifier)) {
55
+ throw new Error(`Invalid SQL identifier: ${identifier}. Only alphanumeric characters, underscores, and dots are allowed.`);
56
+ }
57
+ const upperIdentifier = identifier.toUpperCase();
58
+ const dangerousPatterns = [
59
+ "DROP",
60
+ "DELETE",
61
+ "INSERT",
62
+ "UPDATE",
63
+ "ALTER",
64
+ "CREATE",
65
+ "EXEC",
66
+ "UNION",
67
+ "SELECT",
68
+ "--",
69
+ "/*",
70
+ "*/",
71
+ ";"
72
+ ];
73
+ for (const pattern of dangerousPatterns) {
74
+ if (upperIdentifier.includes(pattern)) {
75
+ throw new Error(`SQL identifier contains forbidden pattern: ${pattern}`);
76
+ }
77
+ }
78
+ return identifier;
79
+ }
80
+ function escapeSqlString(value) {
81
+ if (typeof value !== "string") {
82
+ return value;
83
+ }
84
+ return value.replace(/'/g, "''");
85
+ }
86
+ function formatSqlValue(value) {
87
+ if (value === null || value === void 0) {
88
+ return "NULL";
89
+ }
90
+ if (typeof value === "string") {
91
+ return `'${escapeSqlString(value)}'`;
92
+ }
93
+ if (typeof value === "boolean") {
94
+ return value ? "TRUE" : "FALSE";
95
+ }
96
+ return value.toString();
97
+ }
98
+ function convertBigIntToNumber(obj) {
99
+ if (typeof obj === "bigint") {
100
+ if (obj <= Number.MAX_SAFE_INTEGER && obj >= Number.MIN_SAFE_INTEGER) {
101
+ return Number(obj);
102
+ } else {
103
+ return obj.toString();
104
+ }
105
+ }
106
+ if (Array.isArray(obj)) {
107
+ return obj.map(convertBigIntToNumber);
108
+ }
109
+ if (obj !== null && typeof obj === "object") {
110
+ if (obj.width !== void 0 && obj.scale !== void 0 && obj.value !== void 0) {
111
+ const scale = Number(obj.scale);
112
+ const value = typeof obj.value === "bigint" ? Number(obj.value) : obj.value;
113
+ return value / Math.pow(10, scale);
114
+ }
115
+ const converted = {};
116
+ for (const [key, value] of Object.entries(obj)) {
117
+ converted[key] = convertBigIntToNumber(value);
118
+ }
119
+ return converted;
120
+ }
121
+ return obj;
122
+ }
123
+ async function queryHandler(connection2, state, sqlQuery, options, composeNextState2) {
124
+ if (!connection2) {
125
+ throw new Error("No active MotherDuck connection found. Ensure you are running within an execute() block.");
126
+ }
127
+ try {
128
+ const result = await connection2.runAndReadAll(sqlQuery);
129
+ const rawRows = result.getRowObjects();
130
+ const rows = convertBigIntToNumber(rawRows);
131
+ const nextState = {
132
+ ...composeNextState2(state, rows),
133
+ response: {
134
+ rows,
135
+ rowCount: rows.length,
136
+ command: sqlQuery.trim().split(" ")[0].toUpperCase(),
137
+ query: options.writeSql ? sqlQuery : "[query hidden]"
138
+ }
139
+ };
140
+ return nextState;
141
+ } catch (error) {
142
+ const errorMessage = `MotherDuck query failed: ${error.message}`;
143
+ console.error(errorMessage);
144
+ console.error("Failed query:", sqlQuery.substring(0, 200) + (sqlQuery.length > 200 ? "..." : ""));
145
+ const enhancedError = new Error(errorMessage);
146
+ enhancedError.originalError = error;
147
+ enhancedError.query = sqlQuery;
148
+ throw enhancedError;
149
+ }
150
+ }
151
+
152
+ // src/mock.js
153
+ var mockTables = {};
154
+ function parseSelectLiterals(sql) {
155
+ const result = {};
156
+ const selectMatch = sql.match(/SELECT\s+(.+?)(?:\s+FROM|\s*$)/is);
157
+ if (!selectMatch)
158
+ return result;
159
+ const selectClause = selectMatch[1].trim();
160
+ const columns = [];
161
+ let current = "";
162
+ let inQuotes = false;
163
+ let parenDepth = 0;
164
+ for (let i = 0; i < selectClause.length; i++) {
165
+ const char = selectClause[i];
166
+ if (char === "'" && (i === 0 || selectClause[i - 1] !== "\\")) {
167
+ inQuotes = !inQuotes;
168
+ }
169
+ if (!inQuotes) {
170
+ if (char === "(")
171
+ parenDepth++;
172
+ if (char === ")")
173
+ parenDepth--;
174
+ if (char === "," && parenDepth === 0) {
175
+ columns.push(current.trim());
176
+ current = "";
177
+ continue;
178
+ }
179
+ }
180
+ current += char;
181
+ }
182
+ if (current.trim())
183
+ columns.push(current.trim());
184
+ columns.forEach((col) => {
185
+ var _a;
186
+ const stringMatch = col.match(/'([^']*)'\s+(?:as\s+)?(\w+)/i);
187
+ const numberMatch = col.match(/^(\d+)\s+(?:as\s+)?(\w+)/i);
188
+ const funcMatch = col.match(/^(\w+)\s*\([^)]*\)\s+(?:as\s+)?(\w+)/i);
189
+ if (stringMatch) {
190
+ result[stringMatch[2]] = stringMatch[1];
191
+ } else if (numberMatch) {
192
+ result[numberMatch[2]] = parseInt(numberMatch[1]);
193
+ } else if (funcMatch) {
194
+ const funcName = funcMatch[1].toLowerCase();
195
+ const alias = funcMatch[2];
196
+ if (funcName === "count") {
197
+ result[alias] = ((_a = mockTables[Object.keys(mockTables)[0]]) == null ? void 0 : _a.length) || 0;
198
+ } else if (funcName === "current_database") {
199
+ result[alias] = "my_db";
200
+ }
201
+ }
202
+ });
203
+ return result;
204
+ }
205
+ function createMockConnection() {
206
+ const mockConnection = {
207
+ runAndReadAll: async (sql) => {
208
+ if (/SELECT\s+FROM/i.test(sql) || /WHERE\s+INVALID/i.test(sql)) {
209
+ throw new Error('Parser Error: syntax error at or near "FROM"');
210
+ }
211
+ let mockData = [];
212
+ if (/CREATE TABLE/i.test(sql)) {
213
+ const tableMatch = sql.match(/CREATE TABLE\s+(\w+)/i);
214
+ if (tableMatch) {
215
+ const tableName = tableMatch[1];
216
+ mockTables[tableName] = [];
217
+ }
218
+ mockData = [{ success: true }];
219
+ } else if (/INSERT INTO/i.test(sql)) {
220
+ const tableMatch = sql.match(/INSERT INTO\s+(\w+)/i);
221
+ const valuesMatch = sql.match(/VALUES\s+(.+)/is);
222
+ if (tableMatch && valuesMatch) {
223
+ const tableName = tableMatch[1];
224
+ if (!mockTables[tableName])
225
+ mockTables[tableName] = [];
226
+ const columnsMatch = sql.match(/\(([^)]+)\)\s+VALUES/i);
227
+ const columns = columnsMatch ? columnsMatch[1].split(",").map((c) => c.trim()) : [];
228
+ const valuesSets = valuesMatch[1].match(/\([^)]+\)/g) || [];
229
+ valuesSets.forEach((valueSet) => {
230
+ const values = valueSet.slice(1, -1).split(",").map((v) => {
231
+ v = v.trim();
232
+ if (v.startsWith("'") && v.endsWith("'")) {
233
+ return v.slice(1, -1).replace(/''/g, "'");
234
+ }
235
+ if (v === "NULL")
236
+ return null;
237
+ if (v === "TRUE")
238
+ return true;
239
+ if (v === "FALSE")
240
+ return false;
241
+ if (/^\d+$/.test(v))
242
+ return parseInt(v);
243
+ return v;
244
+ });
245
+ const row = {};
246
+ columns.forEach((col, idx) => {
247
+ row[col] = values[idx];
248
+ });
249
+ mockTables[tableName].push(row);
250
+ });
251
+ }
252
+ mockData = [{ success: true }];
253
+ } else if (/SELECT/i.test(sql)) {
254
+ const fromMatch = sql.match(/FROM\s+(\w+)/i);
255
+ if (fromMatch) {
256
+ const tableName = fromMatch[1];
257
+ const tableData = mockTables[tableName] || [];
258
+ if (/COUNT\s*\(\s*\*\s*\)/i.test(sql)) {
259
+ const aliasMatch = sql.match(/COUNT\s*\(\s*\*\s*\)\s+(?:as\s+)?(\w+)/i);
260
+ const alias = aliasMatch ? aliasMatch[1] : "count";
261
+ mockData = [{ [alias]: tableData.length }];
262
+ } else if (/SELECT\s+\*/i.test(sql)) {
263
+ mockData = [...tableData];
264
+ } else {
265
+ const selectMatch = sql.match(/SELECT\s+(.+?)\s+FROM/is);
266
+ if (selectMatch) {
267
+ const columns = selectMatch[1].split(",").map((c) => c.trim());
268
+ mockData = tableData.map((row) => {
269
+ const newRow = {};
270
+ columns.forEach((col) => {
271
+ if (row[col] !== void 0) {
272
+ newRow[col] = row[col];
273
+ }
274
+ });
275
+ return newRow;
276
+ });
277
+ }
278
+ }
279
+ } else {
280
+ const literals = parseSelectLiterals(sql);
281
+ if (Object.keys(literals).length === 0) {
282
+ mockData = [];
283
+ } else {
284
+ mockData = [literals];
285
+ }
286
+ }
287
+ } else if (/UPDATE/i.test(sql)) {
288
+ mockData = [{ success: true }];
289
+ } else if (/DELETE/i.test(sql)) {
290
+ mockData = [{ success: true }];
291
+ } else if (/DROP TABLE/i.test(sql)) {
292
+ const tableMatch = sql.match(/DROP TABLE\s+(?:IF EXISTS\s+)?(\w+)/i);
293
+ if (tableMatch) {
294
+ delete mockTables[tableMatch[1]];
295
+ }
296
+ mockData = [{ success: true }];
297
+ } else {
298
+ mockData = [{ result: "mock_data" }];
299
+ }
300
+ return {
301
+ getRowObjects: () => mockData
302
+ };
303
+ },
304
+ close: () => {
305
+ Object.keys(mockTables).forEach((key) => delete mockTables[key]);
306
+ }
307
+ };
308
+ const mockInstance = {
309
+ connect: async () => mockConnection,
310
+ close: () => {
311
+ Object.keys(mockTables).forEach((key) => delete mockTables[key]);
312
+ }
313
+ };
314
+ return { mockInstance, mockConnection };
315
+ }
316
+
317
+ // src/Adaptor.js
318
+ import {
319
+ alterState,
320
+ arrayToString,
321
+ combine,
322
+ cursor,
323
+ dataPath,
324
+ dataValue,
325
+ dateFns,
326
+ each,
327
+ field,
328
+ fields,
329
+ fn,
330
+ fnIf,
331
+ group,
332
+ lastReferenceValue,
333
+ map,
334
+ merge,
335
+ sourceValue,
336
+ as
337
+ } from "@openfn/language-common";
338
+ var instance = null;
339
+ var connection = null;
340
+ function execute(...operations) {
341
+ const initialState = {
342
+ references: [],
343
+ data: null
344
+ };
345
+ return async (state) => {
346
+ try {
347
+ return await commonExecute(
348
+ createConnection,
349
+ ...operations,
350
+ disconnect
351
+ )({ ...initialState, ...state });
352
+ } catch (error) {
353
+ disconnect(state);
354
+ throw error;
355
+ }
356
+ };
357
+ }
358
+ async function createConnection(state) {
359
+ const {
360
+ token,
361
+ database,
362
+ sessionHint,
363
+ testMode = false
364
+ } = state.configuration;
365
+ if (testMode || process.env.NODE_ENV === "test") {
366
+ const { mockInstance, mockConnection } = createMockConnection();
367
+ instance = mockInstance;
368
+ connection = mockConnection;
369
+ return state;
370
+ }
371
+ if (!token) {
372
+ throw new Error(
373
+ "MotherDuck token is required. Please provide a token in the configuration."
374
+ );
375
+ }
376
+ let databasePath = `md:${database}`;
377
+ if (sessionHint) {
378
+ databasePath += `?session_hint=${sessionHint}`;
379
+ }
380
+ const config = {
381
+ motherduck_token: token
382
+ };
383
+ console.log(`Connecting to MotherDuck cloud database: ${database}`);
384
+ instance = await DuckDBInstance.create(databasePath, config);
385
+ connection = await instance.connect();
386
+ console.log("Connected successfully to MotherDuck");
387
+ return state;
388
+ }
389
+ function disconnect(state) {
390
+ if (connection && typeof connection.close === "function") {
391
+ connection.close();
392
+ }
393
+ if (instance && typeof instance.close === "function") {
394
+ instance.close();
395
+ }
396
+ connection = null;
397
+ instance = null;
398
+ return state;
399
+ }
400
+ function query(sqlQuery, options = {}) {
401
+ return (state) => {
402
+ const [resolvedQuery, resolvedOptions] = expandReferences(
403
+ state,
404
+ sqlQuery,
405
+ options
406
+ );
407
+ return queryHandler(
408
+ connection,
409
+ state,
410
+ resolvedQuery,
411
+ resolvedOptions,
412
+ composeNextState
413
+ );
414
+ };
415
+ }
416
+ function insert(table, records, options = {}) {
417
+ return async (state) => {
418
+ const [resolvedTable, resolvedRecords, resolvedOptions] = expandReferences(
419
+ state,
420
+ table,
421
+ records,
422
+ options
423
+ );
424
+ const batchSize = resolvedOptions.batchSize || 1e3;
425
+ const recordsArray = Array.isArray(resolvedRecords) ? resolvedRecords : [resolvedRecords];
426
+ if (!recordsArray || recordsArray.length === 0) {
427
+ console.log("No records provided; skipping insert.");
428
+ return {
429
+ ...state,
430
+ data: { recordsInserted: 0, batches: 0 }
431
+ };
432
+ }
433
+ validateSqlIdentifier(resolvedTable);
434
+ const totalRecords = recordsArray.length;
435
+ console.log(
436
+ `Preparing to insert ${totalRecords} record${totalRecords !== 1 ? "s" : ""} into:`,
437
+ resolvedTable
438
+ );
439
+ const chunks = _.chunk(recordsArray, batchSize);
440
+ if (chunks.length > 1) {
441
+ console.log(
442
+ `Large dataset detected. Splitting into ${chunks.length} batches of up to ${batchSize} records.`
443
+ );
444
+ }
445
+ let currentState = state;
446
+ let totalInserted = 0;
447
+ for (let i = 0; i < chunks.length; i++) {
448
+ const chunk = chunks[i];
449
+ const batchNumber = i + 1;
450
+ if (chunks.length > 1) {
451
+ console.log(
452
+ `Processing batch ${batchNumber}/${chunks.length}: ${chunk.length} records`
453
+ );
454
+ }
455
+ const columns = Object.keys(chunk[0]);
456
+ const columnsList = columns.join(", ");
457
+ columns.forEach((col) => validateSqlIdentifier(col));
458
+ const valuesStrings = chunk.map((record) => {
459
+ const values = columns.map((key) => formatSqlValue(record[key]));
460
+ return `(${values.join(", ")})`;
461
+ });
462
+ const sqlQuery = `INSERT INTO ${resolvedTable} (${columnsList}) VALUES ${valuesStrings.join(
463
+ ", "
464
+ )}`;
465
+ currentState = await queryHandler(
466
+ connection,
467
+ currentState,
468
+ sqlQuery,
469
+ resolvedOptions,
470
+ composeNextState
471
+ );
472
+ totalInserted += chunk.length;
473
+ }
474
+ if (chunks.length > 1) {
475
+ console.log(
476
+ `Successfully inserted ${totalInserted} records in ${chunks.length} batches.`
477
+ );
478
+ }
479
+ return {
480
+ ...currentState,
481
+ data: { recordsInserted: totalInserted, batches: chunks.length }
482
+ };
483
+ };
484
+ }
485
+
486
+ // src/index.js
487
+ var src_default = Adaptor_exports;
488
+ export {
489
+ alterState,
490
+ arrayToString,
491
+ as,
492
+ combine,
493
+ cursor,
494
+ dataPath,
495
+ dataValue,
496
+ dateFns,
497
+ src_default as default,
498
+ each,
499
+ execute,
500
+ field,
501
+ fields,
502
+ fn,
503
+ fnIf,
504
+ group,
505
+ insert,
506
+ lastReferenceValue,
507
+ map,
508
+ merge,
509
+ query,
510
+ sourceValue,
511
+ util_exports as util
512
+ };
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@openfn/language-motherduck",
3
+ "version": "1.0.0",
4
+ "description": "OpenFn MotherDuck cloud database adaptor",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "import": "./dist/index.js",
9
+ "types": "./types/index.d.ts",
10
+ "require": "./dist/index.cjs"
11
+ },
12
+ "./package.json": "./package.json"
13
+ },
14
+ "author": "Aseidas Blauvelt <ablauvelt@verasolutions.org> (Vera Solutions)",
15
+ "license": "LGPLv3",
16
+ "files": [
17
+ "dist/",
18
+ "types/",
19
+ "ast.json",
20
+ "configuration-schema.json"
21
+ ],
22
+ "dependencies": {
23
+ "@duckdb/node-api": "^1.3.2-alpha.25",
24
+ "lodash": "^4.17.21",
25
+ "@openfn/language-common": "3.1.1"
26
+ },
27
+ "devDependencies": {
28
+ "assertion-error": "2.0.0",
29
+ "chai": "4.3.6",
30
+ "deep-eql": "4.1.1",
31
+ "mocha": "^10.7.3",
32
+ "rimraf": "3.0.2",
33
+ "undici": "^5.22.1"
34
+ },
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "https://github.com/openfn/adaptors.git"
38
+ },
39
+ "types": "types/index.d.ts",
40
+ "main": "dist/index.cjs",
41
+ "scripts": {
42
+ "build": "pnpm clean && build-adaptor motherduck",
43
+ "test": "mocha --experimental-specifier-resolution=node --no-warnings",
44
+ "test:watch": "mocha -w --experimental-specifier-resolution=node --no-warnings",
45
+ "clean": "rimraf dist types docs",
46
+ "pack": "pnpm pack --pack-destination ../../dist",
47
+ "lint": "eslint src"
48
+ }
49
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Execute a sequence of operations against MotherDuck cloud database.
3
+ * Wraps `@openfn/language-common/execute`, and prepends initial state for MotherDuck.
4
+ * @example
5
+ * execute(
6
+ * query('SELECT * FROM my_table'),
7
+ * insert('users', { name: 'John', age: 30 })
8
+ * )(state)
9
+ * @public
10
+ * @function
11
+ * @param {...Operation} operations - Operations to be performed.
12
+ * @returns {Operation}
13
+ * @state {Array} references - Array of previous operation results
14
+ * @state {*} data - Data from the last operation
15
+ */
16
+ export function execute(...operations: Operation[]): Operation;
17
+ /**
18
+ * Execute a SQL query against the MotherDuck database.
19
+ * @public
20
+ * @example <caption>Simple query</caption>
21
+ * query('SELECT * FROM users WHERE age > 18')
22
+ * @example <caption>Query with SQL logging</caption>
23
+ * query('SELECT * FROM orders', { writeSql: true })
24
+ * @function
25
+ * @param {string} sqlQuery - SQL query string
26
+ * @param {object} [options] - Query execution options
27
+ * @param {boolean} [options.writeSql=false] - Include full SQL in response.query (default: false, hides query for security)
28
+ * @returns {Operation}
29
+ * @state {Array} data - Query results as array of row objects
30
+ * @state {object} response - Metadata including rowCount, command, and query
31
+ */
32
+ export function query(sqlQuery: string, options?: {
33
+ writeSql?: boolean;
34
+ }): Operation;
35
+ /**
36
+ * Insert one or more records into a MotherDuck table with automatic batching.
37
+ * Large datasets are automatically split into batches for optimal performance.
38
+ * @public
39
+ * @example <caption>Insert a single record</caption>
40
+ * insert('users', { name: 'John', age: 30, email: 'john@example.com' })
41
+ * @example <caption>Insert multiple records</caption>
42
+ * insert('users', [
43
+ * { name: 'John', age: 30 },
44
+ * { name: 'Jane', age: 25 }
45
+ * ])
46
+ * @example <caption>Insert with custom batch size</caption>
47
+ * insert('users', records, { batchSize: 500 })
48
+ * @function
49
+ * @param {string} table - Target table name
50
+ * @param {object|Array} records - A single record object or array of records
51
+ * @param {object} [options] - Insert options
52
+ * @param {number} [options.batchSize=1000] - Number of records per batch
53
+ * @returns {Operation}
54
+ * @state {object} data - Metadata including recordsInserted and batches
55
+ */
56
+ export function insert(table: string, records: object | any[], options?: {
57
+ batchSize?: number;
58
+ }): Operation;
59
+ export { util };
60
+ import * as util from "./util.js";
61
+ export { alterState, arrayToString, combine, cursor, dataPath, dataValue, dateFns, each, field, fields, fn, fnIf, group, lastReferenceValue, map, merge, sourceValue, as } from "@openfn/language-common";
@@ -0,0 +1,4 @@
1
+ export default Adaptor;
2
+ export * from "./Adaptor";
3
+ export * as util from "./util.js";
4
+ import * as Adaptor from "./Adaptor";
@@ -0,0 +1,17 @@
1
+ export function createMockConnection(): {
2
+ mockInstance: {
3
+ connect: () => Promise<{
4
+ runAndReadAll: (sql: any) => Promise<{
5
+ getRowObjects: () => any[];
6
+ }>;
7
+ close: () => void;
8
+ }>;
9
+ close: () => void;
10
+ };
11
+ mockConnection: {
12
+ runAndReadAll: (sql: any) => Promise<{
13
+ getRowObjects: () => any[];
14
+ }>;
15
+ close: () => void;
16
+ };
17
+ };
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Validate SQL identifier (table names, column names, etc.)
3
+ * @public
4
+ * @function
5
+ * @param {string} identifier - SQL identifier to validate
6
+ * @returns {string} - Validated identifier
7
+ * @throws {Error} - If identifier contains dangerous characters
8
+ */
9
+ export function validateSqlIdentifier(identifier: string): string;
10
+ /**
11
+ * Escape SQL string values to prevent SQL injection
12
+ * @public
13
+ * @function
14
+ * @param {string} value - String value to escape
15
+ * @returns {string} - Escaped string value
16
+ */
17
+ export function escapeSqlString(value: string): string;
18
+ /**
19
+ * Format a value for SQL insertion
20
+ * @public
21
+ * @function
22
+ * @param {any} value - Value to format
23
+ * @returns {string} - Formatted SQL value
24
+ */
25
+ export function formatSqlValue(value: any): string;
26
+ /**
27
+ * Convert BigInt and DECIMAL values to regular numbers for JSON serialization
28
+ * @private
29
+ * @function
30
+ * @param {any} obj - Object to convert
31
+ * @returns {any} - Object with BigInt and DECIMAL values converted to numbers
32
+ */
33
+ export function convertBigIntToNumber(obj: any): any;
34
+ /**
35
+ * Helper function to handle query execution
36
+ * @private
37
+ * @function
38
+ * @param {object} connection - Active database connection
39
+ * @param {object} state - Runtime state
40
+ * @param {string} sqlQuery - SQL query to execute
41
+ * @param {object} options - Query options
42
+ * @param {Function} composeNextState - State composition function
43
+ * @returns {Promise}
44
+ */
45
+ export function queryHandler(connection: object, state: object, sqlQuery: string, options: object, composeNextState: Function): Promise<any>;