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.
Files changed (88) hide show
  1. package/README.md +88 -0
  2. package/dist/fhirpath/jsonSafeEmptiness.test.d.ts +19 -0
  3. package/dist/fhirpath/jsonSafeEmptiness.test.d.ts.map +1 -0
  4. package/dist/fhirpath/jsonSafeEmptiness.test.js +80 -0
  5. package/dist/fhirpath/jsonSafeEmptiness.test.js.map +1 -0
  6. package/dist/fhirpath/visitor.d.ts +78 -0
  7. package/dist/fhirpath/visitor.d.ts.map +1 -1
  8. package/dist/fhirpath/visitor.js +228 -23
  9. package/dist/fhirpath/visitor.js.map +1 -1
  10. package/dist/load.d.ts +9 -0
  11. package/dist/load.d.ts.map +1 -1
  12. package/dist/load.js +4 -0
  13. package/dist/load.js.map +1 -1
  14. package/dist/loader/feedback.integration.test.d.ts +14 -0
  15. package/dist/loader/feedback.integration.test.d.ts.map +1 -0
  16. package/dist/loader/feedback.integration.test.js +76 -0
  17. package/dist/loader/feedback.integration.test.js.map +1 -0
  18. package/dist/loader/index.d.ts.map +1 -1
  19. package/dist/loader/index.js +19 -6
  20. package/dist/loader/index.js.map +1 -1
  21. package/dist/loader/jsonColumn.integration.test.d.ts +13 -0
  22. package/dist/loader/jsonColumn.integration.test.d.ts.map +1 -0
  23. package/dist/loader/jsonColumn.integration.test.js +86 -0
  24. package/dist/loader/jsonColumn.integration.test.js.map +1 -0
  25. package/dist/loader/tables.d.ts +96 -3
  26. package/dist/loader/tables.d.ts.map +1 -1
  27. package/dist/loader/tables.js +182 -16
  28. package/dist/loader/tables.js.map +1 -1
  29. package/dist/loader/tables.test.d.ts +12 -0
  30. package/dist/loader/tables.test.d.ts.map +1 -0
  31. package/dist/loader/tables.test.js +130 -0
  32. package/dist/loader/tables.test.js.map +1 -0
  33. package/dist/loader/types.d.ts +9 -0
  34. package/dist/loader/types.d.ts.map +1 -1
  35. package/dist/parser.d.ts +1 -1
  36. package/dist/parser.js +1 -1
  37. package/dist/queryGenerator/treeWalker/compile.js +0 -1
  38. package/dist/queryGenerator/treeWalker/compile.js.map +1 -1
  39. package/dist/queryGenerator/treeWalker/cteTemplates.d.ts +9 -1
  40. package/dist/queryGenerator/treeWalker/cteTemplates.d.ts.map +1 -1
  41. package/dist/queryGenerator/treeWalker/cteTemplates.js +39 -3
  42. package/dist/queryGenerator/treeWalker/cteTemplates.js.map +1 -1
  43. package/dist/queryGenerator/treeWalker/operators/forEach.d.ts +4 -2
  44. package/dist/queryGenerator/treeWalker/operators/forEach.d.ts.map +1 -1
  45. package/dist/queryGenerator/treeWalker/operators/forEach.js +11 -5
  46. package/dist/queryGenerator/treeWalker/operators/forEach.js.map +1 -1
  47. package/dist/queryGenerator/treeWalker/operators/repeat.d.ts +2 -0
  48. package/dist/queryGenerator/treeWalker/operators/repeat.d.ts.map +1 -1
  49. package/dist/queryGenerator/treeWalker/operators/repeat.js +12 -0
  50. package/dist/queryGenerator/treeWalker/operators/repeat.js.map +1 -1
  51. package/dist/queryGenerator/treeWalker/types.d.ts +0 -2
  52. package/dist/queryGenerator/treeWalker/types.d.ts.map +1 -1
  53. package/dist/tests/load.test.d.ts +11 -0
  54. package/dist/tests/load.test.d.ts.map +1 -0
  55. package/dist/tests/load.test.js +37 -0
  56. package/dist/tests/load.test.js.map +1 -0
  57. package/dist/tests/utils/database.d.ts +2 -0
  58. package/dist/tests/utils/database.d.ts.map +1 -1
  59. package/dist/tests/utils/database.js +61 -4
  60. package/dist/tests/utils/database.js.map +1 -1
  61. package/dist/tests/utils/generator.d.ts +2 -0
  62. package/dist/tests/utils/generator.d.ts.map +1 -1
  63. package/dist/tests/utils/generator.js +24 -0
  64. package/dist/tests/utils/generator.js.map +1 -1
  65. package/dist/tests/utils/loaderIntegration.d.ts +59 -0
  66. package/dist/tests/utils/loaderIntegration.d.ts.map +1 -0
  67. package/dist/tests/utils/loaderIntegration.js +137 -0
  68. package/dist/tests/utils/loaderIntegration.js.map +1 -0
  69. package/dist/tests/utils/reporter.d.ts +1 -1
  70. package/dist/tests/utils/reporter.d.ts.map +1 -1
  71. package/dist/tests/utils/reporter.js +5 -4
  72. package/dist/tests/utils/reporter.js.map +1 -1
  73. package/dist/tests/utils/serverCapabilities.d.ts +23 -0
  74. package/dist/tests/utils/serverCapabilities.d.ts.map +1 -0
  75. package/dist/tests/utils/serverCapabilities.js +35 -0
  76. package/dist/tests/utils/serverCapabilities.js.map +1 -0
  77. package/dist/tests/utils/types.d.ts +1 -1
  78. package/dist/tests/utils/types.js +1 -1
  79. package/dist/tests/validation.test.d.ts +13 -0
  80. package/dist/tests/validation.test.d.ts.map +1 -0
  81. package/dist/tests/validation.test.js +81 -0
  82. package/dist/tests/validation.test.js.map +1 -0
  83. package/dist/types.d.ts +1 -1
  84. package/dist/validation.d.ts +40 -0
  85. package/dist/validation.d.ts.map +1 -1
  86. package/dist/validation.js +56 -0
  87. package/dist/validation.js.map +1 -1
  88. 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":"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"}
1
+ {"version":3,"file":"visitor.d.ts","sourceRoot":"","sources":["../../src/fhirpath/visitor.ts"],"names":[],"mappings":"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"}