sqlstack 2.0.0 → 3.1.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/cjs/codegen/generateManifest.d.ts +12 -0
- package/dist/cjs/codegen/generateManifest.d.ts.map +1 -1
- package/dist/cjs/codegen/generateManifest.js +21 -11
- package/dist/cjs/codegen/generateManifest.js.map +1 -1
- package/dist/cjs/core/resolver.d.ts +25 -12
- package/dist/cjs/core/resolver.d.ts.map +1 -1
- package/dist/cjs/core/resolver.js +66 -20
- package/dist/cjs/core/resolver.js.map +1 -1
- package/dist/cjs/core/sourceScanResolver.d.ts +25 -0
- package/dist/cjs/core/sourceScanResolver.d.ts.map +1 -0
- package/dist/cjs/core/sourceScanResolver.js +56 -0
- package/dist/cjs/core/sourceScanResolver.js.map +1 -0
- package/dist/cjs/decorators/query.d.ts.map +1 -1
- package/dist/cjs/decorators/query.js +276 -245
- package/dist/cjs/decorators/query.js.map +1 -1
- package/dist/cjs/families/sqliteFamily.d.ts +26 -0
- package/dist/cjs/families/sqliteFamily.d.ts.map +1 -0
- package/dist/cjs/families/sqliteFamily.js +187 -0
- package/dist/cjs/families/sqliteFamily.js.map +1 -0
- package/dist/cjs/family.d.ts +69 -0
- package/dist/cjs/family.d.ts.map +1 -0
- package/dist/cjs/family.js +206 -0
- package/dist/cjs/family.js.map +1 -0
- package/dist/cjs/familyBinding.d.ts +22 -0
- package/dist/cjs/familyBinding.d.ts.map +1 -0
- package/dist/cjs/familyBinding.js +22 -0
- package/dist/cjs/familyBinding.js.map +1 -0
- package/dist/cjs/index.d.ts +10 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +17 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/ioc.d.ts +22 -0
- package/dist/cjs/ioc.d.ts.map +1 -0
- package/dist/cjs/ioc.js +35 -0
- package/dist/cjs/ioc.js.map +1 -0
- package/dist/cjs/run.d.ts +29 -0
- package/dist/cjs/run.d.ts.map +1 -0
- package/dist/cjs/run.js +38 -0
- package/dist/cjs/run.js.map +1 -0
- package/dist/cjs/runtime.d.ts +25 -0
- package/dist/cjs/runtime.d.ts.map +1 -0
- package/dist/cjs/runtime.js +133 -0
- package/dist/cjs/runtime.js.map +1 -0
- package/dist/cjs/stack.d.ts +101 -0
- package/dist/cjs/stack.d.ts.map +1 -0
- package/dist/cjs/stack.js +288 -0
- package/dist/cjs/stack.js.map +1 -0
- package/dist/cjs/testing/builder.d.ts +29 -0
- package/dist/cjs/testing/builder.d.ts.map +1 -0
- package/dist/cjs/testing/builder.js +131 -0
- package/dist/cjs/testing/builder.js.map +1 -0
- package/dist/cjs/testing/index.d.ts +14 -0
- package/dist/cjs/testing/index.d.ts.map +1 -0
- package/dist/cjs/testing/index.js +22 -0
- package/dist/cjs/testing/index.js.map +1 -0
- package/dist/cjs/testing/postgres/allocator.d.ts +17 -0
- package/dist/cjs/testing/postgres/allocator.d.ts.map +1 -0
- package/dist/cjs/testing/postgres/allocator.js +54 -0
- package/dist/cjs/testing/postgres/allocator.js.map +1 -0
- package/dist/cjs/testing/postgres/database.d.ts +21 -0
- package/dist/cjs/testing/postgres/database.d.ts.map +1 -0
- package/dist/cjs/testing/postgres/database.js +39 -0
- package/dist/cjs/testing/postgres/database.js.map +1 -0
- package/dist/cjs/testing/postgres/loadData.d.ts +31 -0
- package/dist/cjs/testing/postgres/loadData.d.ts.map +1 -0
- package/dist/cjs/testing/postgres/loadData.js +184 -0
- package/dist/cjs/testing/postgres/loadData.js.map +1 -0
- package/dist/cjs/testing/postgres/server.d.ts +19 -0
- package/dist/cjs/testing/postgres/server.d.ts.map +1 -0
- package/dist/cjs/testing/postgres/server.js +78 -0
- package/dist/cjs/testing/postgres/server.js.map +1 -0
- package/dist/cjs/testing/schema/ddl.d.ts +5 -0
- package/dist/cjs/testing/schema/ddl.d.ts.map +1 -0
- package/dist/cjs/testing/schema/ddl.js +70 -0
- package/dist/cjs/testing/schema/ddl.js.map +1 -0
- package/dist/cjs/testing/schema/normalize.d.ts +7 -0
- package/dist/cjs/testing/schema/normalize.d.ts.map +1 -0
- package/dist/cjs/testing/schema/normalize.js +202 -0
- package/dist/cjs/testing/schema/normalize.js.map +1 -0
- package/dist/cjs/testing/schema/parser.d.ts +9 -0
- package/dist/cjs/testing/schema/parser.d.ts.map +1 -0
- package/dist/cjs/testing/schema/parser.js +80 -0
- package/dist/cjs/testing/schema/parser.js.map +1 -0
- package/dist/cjs/testing/schema/types.d.ts +63 -0
- package/dist/cjs/testing/schema/types.d.ts.map +1 -0
- package/dist/cjs/testing/schema/types.js +6 -0
- package/dist/cjs/testing/schema/types.js.map +1 -0
- package/dist/cjs/testing/sqlite/builder.d.ts +24 -0
- package/dist/cjs/testing/sqlite/builder.d.ts.map +1 -0
- package/dist/cjs/testing/sqlite/builder.js +134 -0
- package/dist/cjs/testing/sqlite/builder.js.map +1 -0
- package/dist/cjs/testing/sqlite/database.d.ts +18 -0
- package/dist/cjs/testing/sqlite/database.d.ts.map +1 -0
- package/dist/cjs/testing/sqlite/database.js +123 -0
- package/dist/cjs/testing/sqlite/database.js.map +1 -0
- package/dist/cjs/testing/sqlite/loadData.d.ts +7 -0
- package/dist/cjs/testing/sqlite/loadData.d.ts.map +1 -0
- package/dist/cjs/testing/sqlite/loadData.js +55 -0
- package/dist/cjs/testing/sqlite/loadData.js.map +1 -0
- package/dist/cjs/transactions.d.ts +14 -5
- package/dist/cjs/transactions.d.ts.map +1 -1
- package/dist/cjs/transactions.js +40 -24
- package/dist/cjs/transactions.js.map +1 -1
- package/dist/esm/codegen/generateManifest.js +20 -11
- package/dist/esm/codegen/generateManifest.js.map +1 -1
- package/dist/esm/core/resolver.js +59 -19
- package/dist/esm/core/resolver.js.map +1 -1
- package/dist/esm/core/sourceScanResolver.js +51 -0
- package/dist/esm/core/sourceScanResolver.js.map +1 -0
- package/dist/esm/decorators/query.js +278 -247
- package/dist/esm/decorators/query.js.map +1 -1
- package/dist/esm/families/sqliteFamily.js +151 -0
- package/dist/esm/families/sqliteFamily.js.map +1 -0
- package/dist/esm/family.js +202 -0
- package/dist/esm/family.js.map +1 -0
- package/dist/esm/familyBinding.js +18 -0
- package/dist/esm/familyBinding.js.map +1 -0
- package/dist/esm/index.js +10 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/ioc.js +32 -0
- package/dist/esm/ioc.js.map +1 -0
- package/dist/esm/run.js +34 -0
- package/dist/esm/run.js.map +1 -0
- package/dist/esm/runtime.js +94 -0
- package/dist/esm/runtime.js.map +1 -0
- package/dist/esm/stack.js +251 -0
- package/dist/esm/stack.js.map +1 -0
- package/dist/esm/testing/builder.js +95 -0
- package/dist/esm/testing/builder.js.map +1 -0
- package/dist/esm/testing/index.js +9 -0
- package/dist/esm/testing/index.js.map +1 -0
- package/dist/esm/testing/postgres/allocator.js +49 -0
- package/dist/esm/testing/postgres/allocator.js.map +1 -0
- package/dist/esm/testing/postgres/database.js +36 -0
- package/dist/esm/testing/postgres/database.js.map +1 -0
- package/dist/esm/testing/postgres/loadData.js +176 -0
- package/dist/esm/testing/postgres/loadData.js.map +1 -0
- package/dist/esm/testing/postgres/server.js +69 -0
- package/dist/esm/testing/postgres/server.js.map +1 -0
- package/dist/esm/testing/schema/ddl.js +65 -0
- package/dist/esm/testing/schema/ddl.js.map +1 -0
- package/dist/esm/testing/schema/normalize.js +164 -0
- package/dist/esm/testing/schema/normalize.js.map +1 -0
- package/dist/esm/testing/schema/parser.js +43 -0
- package/dist/esm/testing/schema/parser.js.map +1 -0
- package/dist/esm/testing/schema/types.js +5 -0
- package/dist/esm/testing/schema/types.js.map +1 -0
- package/dist/esm/testing/sqlite/builder.js +98 -0
- package/dist/esm/testing/sqlite/builder.js.map +1 -0
- package/dist/esm/testing/sqlite/database.js +85 -0
- package/dist/esm/testing/sqlite/database.js.map +1 -0
- package/dist/esm/testing/sqlite/loadData.js +48 -0
- package/dist/esm/testing/sqlite/loadData.js.map +1 -0
- package/dist/esm/transactions.js +39 -24
- package/dist/esm/transactions.js.map +1 -1
- package/package.json +19 -2
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { getClassLabels, getMethodLabels, setMethodLabels } from '../core/metadata.js';
|
|
2
2
|
import { SqlStackError, ExactlyOneRowError, NoRowsError, RowsExistError, DatabaseError } from '../core/errors.js';
|
|
3
3
|
import { isValidationDisabled, validateOutput } from '../core/validator.js';
|
|
4
|
-
import {
|
|
4
|
+
import { resolveExecution } from '../runtime.js';
|
|
5
5
|
import { createSqliteBinder, createMysqlBinder, createPostgresBinder } from '../binders/sqlBinder.js';
|
|
6
|
-
import { coerceRootPath,
|
|
7
|
-
import {
|
|
6
|
+
import { coerceRootPath, resolveSqlFromFs } from '../core/fileResolution.js';
|
|
7
|
+
import { registerDecoratedMethod, recordQueryMethodOnClass } from '../core/resolver.js';
|
|
8
|
+
import { defaultSourceScanResolver } from '../core/sourceScanResolver.js';
|
|
8
9
|
import { getActiveTransaction } from '../transactions.js';
|
|
9
10
|
import { SqlFragment } from '../sql.js';
|
|
11
|
+
import { runStorage } from '../run.js';
|
|
10
12
|
export function Query(arg) {
|
|
11
13
|
return function (_target, prop, desc) {
|
|
12
14
|
const options = typeof arg === 'string' ? undefined : arg;
|
|
@@ -20,292 +22,321 @@ export function Query(arg) {
|
|
|
20
22
|
setMethodLabels(_target, prop, { source: { kind: 'inline', sql: options.sql } });
|
|
21
23
|
}
|
|
22
24
|
else {
|
|
23
|
-
// FS-based resolution
|
|
25
|
+
// FS-based resolution: only an EXPLICIT path argument pins a directory.
|
|
26
|
+
// Bare @Query() resolves through the active resolver (or the default
|
|
27
|
+
// SourceScanResolver) — no stack-based directory guessing, which breaks
|
|
28
|
+
// under bundlers and test transforms.
|
|
24
29
|
const { rootDir } = coerceRootPath(rootArg);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
setMethodLabels(_target, prop, { source: { kind: 'fs', rootDir: dir } });
|
|
30
|
+
if (rootDir) {
|
|
31
|
+
setMethodLabels(_target, prop, { source: { kind: 'fs', rootDir } });
|
|
28
32
|
}
|
|
29
33
|
}
|
|
30
|
-
// Record for resolver coverage checks
|
|
31
|
-
|
|
34
|
+
// Record for resolver coverage checks: immutable class metadata (the
|
|
35
|
+
// canonical, environment-local source) plus the deprecated global list
|
|
36
|
+
// consulted by the legacy global-resolver path.
|
|
37
|
+
const decoratedEntry = {
|
|
32
38
|
className: _target?.constructor?.name ?? '<anonymous>',
|
|
33
39
|
methodName: String(prop),
|
|
34
40
|
inline: !!options?.sql,
|
|
35
|
-
}
|
|
41
|
+
};
|
|
42
|
+
recordQueryMethodOnClass(_target, decoratedEntry);
|
|
43
|
+
registerDecoratedMethod(decoratedEntry);
|
|
36
44
|
const original = desc.value;
|
|
37
45
|
desc.value = async function (...callArgs) {
|
|
38
46
|
const labels = getMethodLabels(this, prop);
|
|
39
47
|
const classLabels = getClassLabels(this);
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
//
|
|
47
|
-
fragmentResult = undefined;
|
|
48
|
-
}
|
|
49
|
-
if (fragmentResult instanceof SqlFragment) {
|
|
50
|
-
const { sql: boundSql, params } = fragmentResult.toSQL();
|
|
48
|
+
// The full file/inline pipeline (resolve source, bind, execute,
|
|
49
|
+
// shape/transform/validate). Invoked either by run() from inside the
|
|
50
|
+
// method body, or directly as the legacy fallback when the body throws.
|
|
51
|
+
const pipeline = async () => {
|
|
52
|
+
// 1) prepare args with defaults
|
|
53
|
+
const rawInput = callArgs.length === 1 ? callArgs[0] : callArgs; // object or positional array
|
|
54
|
+
// 2) choose database and dialect via the active environment
|
|
51
55
|
const dbName = labels.db ?? options?.db ?? classLabels?.db;
|
|
52
|
-
const {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
56
|
+
const { entry, resolver: activeResolver } = await resolveExecution(dbName);
|
|
57
|
+
const db = entry.db;
|
|
58
|
+
const dialect = options?.dialect ?? db.dialect ?? 'sqlite';
|
|
59
|
+
// 3) load SQL source (inline, environment resolver, v1 fs fallback,
|
|
60
|
+
// or the zero-config source scan — bare @Query() with nothing installed)
|
|
61
|
+
const sql = labels?.source?.kind === 'inline'
|
|
62
|
+
? labels.source.sql
|
|
63
|
+
: activeResolver
|
|
64
|
+
? activeResolver.resolve({
|
|
65
|
+
className: this?.constructor?.name ?? '<anonymous>',
|
|
66
|
+
methodName: String(prop),
|
|
67
|
+
dialect,
|
|
68
|
+
rootDir: labels?.source?.kind === 'fs' ? labels.source.rootDir : undefined,
|
|
69
|
+
})
|
|
70
|
+
: labels?.source?.kind === 'fs'
|
|
71
|
+
? resolveSqlFromFs(labels.source.rootDir, String(prop), dialect).sql
|
|
72
|
+
: defaultSourceScanResolver()?.resolve({
|
|
73
|
+
className: this?.constructor?.name ?? '<anonymous>',
|
|
74
|
+
methodName: String(prop),
|
|
75
|
+
dialect,
|
|
76
|
+
});
|
|
77
|
+
if (!sql) {
|
|
78
|
+
throw new SqlStackError(`No SQL source provided for method ${String(prop)}. Use @Query() with a file/folder path or inline sql.`);
|
|
74
79
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const { name: resolvedDbName, db } = SqlStackDB.getEntry(dbName);
|
|
82
|
-
const dialect = options?.dialect ?? db.dialect ?? 'sqlite';
|
|
83
|
-
// 3) load SQL source (inline, active resolver, or v1 fs fallback)
|
|
84
|
-
const activeResolver = getActiveResolver();
|
|
85
|
-
const sql = labels?.source?.kind === 'inline'
|
|
86
|
-
? labels.source.sql
|
|
87
|
-
: activeResolver
|
|
88
|
-
? activeResolver.resolve({
|
|
89
|
-
className: this?.constructor?.name ?? '<anonymous>',
|
|
90
|
-
methodName: String(prop),
|
|
91
|
-
dialect,
|
|
92
|
-
rootDir: labels?.source?.kind === 'fs' ? labels.source.rootDir : undefined,
|
|
93
|
-
})
|
|
94
|
-
: labels?.source?.kind === 'fs'
|
|
95
|
-
? resolveSqlFromFs(labels.source.rootDir, String(prop), dialect).sql
|
|
96
|
-
: undefined;
|
|
97
|
-
if (!sql) {
|
|
98
|
-
throw new SqlStackError(`No SQL source provided for method ${String(prop)}. Use @Query() with a file/folder path or inline sql.`);
|
|
99
|
-
}
|
|
100
|
-
// 4) bind placeholders
|
|
101
|
-
// Decide argument mode: positional if SQL contains :argN
|
|
102
|
-
let workingSql = sql;
|
|
103
|
-
const hasPositional = /:arg[1-9][0-9]*/.test(workingSql);
|
|
104
|
-
// Prepare args for binder
|
|
105
|
-
let args;
|
|
106
|
-
if (hasPositional) {
|
|
107
|
-
args = callArgs;
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
const namedBase = callArgs.length > 0 ? callArgs[0] : undefined;
|
|
111
|
-
args = applyDefaults(labels.defaults, namedBase ?? {});
|
|
112
|
-
}
|
|
113
|
-
// Compute paging and inject :offset/:limit (named mode) or rewrite to positional
|
|
114
|
-
const pageCfg = labels.page;
|
|
115
|
-
if (pageCfg) {
|
|
116
|
-
const { size, zeroBase } = pageCfg;
|
|
117
|
-
const base = zeroBase ? 0 : 1;
|
|
118
|
-
// Extract page number from input (object) or a trailing options object in positional mode
|
|
119
|
-
const pageFromObject = (val) => {
|
|
120
|
-
if (val && typeof val === 'object' && !Array.isArray(val) && typeof val.page !== 'undefined') {
|
|
121
|
-
const p = val.page;
|
|
122
|
-
if (typeof p === 'number' && isFinite(p))
|
|
123
|
-
return p;
|
|
124
|
-
}
|
|
125
|
-
return undefined;
|
|
126
|
-
};
|
|
127
|
-
let pageNum;
|
|
80
|
+
// 4) bind placeholders
|
|
81
|
+
// Decide argument mode: positional if SQL contains :argN
|
|
82
|
+
let workingSql = sql;
|
|
83
|
+
const hasPositional = /:arg[1-9][0-9]*/.test(workingSql);
|
|
84
|
+
// Prepare args for binder
|
|
85
|
+
let args;
|
|
128
86
|
if (hasPositional) {
|
|
129
|
-
|
|
130
|
-
if (callArgs.length > 0) {
|
|
131
|
-
const last = callArgs[callArgs.length - 1];
|
|
132
|
-
pageNum = pageFromObject(last);
|
|
133
|
-
}
|
|
87
|
+
args = callArgs;
|
|
134
88
|
}
|
|
135
89
|
else {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
90
|
+
const namedBase = callArgs.length > 0 ? callArgs[0] : undefined;
|
|
91
|
+
args = applyDefaults(labels.defaults, namedBase ?? {});
|
|
92
|
+
}
|
|
93
|
+
// Compute paging and inject :offset/:limit (named mode) or rewrite to positional
|
|
94
|
+
const pageCfg = labels.page;
|
|
95
|
+
if (pageCfg) {
|
|
96
|
+
const { size, zeroBase } = pageCfg;
|
|
97
|
+
const base = zeroBase ? 0 : 1;
|
|
98
|
+
// Extract page number from input (object) or a trailing options object in positional mode
|
|
99
|
+
const pageFromObject = (val) => {
|
|
100
|
+
if (val && typeof val === 'object' && !Array.isArray(val) && typeof val.page !== 'undefined') {
|
|
101
|
+
const p = val.page;
|
|
102
|
+
if (typeof p === 'number' && isFinite(p))
|
|
103
|
+
return p;
|
|
104
|
+
}
|
|
105
|
+
return undefined;
|
|
106
|
+
};
|
|
107
|
+
let pageNum;
|
|
108
|
+
if (hasPositional) {
|
|
109
|
+
// Variadic-style: only the LAST argument may carry `{ page }`
|
|
110
|
+
if (callArgs.length > 0) {
|
|
111
|
+
const last = callArgs[callArgs.length - 1];
|
|
112
|
+
pageNum = pageFromObject(last);
|
|
113
|
+
}
|
|
140
114
|
}
|
|
141
|
-
|
|
142
|
-
|
|
115
|
+
else {
|
|
116
|
+
// Prefer last-arg options if provided; otherwise read from named object
|
|
117
|
+
if (callArgs.length > 1) {
|
|
118
|
+
const last = callArgs[callArgs.length - 1];
|
|
119
|
+
pageNum = pageFromObject(last);
|
|
120
|
+
}
|
|
121
|
+
if (pageNum === undefined) {
|
|
122
|
+
pageNum = pageFromObject(args);
|
|
123
|
+
}
|
|
143
124
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
inLC = false;
|
|
165
|
-
i++;
|
|
166
|
-
continue;
|
|
167
|
-
}
|
|
168
|
-
if (inBC) {
|
|
169
|
-
out.push(ch);
|
|
170
|
-
if (ch === '*' && next === '/') {
|
|
171
|
-
out.push(next);
|
|
125
|
+
const effectivePage = typeof pageNum === 'number' ? pageNum : base;
|
|
126
|
+
const offset = (effectivePage - base) * size;
|
|
127
|
+
const limit = size;
|
|
128
|
+
if (hasPositional) {
|
|
129
|
+
// Replace occurrences of :offset and :limit with new :argN tokens appended to args
|
|
130
|
+
const baseIndex = callArgs.length; // start numbering after user args
|
|
131
|
+
let nextIndex = baseIndex + 1;
|
|
132
|
+
const additions = [];
|
|
133
|
+
const rewrite = (sqlText) => {
|
|
134
|
+
const out = [];
|
|
135
|
+
let i = 0;
|
|
136
|
+
let inSQ = false, inDQ = false, inLC = false, inBC = false;
|
|
137
|
+
const len = sqlText.length;
|
|
138
|
+
while (i < len) {
|
|
139
|
+
const ch = sqlText[i];
|
|
140
|
+
const next = i + 1 < len ? sqlText[i + 1] : '';
|
|
141
|
+
if (inLC) {
|
|
142
|
+
out.push(ch);
|
|
143
|
+
if (ch === '\n')
|
|
144
|
+
inLC = false;
|
|
172
145
|
i++;
|
|
173
|
-
|
|
146
|
+
continue;
|
|
174
147
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
148
|
+
if (inBC) {
|
|
149
|
+
out.push(ch);
|
|
150
|
+
if (ch === '*' && next === '/') {
|
|
151
|
+
out.push(next);
|
|
152
|
+
i++;
|
|
153
|
+
inBC = false;
|
|
154
|
+
}
|
|
155
|
+
i++;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (inSQ) {
|
|
159
|
+
out.push(ch);
|
|
160
|
+
if (ch === "'" && next === "'") {
|
|
161
|
+
out.push(next);
|
|
162
|
+
i += 2;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (ch === "'")
|
|
166
|
+
inSQ = false;
|
|
167
|
+
i++;
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (inDQ) {
|
|
171
|
+
out.push(ch);
|
|
172
|
+
if (ch === '"' && next === '"') {
|
|
173
|
+
out.push(next);
|
|
174
|
+
i += 2;
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (ch === '"')
|
|
178
|
+
inDQ = false;
|
|
179
|
+
i++;
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (ch === '-' && next === '-') {
|
|
183
|
+
out.push(ch, next);
|
|
182
184
|
i += 2;
|
|
185
|
+
inLC = true;
|
|
183
186
|
continue;
|
|
184
187
|
}
|
|
185
|
-
if (ch ===
|
|
186
|
-
|
|
187
|
-
i++;
|
|
188
|
-
continue;
|
|
189
|
-
}
|
|
190
|
-
if (inDQ) {
|
|
191
|
-
out.push(ch);
|
|
192
|
-
if (ch === '"' && next === '"') {
|
|
193
|
-
out.push(next);
|
|
188
|
+
if (ch === '/' && next === '*') {
|
|
189
|
+
out.push(ch, next);
|
|
194
190
|
i += 2;
|
|
191
|
+
inBC = true;
|
|
195
192
|
continue;
|
|
196
193
|
}
|
|
197
|
-
if (ch === '"')
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
if (ch === '-' && next === '-') {
|
|
203
|
-
out.push(ch, next);
|
|
204
|
-
i += 2;
|
|
205
|
-
inLC = true;
|
|
206
|
-
continue;
|
|
207
|
-
}
|
|
208
|
-
if (ch === '/' && next === '*') {
|
|
209
|
-
out.push(ch, next);
|
|
210
|
-
i += 2;
|
|
211
|
-
inBC = true;
|
|
212
|
-
continue;
|
|
213
|
-
}
|
|
214
|
-
if (ch === '"') {
|
|
215
|
-
out.push(ch);
|
|
216
|
-
inDQ = true;
|
|
217
|
-
i++;
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
|
-
if (ch === "'") {
|
|
221
|
-
out.push(ch);
|
|
222
|
-
inSQ = true;
|
|
223
|
-
i++;
|
|
224
|
-
continue;
|
|
225
|
-
}
|
|
226
|
-
if (ch === ':' && next === ':') {
|
|
227
|
-
out.push('::');
|
|
228
|
-
i += 2;
|
|
229
|
-
continue;
|
|
230
|
-
}
|
|
231
|
-
if (ch === ':') {
|
|
232
|
-
// parse token
|
|
233
|
-
let j = i + 1;
|
|
234
|
-
const start = j;
|
|
235
|
-
while (j < len) {
|
|
236
|
-
const c = sqlText[j];
|
|
237
|
-
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c === '_')
|
|
238
|
-
j++;
|
|
239
|
-
else
|
|
240
|
-
break;
|
|
194
|
+
if (ch === '"') {
|
|
195
|
+
out.push(ch);
|
|
196
|
+
inDQ = true;
|
|
197
|
+
i++;
|
|
198
|
+
continue;
|
|
241
199
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
200
|
+
if (ch === "'") {
|
|
201
|
+
out.push(ch);
|
|
202
|
+
inSQ = true;
|
|
203
|
+
i++;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
if (ch === ':' && next === ':') {
|
|
207
|
+
out.push('::');
|
|
208
|
+
i += 2;
|
|
248
209
|
continue;
|
|
249
210
|
}
|
|
250
|
-
if (
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
211
|
+
if (ch === ':') {
|
|
212
|
+
// parse token
|
|
213
|
+
let j = i + 1;
|
|
214
|
+
const start = j;
|
|
215
|
+
while (j < len) {
|
|
216
|
+
const c = sqlText[j];
|
|
217
|
+
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c === '_')
|
|
218
|
+
j++;
|
|
219
|
+
else
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
const name = sqlText.slice(start, j);
|
|
223
|
+
if (name === 'offset') {
|
|
224
|
+
const token = `:arg${nextIndex++}`;
|
|
225
|
+
out.push(token);
|
|
226
|
+
additions.push(offset);
|
|
227
|
+
i = j;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (name === 'limit') {
|
|
231
|
+
const token = `:arg${nextIndex++}`;
|
|
232
|
+
out.push(token);
|
|
233
|
+
additions.push(limit);
|
|
234
|
+
i = j;
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
// otherwise leave untouched
|
|
238
|
+
out.push(':');
|
|
239
|
+
i++;
|
|
255
240
|
continue;
|
|
256
241
|
}
|
|
257
|
-
|
|
258
|
-
out.push(':');
|
|
242
|
+
out.push(ch);
|
|
259
243
|
i++;
|
|
260
|
-
continue;
|
|
261
244
|
}
|
|
262
|
-
out.
|
|
263
|
-
|
|
245
|
+
return out.join('');
|
|
246
|
+
};
|
|
247
|
+
const rewritten = rewrite(workingSql);
|
|
248
|
+
if (rewritten !== workingSql) {
|
|
249
|
+
workingSql = rewritten;
|
|
250
|
+
// append values so :argK map correctly
|
|
251
|
+
args.push(...additions);
|
|
264
252
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
workingSql = rewritten;
|
|
270
|
-
// append values so :argK map correctly
|
|
271
|
-
args.push(...additions);
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
// Named-mode: attach computed values (override any provided)
|
|
256
|
+
args = { ...args, offset, limit };
|
|
272
257
|
}
|
|
273
258
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
259
|
+
const binder = dialect === 'postgres' ? createPostgresBinder() : dialect === 'mysql' ? createMysqlBinder() : createSqliteBinder();
|
|
260
|
+
const { sql: boundSql, params } = binder.bind(workingSql, args);
|
|
261
|
+
// 5) execute query via registry DB
|
|
262
|
+
let out;
|
|
263
|
+
try {
|
|
264
|
+
const tx = getActiveTransaction(entry);
|
|
265
|
+
out = tx ? await tx.query(boundSql, params) : await db.query(boundSql, params);
|
|
277
266
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
267
|
+
catch (dbError) {
|
|
268
|
+
// Attach SQL query (with placeholders) to error for debugging
|
|
269
|
+
throw new DatabaseError(dbError?.message || String(dbError), boundSql, dbError instanceof Error ? dbError : undefined);
|
|
270
|
+
}
|
|
271
|
+
// 6) If rows were returned (SELECT/CTE), shape + transform + validate
|
|
272
|
+
if (Array.isArray(out)) {
|
|
273
|
+
const shaped = applyShape(out, labels.shape);
|
|
274
|
+
const transformed = labels.transform ? labels.transform(shaped) : shaped;
|
|
275
|
+
// 7) validate result via JSON Schema, if provided
|
|
276
|
+
if (labels.validateSchema && !isValidationDisabled()) {
|
|
277
|
+
if (Array.isArray(transformed)) {
|
|
278
|
+
for (const row of transformed)
|
|
279
|
+
validateOutput(labels.validateSchema, row);
|
|
280
|
+
}
|
|
281
|
+
else if (transformed !== null && transformed !== undefined) {
|
|
282
|
+
validateOutput(labels.validateSchema, transformed);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return transformed;
|
|
286
|
+
}
|
|
287
|
+
// Non-SELECT: return the write result as-is
|
|
288
|
+
return out;
|
|
289
|
+
};
|
|
290
|
+
// 0) Invoke the method body inside a run() context. Three outcomes:
|
|
291
|
+
// - body calls run(): the pipeline executed inside the body; return
|
|
292
|
+
// the body's result verbatim (it may have caught/transformed it).
|
|
293
|
+
// - body returns a SqlFragment (template literal path): execute it.
|
|
294
|
+
// - body throws without calling run() (legacy placeholder throw):
|
|
295
|
+
// fall through to the pipeline.
|
|
296
|
+
const runCtx = { ran: false, execute: pipeline };
|
|
297
|
+
let fragmentResult;
|
|
283
298
|
try {
|
|
284
|
-
|
|
285
|
-
out = tx ? await tx.query(boundSql, params) : await db.query(boundSql, params);
|
|
299
|
+
fragmentResult = await runStorage.run(runCtx, () => original.apply(this, callArgs));
|
|
286
300
|
}
|
|
287
|
-
catch (
|
|
288
|
-
//
|
|
289
|
-
|
|
301
|
+
catch (bodyError) {
|
|
302
|
+
// A body that invoked run() is real logic — its errors propagate.
|
|
303
|
+
if (runCtx.ran)
|
|
304
|
+
throw bodyError;
|
|
305
|
+
// Method body threw (e.g. placeholder `throw new Error('replaced')`) — fall through to file/inline SQL
|
|
306
|
+
fragmentResult = undefined;
|
|
290
307
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
308
|
+
if (runCtx.ran)
|
|
309
|
+
return fragmentResult;
|
|
310
|
+
if (fragmentResult instanceof SqlFragment) {
|
|
311
|
+
const { sql: boundSql, params } = fragmentResult.toSQL();
|
|
312
|
+
const dbName = labels.db ?? options?.db ?? classLabels?.db;
|
|
313
|
+
const { entry } = await resolveExecution(dbName);
|
|
314
|
+
const db = entry.db;
|
|
315
|
+
let out;
|
|
316
|
+
try {
|
|
317
|
+
const tx = getActiveTransaction(entry);
|
|
318
|
+
out = tx ? await tx.query(boundSql, params) : await db.query(boundSql, params);
|
|
319
|
+
}
|
|
320
|
+
catch (dbError) {
|
|
321
|
+
throw new DatabaseError(dbError?.message || String(dbError), boundSql, dbError instanceof Error ? dbError : undefined);
|
|
322
|
+
}
|
|
323
|
+
if (Array.isArray(out)) {
|
|
324
|
+
const shaped = applyShape(out, labels.shape);
|
|
325
|
+
const transformed = labels.transform ? labels.transform(shaped) : shaped;
|
|
326
|
+
if (labels.validateSchema && !isValidationDisabled()) {
|
|
327
|
+
if (Array.isArray(transformed)) {
|
|
328
|
+
for (const row of transformed)
|
|
329
|
+
validateOutput(labels.validateSchema, row);
|
|
330
|
+
}
|
|
331
|
+
else if (transformed !== null && transformed !== undefined) {
|
|
332
|
+
validateOutput(labels.validateSchema, transformed);
|
|
333
|
+
}
|
|
303
334
|
}
|
|
335
|
+
return transformed;
|
|
304
336
|
}
|
|
305
|
-
return
|
|
337
|
+
return out;
|
|
306
338
|
}
|
|
307
|
-
|
|
308
|
-
return out;
|
|
339
|
+
return pipeline();
|
|
309
340
|
};
|
|
310
341
|
return desc;
|
|
311
342
|
};
|