sof-mssql 2.2.0 → 2.4.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/README.md +88 -0
- package/dist/fhirpath/jsonSafeEmptiness.test.d.ts +19 -0
- package/dist/fhirpath/jsonSafeEmptiness.test.d.ts.map +1 -0
- package/dist/fhirpath/jsonSafeEmptiness.test.js +80 -0
- package/dist/fhirpath/jsonSafeEmptiness.test.js.map +1 -0
- package/dist/fhirpath/visitor.d.ts +78 -0
- package/dist/fhirpath/visitor.d.ts.map +1 -1
- package/dist/fhirpath/visitor.js +228 -23
- package/dist/fhirpath/visitor.js.map +1 -1
- package/dist/load.d.ts +9 -0
- package/dist/load.d.ts.map +1 -1
- package/dist/load.js +4 -0
- package/dist/load.js.map +1 -1
- package/dist/loader/feedback.integration.test.d.ts +14 -0
- package/dist/loader/feedback.integration.test.d.ts.map +1 -0
- package/dist/loader/feedback.integration.test.js +76 -0
- package/dist/loader/feedback.integration.test.js.map +1 -0
- package/dist/loader/index.d.ts.map +1 -1
- package/dist/loader/index.js +19 -6
- package/dist/loader/index.js.map +1 -1
- package/dist/loader/jsonColumn.integration.test.d.ts +13 -0
- package/dist/loader/jsonColumn.integration.test.d.ts.map +1 -0
- package/dist/loader/jsonColumn.integration.test.js +86 -0
- package/dist/loader/jsonColumn.integration.test.js.map +1 -0
- package/dist/loader/tables.d.ts +96 -3
- package/dist/loader/tables.d.ts.map +1 -1
- package/dist/loader/tables.js +182 -16
- package/dist/loader/tables.js.map +1 -1
- package/dist/loader/tables.test.d.ts +12 -0
- package/dist/loader/tables.test.d.ts.map +1 -0
- package/dist/loader/tables.test.js +130 -0
- package/dist/loader/tables.test.js.map +1 -0
- package/dist/loader/types.d.ts +9 -0
- package/dist/loader/types.d.ts.map +1 -1
- package/dist/parser.d.ts +1 -1
- package/dist/parser.js +1 -1
- package/dist/queryGenerator/treeWalker/compile.js +0 -1
- package/dist/queryGenerator/treeWalker/compile.js.map +1 -1
- package/dist/queryGenerator/treeWalker/cteTemplates.d.ts +9 -1
- package/dist/queryGenerator/treeWalker/cteTemplates.d.ts.map +1 -1
- package/dist/queryGenerator/treeWalker/cteTemplates.js +39 -3
- package/dist/queryGenerator/treeWalker/cteTemplates.js.map +1 -1
- package/dist/queryGenerator/treeWalker/operators/forEach.d.ts +4 -2
- package/dist/queryGenerator/treeWalker/operators/forEach.d.ts.map +1 -1
- package/dist/queryGenerator/treeWalker/operators/forEach.js +11 -5
- package/dist/queryGenerator/treeWalker/operators/forEach.js.map +1 -1
- package/dist/queryGenerator/treeWalker/operators/repeat.d.ts +2 -0
- package/dist/queryGenerator/treeWalker/operators/repeat.d.ts.map +1 -1
- package/dist/queryGenerator/treeWalker/operators/repeat.js +12 -0
- package/dist/queryGenerator/treeWalker/operators/repeat.js.map +1 -1
- package/dist/queryGenerator/treeWalker/types.d.ts +0 -2
- package/dist/queryGenerator/treeWalker/types.d.ts.map +1 -1
- package/dist/tests/load.test.d.ts +11 -0
- package/dist/tests/load.test.d.ts.map +1 -0
- package/dist/tests/load.test.js +37 -0
- package/dist/tests/load.test.js.map +1 -0
- package/dist/tests/utils/database.d.ts +2 -0
- package/dist/tests/utils/database.d.ts.map +1 -1
- package/dist/tests/utils/database.js +61 -4
- package/dist/tests/utils/database.js.map +1 -1
- package/dist/tests/utils/generator.d.ts +2 -0
- package/dist/tests/utils/generator.d.ts.map +1 -1
- package/dist/tests/utils/generator.js +24 -0
- package/dist/tests/utils/generator.js.map +1 -1
- package/dist/tests/utils/loaderIntegration.d.ts +59 -0
- package/dist/tests/utils/loaderIntegration.d.ts.map +1 -0
- package/dist/tests/utils/loaderIntegration.js +137 -0
- package/dist/tests/utils/loaderIntegration.js.map +1 -0
- package/dist/tests/utils/reporter.d.ts +1 -1
- package/dist/tests/utils/reporter.d.ts.map +1 -1
- package/dist/tests/utils/reporter.js +5 -4
- package/dist/tests/utils/reporter.js.map +1 -1
- package/dist/tests/utils/serverCapabilities.d.ts +23 -0
- package/dist/tests/utils/serverCapabilities.d.ts.map +1 -0
- package/dist/tests/utils/serverCapabilities.js +35 -0
- package/dist/tests/utils/serverCapabilities.js.map +1 -0
- package/dist/tests/utils/types.d.ts +1 -1
- package/dist/tests/utils/types.js +1 -1
- package/dist/tests/validation.test.d.ts +13 -0
- package/dist/tests/validation.test.d.ts.map +1 -0
- package/dist/tests/validation.test.js +81 -0
- package/dist/tests/validation.test.js.map +1 -0
- package/dist/types.d.ts +1 -1
- package/dist/validation.d.ts +40 -0
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +56 -0
- package/dist/validation.js.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -74,6 +74,9 @@ npx sof-mssql load ./data \
|
|
|
74
74
|
|
|
75
75
|
# Preview what would be loaded
|
|
76
76
|
npx sof-mssql load ./data --dry-run
|
|
77
|
+
|
|
78
|
+
# Load into a native JSON column (SQL Server 2025+)
|
|
79
|
+
npx sof-mssql load ./data --resource-json-data-type JSON
|
|
77
80
|
```
|
|
78
81
|
|
|
79
82
|
**File naming:** Files must follow the pattern `{ResourceType}.ndjson` (e.g.,
|
|
@@ -94,6 +97,10 @@ filename and stored in the `resource_type` column.
|
|
|
94
97
|
- `--table-name <name>` - Table name (default: `fhir_resources`)
|
|
95
98
|
- `--schema-name <name>` - Schema name (default: `dbo`)
|
|
96
99
|
- `--resource-type <type>` - Load only specific resource type
|
|
100
|
+
- `--resource-json-data-type <type>` - Storage type for the `json` column:
|
|
101
|
+
`NVARCHAR(MAX)` (default) or `JSON`. The `JSON` value uses SQL Server 2025's
|
|
102
|
+
native JSON type and requires SQL Server 2025 or later; the value is
|
|
103
|
+
case-insensitive. Omit the option for the default `NVARCHAR(MAX)` behaviour.
|
|
97
104
|
- `--truncate` - Truncate table before loading
|
|
98
105
|
- `--no-create-table` - Don't create table if it doesn't exist
|
|
99
106
|
|
|
@@ -292,6 +299,45 @@ const sqlOnFhir = new SqlOnFhir({
|
|
|
292
299
|
});
|
|
293
300
|
```
|
|
294
301
|
|
|
302
|
+
> **Note:** `SqlOnFhir` configures the transpiler only. The storage type of the
|
|
303
|
+
> `json` column - `NVARCHAR(MAX)` (default) or the native `JSON` type - is a
|
|
304
|
+
> loader concern, set with the `--resource-json-data-type` CLI flag or the
|
|
305
|
+
> `resourceJsonDataType` option of `loadNdjsonFiles`, not via
|
|
306
|
+
> `new SqlOnFhir({ ... })`. The transpiler is storage-type agnostic: the same
|
|
307
|
+
> transpiled query works over either column type, so your ViewDefinitions and
|
|
308
|
+
> transpiler configuration are unchanged for a native `JSON` column.
|
|
309
|
+
|
|
310
|
+
### Loading into a native JSON column
|
|
311
|
+
|
|
312
|
+
The loader can store each resource in SQL Server 2025's native `JSON` type
|
|
313
|
+
instead of `NVARCHAR(MAX)`:
|
|
314
|
+
|
|
315
|
+
```javascript
|
|
316
|
+
import { loadNdjsonFiles } from 'sof-mssql';
|
|
317
|
+
|
|
318
|
+
await loadNdjsonFiles({
|
|
319
|
+
directory: './data',
|
|
320
|
+
database: {
|
|
321
|
+
host: 'localhost',
|
|
322
|
+
user: 'sa',
|
|
323
|
+
password: process.env.MSSQL_PASSWORD,
|
|
324
|
+
database: 'fhir',
|
|
325
|
+
},
|
|
326
|
+
// "NVARCHAR(MAX)" (default) or "JSON" (SQL Server 2025+); case-insensitive.
|
|
327
|
+
resourceJsonDataType: 'JSON',
|
|
328
|
+
});
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
An invalid value is rejected with a clear error before any database connection
|
|
332
|
+
is opened. If the target table already exists with a `json` column that is the
|
|
333
|
+
other supported type (`NVARCHAR(MAX)` when `JSON` was requested, or vice versa),
|
|
334
|
+
the loader prints a warning naming both types and loads into the existing table
|
|
335
|
+
without altering it. If the existing `json` column is neither `NVARCHAR(MAX)` nor
|
|
336
|
+
native `JSON` - for example a bounded `VARCHAR(100)` or `TEXT` that cannot hold a
|
|
337
|
+
serialised FHIR resource - the loader fails fast with an error naming the
|
|
338
|
+
offending type, before any rows are loaded, rather than writing into a column
|
|
339
|
+
that would truncate or corrupt the data.
|
|
340
|
+
|
|
295
341
|
### Working with ViewDefinition strings
|
|
296
342
|
|
|
297
343
|
```javascript
|
|
@@ -453,6 +499,18 @@ CREATE INDEX [IX_fhir_resources_resource_type]
|
|
|
453
499
|
ON [dbo].[fhir_resources] ([resource_type]);
|
|
454
500
|
```
|
|
455
501
|
|
|
502
|
+
On SQL Server 2025 or later the `json` column may instead use the native `JSON`
|
|
503
|
+
type for more efficient storage, with the rest of the table unchanged:
|
|
504
|
+
|
|
505
|
+
```sql
|
|
506
|
+
[json] JSON NOT NULL
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
`NVARCHAR(MAX)` is the default and works on SQL Server 2017+. The native `JSON`
|
|
510
|
+
type is opt-in (`--resource-json-data-type JSON` or `resourceJsonDataType:
|
|
511
|
+
"JSON"`) and requires SQL Server 2025 or later. The same transpiled query SQL
|
|
512
|
+
runs over either storage type.
|
|
513
|
+
|
|
456
514
|
The generated queries use:
|
|
457
515
|
|
|
458
516
|
- `resource_type` column for filtering by FHIR resource type (indexed for
|
|
@@ -473,6 +531,36 @@ creates the table with the correct structure. All FHIR resources are stored in a
|
|
|
473
531
|
single table (default: `fhir_resources`), with the resource type extracted from
|
|
474
532
|
the filename.
|
|
475
533
|
|
|
534
|
+
### Comparing NVARCHAR(MAX) and native JSON storage
|
|
535
|
+
|
|
536
|
+
The native `JSON` type stores documents in a pre-parsed binary form, which
|
|
537
|
+
Microsoft documents as roughly an 18% storage reduction; actual figures depend
|
|
538
|
+
on the dataset. Because timings and storage are environment-dependent, this is a
|
|
539
|
+
manual procedure rather than an automated test. To compare the two storage types
|
|
540
|
+
for your own data on a single SQL Server 2025 instance:
|
|
541
|
+
|
|
542
|
+
1. Prepare a representative NDJSON dataset (for example, a few hundred megabytes
|
|
543
|
+
of FHIR resources).
|
|
544
|
+
2. Load the same data into two separate tables, one per storage type:
|
|
545
|
+
|
|
546
|
+
```bash
|
|
547
|
+
npx sof-mssql load ./data --table-name fhir_nvarchar
|
|
548
|
+
npx sof-mssql load ./data --table-name fhir_json --resource-json-data-type JSON
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
3. Compare storage with `sp_spaceused`, recording `data` and `reserved` for
|
|
552
|
+
each:
|
|
553
|
+
|
|
554
|
+
```sql
|
|
555
|
+
EXEC sp_spaceused 'dbo.fhir_nvarchar';
|
|
556
|
+
EXEC sp_spaceused 'dbo.fhir_json';
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
4. Compare read performance by running an identical transpiled ViewDefinition
|
|
560
|
+
query against each table several times with `SET STATISTICS TIME ON;`,
|
|
561
|
+
discarding the first (cold-cache) run and recording the elapsed times.
|
|
562
|
+
5. Record the dataset description, row counts, storage figures and timings.
|
|
563
|
+
|
|
476
564
|
## Contributing
|
|
477
565
|
|
|
478
566
|
Contributions are welcome! Please read our [CONTRIBUTING](CONTRIBUTING.md)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression tests for json-safe array-emptiness checks in the transpiler.
|
|
3
|
+
*
|
|
4
|
+
* The exists()/empty() FHIRPath functions detect an empty collection by
|
|
5
|
+
* comparing the extracted value to the literal '[]'. Over an NVARCHAR(MAX)
|
|
6
|
+
* column JSON_QUERY returns nvarchar, so the comparison works; over SQL Server
|
|
7
|
+
* 2025's native json column JSON_QUERY returns a json-typed value, and
|
|
8
|
+
* comparing json to a varchar literal raises "the data types json and varchar
|
|
9
|
+
* are incompatible". The fix coerces the operand with CAST(... AS NVARCHAR(MAX))
|
|
10
|
+
* before the comparison, which is a no-op on nvarchar sources and makes the SQL
|
|
11
|
+
* valid on json columns (Constitution Principle II; FR-011/SC-002).
|
|
12
|
+
*
|
|
13
|
+
* These tests assert the generated SQL invariant without a database, so they
|
|
14
|
+
* guard the behaviour on every SQL Server version.
|
|
15
|
+
*
|
|
16
|
+
* @author John Grimes
|
|
17
|
+
*/
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=jsonSafeEmptiness.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonSafeEmptiness.test.d.ts","sourceRoot":"","sources":["../../src/fhirpath/jsonSafeEmptiness.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Regression tests for json-safe array-emptiness checks in the transpiler.
|
|
4
|
+
*
|
|
5
|
+
* The exists()/empty() FHIRPath functions detect an empty collection by
|
|
6
|
+
* comparing the extracted value to the literal '[]'. Over an NVARCHAR(MAX)
|
|
7
|
+
* column JSON_QUERY returns nvarchar, so the comparison works; over SQL Server
|
|
8
|
+
* 2025's native json column JSON_QUERY returns a json-typed value, and
|
|
9
|
+
* comparing json to a varchar literal raises "the data types json and varchar
|
|
10
|
+
* are incompatible". The fix coerces the operand with CAST(... AS NVARCHAR(MAX))
|
|
11
|
+
* before the comparison, which is a no-op on nvarchar sources and makes the SQL
|
|
12
|
+
* valid on json columns (Constitution Principle II; FR-011/SC-002).
|
|
13
|
+
*
|
|
14
|
+
* These tests assert the generated SQL invariant without a database, so they
|
|
15
|
+
* guard the behaviour on every SQL Server version.
|
|
16
|
+
*
|
|
17
|
+
* @author John Grimes
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
const vitest_1 = require("vitest");
|
|
21
|
+
const index_1 = require("../index");
|
|
22
|
+
/**
|
|
23
|
+
* Transpile a single boolean column for the given FHIRPath expression.
|
|
24
|
+
*
|
|
25
|
+
* @param path - The FHIRPath expression to transpile.
|
|
26
|
+
* @returns The generated T-SQL.
|
|
27
|
+
*/
|
|
28
|
+
function transpilePath(path) {
|
|
29
|
+
const sof = new index_1.SqlOnFhir();
|
|
30
|
+
return sof.transpile({
|
|
31
|
+
resource: "Patient",
|
|
32
|
+
status: "active",
|
|
33
|
+
select: [{ column: [{ name: "x", path, type: "boolean" }] }],
|
|
34
|
+
}).sql;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Report whether the SQL contains a comparison against the literal '[]' that is
|
|
38
|
+
* not coerced to nvarchar first. Such a bare comparison fails on a native json
|
|
39
|
+
* column.
|
|
40
|
+
*
|
|
41
|
+
* @param sql - The generated T-SQL.
|
|
42
|
+
* @returns true if a bare (uncast) '[]' comparison is present.
|
|
43
|
+
*/
|
|
44
|
+
function hasBareArrayLiteralComparison(sql) {
|
|
45
|
+
const comparison = /(?:!=|=)\s*'\[\]'/g;
|
|
46
|
+
let match;
|
|
47
|
+
while ((match = comparison.exec(sql)) !== null) {
|
|
48
|
+
const preceding = sql.slice(0, match.index).trimEnd();
|
|
49
|
+
if (!preceding.endsWith("AS NVARCHAR(MAX))")) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
(0, vitest_1.describe)("json-safe emptiness checks", () => {
|
|
56
|
+
// Each of these expressions exercises a '[]' comparison in exists()/empty().
|
|
57
|
+
const expressions = [
|
|
58
|
+
"name.exists()",
|
|
59
|
+
"name.given.exists()",
|
|
60
|
+
"name.empty()",
|
|
61
|
+
"name.given.empty()",
|
|
62
|
+
];
|
|
63
|
+
for (const path of expressions) {
|
|
64
|
+
(0, vitest_1.it)(`emits no bare '[]' comparison for ${path}`, () => {
|
|
65
|
+
const sql = transpilePath(path);
|
|
66
|
+
(0, vitest_1.expect)(hasBareArrayLiteralComparison(sql)).toBe(false);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
(0, vitest_1.it)("wraps the exists() array check in CAST(... AS NVARCHAR(MAX))", () => {
|
|
70
|
+
const sql = transpilePath("name.exists()");
|
|
71
|
+
(0, vitest_1.expect)(sql).toContain("CAST(JSON_QUERY(r.json, '$.name') AS NVARCHAR(MAX)) != '[]'");
|
|
72
|
+
});
|
|
73
|
+
(0, vitest_1.it)("still guards exists() against a null array with IS NOT NULL", () => {
|
|
74
|
+
// The presence check on the raw value is valid on a json column and must
|
|
75
|
+
// remain so the empty-vs-absent distinction is preserved.
|
|
76
|
+
const sql = transpilePath("name.exists()");
|
|
77
|
+
(0, vitest_1.expect)(sql).toContain("JSON_QUERY(r.json, '$.name') IS NOT NULL");
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
//# sourceMappingURL=jsonSafeEmptiness.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonSafeEmptiness.test.js","sourceRoot":"","sources":["../../src/fhirpath/jsonSafeEmptiness.test.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;GAgBG;;AAEH,mCAA8C;AAC9C,oCAAqC;AAErC;;;;;GAKG;AACH,SAAS,aAAa,CAAC,IAAY;IACjC,MAAM,GAAG,GAAG,IAAI,iBAAS,EAAE,CAAC;IAC5B,OAAO,GAAG,CAAC,SAAS,CAAC;QACnB,QAAQ,EAAE,SAAS;QACnB,MAAM,EAAE,QAAQ;QAChB,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;KAC7D,CAAC,CAAC,GAAG,CAAC;AACT,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,6BAA6B,CAAC,GAAW;IAChD,MAAM,UAAU,GAAG,oBAAoB,CAAC;IACxC,IAAI,KAA6B,CAAC;IAClC,OAAO,CAAC,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAC/C,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;QACtD,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;YAC7C,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,IAAA,iBAAQ,EAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,6EAA6E;IAC7E,MAAM,WAAW,GAAG;QAClB,eAAe;QACf,qBAAqB;QACrB,cAAc;QACd,oBAAoB;KACrB,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,IAAA,WAAE,EAAC,qCAAqC,IAAI,EAAE,EAAE,GAAG,EAAE;YACnD,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;YAChC,IAAA,eAAM,EAAC,6BAA6B,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzD,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAA,WAAE,EAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,MAAM,GAAG,GAAG,aAAa,CAAC,eAAe,CAAC,CAAC;QAC3C,IAAA,eAAM,EAAC,GAAG,CAAC,CAAC,SAAS,CACnB,6DAA6D,CAC9D,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,yEAAyE;QACzE,0DAA0D;QAC1D,MAAM,GAAG,GAAG,aAAa,CAAC,eAAe,CAAC,CAAC;QAC3C,IAAA,eAAM,EAAC,GAAG,CAAC,CAAC,SAAS,CAAC,0CAA0C,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* FHIRPath to T-SQL visitor implementation using ANTLR.
|
|
3
|
+
*
|
|
4
|
+
* @author John Grimes
|
|
3
5
|
*/
|
|
4
6
|
import { AbstractParseTreeVisitor } from "antlr4ts/tree/AbstractParseTreeVisitor";
|
|
5
7
|
import { AdditiveExpressionContext, AndExpressionContext, BooleanLiteralContext, DateLiteralContext, DateTimeLiteralContext, EntireExpressionContext, EqualityExpressionContext, ExternalConstantContext, ExternalConstantTermContext, FunctionContext, FunctionInvocationContext, IdentifierContext, ImpliesExpressionContext, IndexerExpressionContext, IndexInvocationContext, InequalityExpressionContext, InvocationExpressionContext, InvocationTermContext, LiteralTermContext, LongNumberLiteralContext, MemberInvocationContext, MembershipExpressionContext, MultiplicativeExpressionContext, NullLiteralContext, NumberLiteralContext, OrExpressionContext, ParenthesizedTermContext, PolarityExpressionContext, QualifiedIdentifierContext, QuantityContext, QuantityLiteralContext, StringLiteralContext, TermExpressionContext, ThisInvocationContext, TimeLiteralContext, TotalInvocationContext, TypeExpressionContext, UnionExpressionContext } from "../generated/grammar/fhirpathParser";
|
|
@@ -13,6 +15,8 @@ export interface TranspilerContext {
|
|
|
13
15
|
currentForEachAlias?: string;
|
|
14
16
|
forEachSource?: string;
|
|
15
17
|
forEachPath?: string;
|
|
18
|
+
rowIndexExpr?: string;
|
|
19
|
+
boundaryType?: string;
|
|
16
20
|
testId?: string;
|
|
17
21
|
}
|
|
18
22
|
export declare class FHIRPathToTSqlVisitor extends AbstractParseTreeVisitor<string> implements fhirpathVisitor<string> {
|
|
@@ -73,6 +77,17 @@ export declare class FHIRPathToTSqlVisitor extends AbstractParseTreeVisitor<stri
|
|
|
73
77
|
private handleJsonValueMember;
|
|
74
78
|
private shouldAddArrayIndexForField;
|
|
75
79
|
private handleFunctionInvocation;
|
|
80
|
+
/**
|
|
81
|
+
* Resolves the FHIR datatype named by an `ofType(X)` invocation when that
|
|
82
|
+
* invocation is applied directly to the given expression (i.e. the expression
|
|
83
|
+
* is `<something>.ofType(X)`). Returns `null` when the expression is not a
|
|
84
|
+
* direct `ofType()` call, including when a member access intervenes between
|
|
85
|
+
* the `ofType()` and the caller.
|
|
86
|
+
*
|
|
87
|
+
* @param baseExpr - The base expression a function is being applied to.
|
|
88
|
+
* @returns The raw ofType datatype name (e.g. "dateTime"), or null.
|
|
89
|
+
*/
|
|
90
|
+
private extractDirectOfTypeName;
|
|
76
91
|
private handleOfTypeFunctionInvocation;
|
|
77
92
|
private handleGetReferenceKeyFunctionInvocation;
|
|
78
93
|
/**
|
|
@@ -148,6 +163,69 @@ export declare class FHIRPathToTSqlVisitor extends AbstractParseTreeVisitor<stri
|
|
|
148
163
|
private handleGetReferenceKeyFunctionWithType;
|
|
149
164
|
private handleNotFunction;
|
|
150
165
|
private handleExtensionFunction;
|
|
166
|
+
/**
|
|
167
|
+
* Generates inline T-SQL for the FHIRPath `lowBoundary()` / `highBoundary()`
|
|
168
|
+
* functions. The boundary is the least (low) or greatest (high) value
|
|
169
|
+
* consistent with the input's stated precision, expressed at the maximum
|
|
170
|
+
* precision for its FHIR datatype (FR-004 to FR-007, FR-010).
|
|
171
|
+
*
|
|
172
|
+
* The governing datatype is taken from an explicit `ofType()` applied directly
|
|
173
|
+
* to the input where present (`this.context.boundaryType`), otherwise inferred
|
|
174
|
+
* from the value's lexical form at SQL runtime. An absent source element
|
|
175
|
+
* yields SQL NULL for every branch (FR-009). All logic is emitted inline so
|
|
176
|
+
* the query stays self-contained, requiring no pre-installed database object
|
|
177
|
+
* (FR-013).
|
|
178
|
+
*
|
|
179
|
+
* @param functionName - Either "lowBoundary" or "highBoundary".
|
|
180
|
+
* @param args - Function arguments; a non-empty list is the unsupported
|
|
181
|
+
* explicit-precision form and is rejected.
|
|
182
|
+
* @returns A T-SQL scalar expression computing the boundary value.
|
|
183
|
+
* @throws If called with an explicit-precision argument, or resolved (via
|
|
184
|
+
* ofType) to a datatype for which boundaries are not supported (FR-008).
|
|
185
|
+
*/
|
|
151
186
|
private handleBoundaryFunction;
|
|
187
|
+
/**
|
|
188
|
+
* Boundary SQL for a value of unknown datatype, classified from its lexical
|
|
189
|
+
* form at SQL runtime (research Decision 2): a `T` marks a dateTime, a `:`
|
|
190
|
+
* marks a time, an interior `-` marks a date, and anything else is treated as
|
|
191
|
+
* a decimal. NULL propagates to NULL (FR-009).
|
|
192
|
+
*/
|
|
193
|
+
private lexicalBoundarySql;
|
|
194
|
+
/**
|
|
195
|
+
* Boundary SQL for a `date` value (maximum precision = day): a partial value
|
|
196
|
+
* is padded to a full date. For `highBoundary`, a year-month resolves to the
|
|
197
|
+
* last day of the month via EOMONTH (FR-005). NULL propagates to NULL.
|
|
198
|
+
*/
|
|
199
|
+
private dateBoundarySql;
|
|
200
|
+
/**
|
|
201
|
+
* Boundary SQL for a `dateTime` value (maximum precision = millisecond +
|
|
202
|
+
* timezone). A date-shaped value (no time component) is padded to a full
|
|
203
|
+
* instant filling the minimum components and the FHIR extreme offset `+14:00`
|
|
204
|
+
* for `lowBoundary`, or the maximum components and `-12:00` for `highBoundary`
|
|
205
|
+
* (FR-006). A value already carrying a time has its time component padded to
|
|
206
|
+
* millisecond precision and the extreme offset appended; explicit offsets in
|
|
207
|
+
* such values are not exercised by the suite. NULL propagates to NULL.
|
|
208
|
+
*/
|
|
209
|
+
private dateTimeBoundarySql;
|
|
210
|
+
/**
|
|
211
|
+
* Boundary SQL for a `time` value (maximum precision = millisecond): the value
|
|
212
|
+
* is padded to `HH:MM:SS.fff`, filling absent components with their minimum
|
|
213
|
+
* (`:00.000`) for `lowBoundary` or maximum (`:59.999`) for `highBoundary`
|
|
214
|
+
* (FR-007). NULL propagates to NULL.
|
|
215
|
+
*/
|
|
216
|
+
private timeBoundarySql;
|
|
217
|
+
/**
|
|
218
|
+
* Pads a bare time component (`HH:MM` or `HH:MM:SS`) to millisecond precision,
|
|
219
|
+
* filling absent seconds/milliseconds with their minimum or maximum.
|
|
220
|
+
*/
|
|
221
|
+
private padTimeComponent;
|
|
222
|
+
/**
|
|
223
|
+
* Boundary SQL for a `decimal` value (FR-010). With N fractional digits the
|
|
224
|
+
* value is known to within half a unit in the last place, so the boundary is
|
|
225
|
+
* `value ∓ 0.5 × 10⁻ᴺ` (e.g. `1.0` → `0.95` / `1.05`). The half-unit delta is
|
|
226
|
+
* built as a decimal literal from the runtime fractional-digit count to avoid
|
|
227
|
+
* relying on POWER's scale behaviour. NULL propagates to NULL.
|
|
228
|
+
*/
|
|
229
|
+
private decimalBoundarySql;
|
|
152
230
|
}
|
|
153
231
|
//# sourceMappingURL=visitor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"visitor.d.ts","sourceRoot":"","sources":["../../src/fhirpath/visitor.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"visitor.d.ts","sourceRoot":"","sources":["../../src/fhirpath/visitor.ts"],"names":[],"mappings":"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"}
|