@memberjunction/metadata-sync 6.2.0-edge.4 → 6.2.0-edge.6

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 (143) hide show
  1. package/README.md +120 -52
  2. package/dist/config.d.ts +18 -0
  3. package/dist/config.d.ts.map +1 -1
  4. package/dist/config.js.map +1 -1
  5. package/dist/index.d.ts +3 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/lib/RecordProcessor.d.ts +98 -0
  10. package/dist/lib/RecordProcessor.d.ts.map +1 -1
  11. package/dist/lib/RecordProcessor.js +424 -22
  12. package/dist/lib/RecordProcessor.js.map +1 -1
  13. package/dist/lib/RelatedEntityHandler.d.ts +4 -1
  14. package/dist/lib/RelatedEntityHandler.d.ts.map +1 -1
  15. package/dist/lib/RelatedEntityHandler.js +49 -11
  16. package/dist/lib/RelatedEntityHandler.js.map +1 -1
  17. package/dist/lib/created-record-key.d.ts +34 -0
  18. package/dist/lib/created-record-key.d.ts.map +1 -0
  19. package/dist/lib/created-record-key.js +46 -0
  20. package/dist/lib/created-record-key.js.map +1 -0
  21. package/dist/lib/deletion-auditor.d.ts.map +1 -1
  22. package/dist/lib/deletion-auditor.js +34 -3
  23. package/dist/lib/deletion-auditor.js.map +1 -1
  24. package/dist/lib/entity-field-names.d.ts +11 -0
  25. package/dist/lib/entity-field-names.d.ts.map +1 -0
  26. package/dist/lib/entity-field-names.js +15 -0
  27. package/dist/lib/entity-field-names.js.map +1 -0
  28. package/dist/lib/env-derived-fields.d.ts +23 -0
  29. package/dist/lib/env-derived-fields.d.ts.map +1 -0
  30. package/dist/lib/env-derived-fields.js +53 -0
  31. package/dist/lib/env-derived-fields.js.map +1 -0
  32. package/dist/lib/env-display.d.ts +54 -0
  33. package/dist/lib/env-display.d.ts.map +1 -0
  34. package/dist/lib/env-display.js +156 -0
  35. package/dist/lib/env-display.js.map +1 -0
  36. package/dist/lib/env-reference.d.ts +104 -0
  37. package/dist/lib/env-reference.d.ts.map +1 -0
  38. package/dist/lib/env-reference.js +241 -0
  39. package/dist/lib/env-reference.js.map +1 -0
  40. package/dist/lib/env-sql-redactor.d.ts +74 -0
  41. package/dist/lib/env-sql-redactor.d.ts.map +1 -0
  42. package/dist/lib/env-sql-redactor.js +232 -0
  43. package/dist/lib/env-sql-redactor.js.map +1 -0
  44. package/dist/lib/existing-record-index.d.ts +70 -0
  45. package/dist/lib/existing-record-index.d.ts.map +1 -0
  46. package/dist/lib/existing-record-index.js +158 -0
  47. package/dist/lib/existing-record-index.js.map +1 -0
  48. package/dist/lib/ignore-directories.d.ts +10 -0
  49. package/dist/lib/ignore-directories.d.ts.map +1 -0
  50. package/dist/lib/ignore-directories.js +12 -0
  51. package/dist/lib/ignore-directories.js.map +1 -0
  52. package/dist/lib/json-record-lines.d.ts +17 -0
  53. package/dist/lib/json-record-lines.d.ts.map +1 -0
  54. package/dist/lib/json-record-lines.js +170 -0
  55. package/dist/lib/json-record-lines.js.map +1 -0
  56. package/dist/lib/json-subproperty-externalization.d.ts.map +1 -1
  57. package/dist/lib/json-subproperty-externalization.js +3 -1
  58. package/dist/lib/json-subproperty-externalization.js.map +1 -1
  59. package/dist/lib/lookup-reference.d.ts +38 -0
  60. package/dist/lib/lookup-reference.d.ts.map +1 -0
  61. package/dist/lib/lookup-reference.js +62 -0
  62. package/dist/lib/lookup-reference.js.map +1 -0
  63. package/dist/lib/lookup-sql.d.ts +108 -0
  64. package/dist/lib/lookup-sql.d.ts.map +1 -0
  65. package/dist/lib/lookup-sql.js +231 -0
  66. package/dist/lib/lookup-sql.js.map +1 -0
  67. package/dist/lib/natural-key-preflight.d.ts +86 -0
  68. package/dist/lib/natural-key-preflight.d.ts.map +1 -0
  69. package/dist/lib/natural-key-preflight.js +173 -0
  70. package/dist/lib/natural-key-preflight.js.map +1 -0
  71. package/dist/lib/provider-utils.d.ts.map +1 -1
  72. package/dist/lib/provider-utils.js +2 -4
  73. package/dist/lib/provider-utils.js.map +1 -1
  74. package/dist/lib/pull-option-overrides.d.ts +24 -0
  75. package/dist/lib/pull-option-overrides.d.ts.map +1 -0
  76. package/dist/lib/pull-option-overrides.js +39 -0
  77. package/dist/lib/pull-option-overrides.js.map +1 -0
  78. package/dist/lib/push-directories.d.ts +42 -0
  79. package/dist/lib/push-directories.d.ts.map +1 -0
  80. package/dist/lib/push-directories.js +71 -0
  81. package/dist/lib/push-directories.js.map +1 -0
  82. package/dist/lib/push-when.d.ts +38 -0
  83. package/dist/lib/push-when.d.ts.map +1 -0
  84. package/dist/lib/push-when.js +84 -0
  85. package/dist/lib/push-when.js.map +1 -0
  86. package/dist/lib/record-dependency-analyzer.d.ts +11 -2
  87. package/dist/lib/record-dependency-analyzer.d.ts.map +1 -1
  88. package/dist/lib/record-dependency-analyzer.js +48 -71
  89. package/dist/lib/record-dependency-analyzer.js.map +1 -1
  90. package/dist/lib/record-file-glob.d.ts +11 -0
  91. package/dist/lib/record-file-glob.d.ts.map +1 -0
  92. package/dist/lib/record-file-glob.js +10 -0
  93. package/dist/lib/record-file-glob.js.map +1 -0
  94. package/dist/lib/reference-parser.d.ts +3 -2
  95. package/dist/lib/reference-parser.d.ts.map +1 -1
  96. package/dist/lib/reference-parser.js +27 -27
  97. package/dist/lib/reference-parser.js.map +1 -1
  98. package/dist/lib/schema-placeholders.d.ts +29 -0
  99. package/dist/lib/schema-placeholders.d.ts.map +1 -0
  100. package/dist/lib/schema-placeholders.js +72 -0
  101. package/dist/lib/schema-placeholders.js.map +1 -0
  102. package/dist/lib/sql-logger.d.ts +9 -1
  103. package/dist/lib/sql-logger.d.ts.map +1 -1
  104. package/dist/lib/sql-logger.js +12 -7
  105. package/dist/lib/sql-logger.js.map +1 -1
  106. package/dist/lib/sql-set-line.d.ts +26 -0
  107. package/dist/lib/sql-set-line.d.ts.map +1 -0
  108. package/dist/lib/sql-set-line.js +84 -0
  109. package/dist/lib/sql-set-line.js.map +1 -0
  110. package/dist/lib/sync-engine.d.ts +58 -4
  111. package/dist/lib/sync-engine.d.ts.map +1 -1
  112. package/dist/lib/sync-engine.js +243 -59
  113. package/dist/lib/sync-engine.js.map +1 -1
  114. package/dist/lib/sync-state-manager.d.ts +7 -3
  115. package/dist/lib/sync-state-manager.d.ts.map +1 -1
  116. package/dist/lib/sync-state-manager.js +19 -4
  117. package/dist/lib/sync-state-manager.js.map +1 -1
  118. package/dist/lib/watch-file-filter.d.ts +31 -0
  119. package/dist/lib/watch-file-filter.d.ts.map +1 -0
  120. package/dist/lib/watch-file-filter.js +64 -0
  121. package/dist/lib/watch-file-filter.js.map +1 -0
  122. package/dist/plugins/index.d.ts.map +1 -1
  123. package/dist/plugins/index.js +44 -14
  124. package/dist/plugins/index.js.map +1 -1
  125. package/dist/services/PullService.d.ts +41 -1
  126. package/dist/services/PullService.d.ts.map +1 -1
  127. package/dist/services/PullService.js +261 -101
  128. package/dist/services/PullService.js.map +1 -1
  129. package/dist/services/PushService.d.ts +80 -0
  130. package/dist/services/PushService.d.ts.map +1 -1
  131. package/dist/services/PushService.js +531 -189
  132. package/dist/services/PushService.js.map +1 -1
  133. package/dist/services/ValidationService.d.ts +43 -8
  134. package/dist/services/ValidationService.d.ts.map +1 -1
  135. package/dist/services/ValidationService.js +338 -127
  136. package/dist/services/ValidationService.js.map +1 -1
  137. package/dist/services/WatchService.d.ts +28 -0
  138. package/dist/services/WatchService.d.ts.map +1 -1
  139. package/dist/services/WatchService.js +226 -74
  140. package/dist/services/WatchService.js.map +1 -1
  141. package/dist/types/validation.d.ts +6 -0
  142. package/dist/types/validation.d.ts.map +1 -1
  143. package/package.json +17 -16
@@ -0,0 +1,108 @@
1
+ /**
2
+ * @fileoverview Writes `@lookup` values into the push SQL log as subqueries on the natural key
3
+ * (#5626, #3619).
4
+ *
5
+ * A lookup resolves at push time to this database's ID. When the push is captured as a migration,
6
+ * that literal ID matches nothing on a database whose row has another ID (one CodeGen created, or
7
+ * one created before a baseline shipped), so an update is a silent no-op and an insert fails on a
8
+ * foreign key. For a primary-key or foreign-key field whose value came from a `@lookup`, the logged
9
+ * `SET @<Field>_<hash> = '<id>'` line is replaced with `SET @<Field>_<hash> = (SELECT …)` on the
10
+ * lookup's criteria. Nested lookups nest. The subquery names the real schema; the logger's schema
11
+ * placeholder rewrite runs after `TransformStatement` and turns it into a placeholder.
12
+ *
13
+ * Only SQL Server save statements have SET lines, so a PostgreSQL log keeps literal IDs.
14
+ * @module lookup-sql
15
+ */
16
+ import type { BaseEntity, EntityInfo } from '@memberjunction/core';
17
+ import type { SyncEngine } from './sync-engine.js';
18
+ /** One `Field=Value` pair of a `@lookup`. A nested `@lookup` value keeps its own source. */
19
+ export interface LookupSqlCriterion {
20
+ FieldName: string;
21
+ /** The resolved value as text, or the source of a nested `@lookup`. */
22
+ Value: string | LookupSqlSource;
23
+ }
24
+ /** What a `@lookup` matched on, kept so the SQL log can match on it too. */
25
+ export interface LookupSqlSource {
26
+ /** The `@lookup:` text as written. */
27
+ Expression: string;
28
+ EntityName: string;
29
+ Criteria: LookupSqlCriterion[];
30
+ /** True when a criterion value (or a nested lookup's) came from `@env:`. */
31
+ EnvSourced: boolean;
32
+ }
33
+ /** A built subquery (`Sql`), or why none could be built (`Problem`). Exactly one is set. */
34
+ export interface LookupSubquery {
35
+ Sql?: string;
36
+ Problem?: string;
37
+ }
38
+ /** Looks up entity metadata by name. */
39
+ export type EntityInfoLookup = (entityName: string) => EntityInfo | null | undefined;
40
+ /**
41
+ * Builds `(SELECT [PK] FROM [schema].[BaseTable] WHERE …)` for a lookup source. Each criterion is
42
+ * matched with `=` on the entity's own table (SQL Server's default collation is case-insensitive,
43
+ * as the push lookup is). `null` matches `IS NULL`. A criterion must be a column of the entity's
44
+ * own table: a virtual field or one an IS-A parent holds has no column there.
45
+ */
46
+ export declare class LookupSubqueryBuilder {
47
+ private readonly entityInfo;
48
+ constructor(entityInfo: EntityInfoLookup);
49
+ Build(source: LookupSqlSource): LookupSubquery;
50
+ private condition;
51
+ }
52
+ /**
53
+ * Replaces the logged SET lines of lookup-sourced fields with subqueries, for one push. Registrations
54
+ * are keyed by the save-call hash of the record (`GenericDatabaseProvider.SaveCallVariableHash`), so
55
+ * only the SET line of that record's save is rewritten. A registration stays for the whole push.
56
+ */
57
+ export declare class LookupSqlRewriter {
58
+ private readonly builder;
59
+ private readonly linesByHash;
60
+ private readonly warnings;
61
+ constructor(builder: LookupSubqueryBuilder);
62
+ /**
63
+ * Registers one field of one save call. `value` is the resolved ID the statement holds. Returns
64
+ * false (and keeps a warning) when no subquery can be built, so the log keeps the literal ID.
65
+ */
66
+ Register(entityName: string, fieldName: string, codeName: string, value: unknown, lookup: LookupSqlSource, saveCallHash: string): boolean;
67
+ /**
68
+ * Registers each lookup-sourced field of the save statement of `entity`. A primary key is
69
+ * registered at every IS-A level, because each level's save sets it; another field at the level
70
+ * whose table holds it. Call it right before the save, so the hash uses the key values the
71
+ * provider hashes. `lookups` keys are matched without regard to case.
72
+ */
73
+ RegisterEntityLookups(entity: BaseEntity, lookups: ReadonlyMap<string, LookupSqlSource> | undefined): void;
74
+ /**
75
+ * Registers the primary key of a record that a `@lookup:…?create` made, keyed by the lookup that
76
+ * made it, and keeps a warning: where no row matches, the replay passes a NULL ID, so the
77
+ * `IF NOT EXISTS` guard inserts the row with an ID that database generates.
78
+ */
79
+ RegisterLookupCreate(created: BaseEntity, lookup: LookupSqlSource): void;
80
+ /**
81
+ * The `SqlLoggingOptions.TransformStatement` function. It can run many times on the same text and
82
+ * on text that is never written. On an unexpected error it returns the text unchanged, so the log
83
+ * keeps the literal IDs, and keeps a warning.
84
+ */
85
+ readonly Transform: (sql: string) => string;
86
+ /** The warnings of this run, each once. */
87
+ Warnings(): string[];
88
+ private registerLevelField;
89
+ private replaceSetLines;
90
+ }
91
+ /** The sync engine members a `LookupSqlLog` uses. */
92
+ type LookupSqlLogEngine = Pick<SyncEngine, 'BeforeLookupCreateSaveSource' | 'GetEntityInfo'>;
93
+ /**
94
+ * The lookup rewriter of one open SQL log, with the `SyncEngine.BeforeLookupCreateSaveSource` hook
95
+ * that registers each record a `?create` lookup makes.
96
+ */
97
+ export declare class LookupSqlLog {
98
+ readonly Rewriter: LookupSqlRewriter;
99
+ private readonly engine;
100
+ private readonly hook;
101
+ constructor(engine: LookupSqlLogEngine);
102
+ /** Clears the engine's hook when it is still this log's hook. */
103
+ Close(): void;
104
+ }
105
+ /** One `TransformStatement` that runs each transform in turn. */
106
+ export declare function ComposeStatementTransforms(...transforms: Array<(sql: string) => string>): (sql: string) => string;
107
+ export {};
108
+ //# sourceMappingURL=lookup-sql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lookup-sql.d.ts","sourceRoot":"","sources":["../../src/lib/lookup-sql.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,UAAU,EAAmB,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAIpF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,4FAA4F;AAC5F,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,KAAK,EAAE,MAAM,GAAG,eAAe,CAAC;CACjC;AAED,4EAA4E;AAC5E,MAAM,WAAW,eAAe;IAC9B,sCAAsC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,kBAAkB,EAAE,CAAC;IAC/B,4EAA4E;IAC5E,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,4FAA4F;AAC5F,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wCAAwC;AACxC,MAAM,MAAM,gBAAgB,GAAG,CAAC,UAAU,EAAE,MAAM,KAAK,UAAU,GAAG,IAAI,GAAG,SAAS,CAAC;AAErF;;;;;GAKG;AACH,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAmB;gBAElC,UAAU,EAAE,gBAAgB;IAIxC,KAAK,CAAC,MAAM,EAAE,eAAe,GAAG,cAAc;IA0B9C,OAAO,CAAC,SAAS;CAsBlB;AA8BD;;;;GAIG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAChD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAiD;IAC7E,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;gBAElC,OAAO,EAAE,qBAAqB;IAI1C;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO;IAwBzI;;;;;OAKG;IACH,qBAAqB,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,SAAS,GAAG,IAAI;IAiB1G;;;;OAIG;IACH,oBAAoB,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI;IAwBxE;;;;OAIG;IACH,QAAQ,CAAC,SAAS,GAAI,KAAK,MAAM,KAAG,MAAM,CAOxC;IAEF,2CAA2C;IAC3C,QAAQ,IAAI,MAAM,EAAE;IAIpB,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,eAAe;CAYxB;AAED,qDAAqD;AACrD,KAAK,kBAAkB,GAAG,IAAI,CAAC,UAAU,EAAE,8BAA8B,GAAG,eAAe,CAAC,CAAC;AAE7F;;;GAGG;AACH,qBAAa,YAAY;IACvB,SAAgB,QAAQ,EAAE,iBAAiB,CAAC;IAC5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA0D;gBAEnE,MAAM,EAAE,kBAAkB;IAOtC,iEAAiE;IACjE,KAAK,IAAI,IAAI;CAKd;AAED,iEAAiE;AACjE,wBAAgB,0BAA0B,CAAC,GAAG,UAAU,EAAE,KAAK,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAEjH"}
@@ -0,0 +1,231 @@
1
+ import { GenericDatabaseProvider } from '@memberjunction/generic-database-provider';
2
+ import { IsDateSQLType, IsUuidSQLType } from '@memberjunction/sql-dialect';
3
+ import { IsALevels, OwningLevel, SaveCallHashesIn, SetLinePattern, SqlLiteralsOf } from './sql-set-line.js';
4
+ /**
5
+ * Builds `(SELECT [PK] FROM [schema].[BaseTable] WHERE …)` for a lookup source. Each criterion is
6
+ * matched with `=` on the entity's own table (SQL Server's default collation is case-insensitive,
7
+ * as the push lookup is). `null` matches `IS NULL`. A criterion must be a column of the entity's
8
+ * own table: a virtual field or one an IS-A parent holds has no column there.
9
+ */
10
+ export class LookupSubqueryBuilder {
11
+ constructor(entityInfo) {
12
+ this.entityInfo = entityInfo;
13
+ }
14
+ Build(source) {
15
+ if (source.EnvSourced) {
16
+ return { Problem: 'its @lookup criteria use an @env value, which the log never holds' };
17
+ }
18
+ const info = this.entityInfo(source.EntityName);
19
+ if (!info) {
20
+ return { Problem: `entity '${source.EntityName}' was not found` };
21
+ }
22
+ if (info.PrimaryKeys.length !== 1) {
23
+ return { Problem: `'${source.EntityName}' does not have a single-column primary key` };
24
+ }
25
+ if (source.Criteria.length === 0) {
26
+ return { Problem: 'the @lookup has no criteria' };
27
+ }
28
+ const conditions = [];
29
+ for (const criterion of source.Criteria) {
30
+ const condition = this.condition(info, criterion);
31
+ if (condition.Problem !== undefined) {
32
+ return condition;
33
+ }
34
+ conditions.push(condition.Sql);
35
+ }
36
+ const key = info.FirstPrimaryKey.Name; // first-pk-ok: a single-column key was checked above; a lookup yields one value
37
+ return { Sql: `(SELECT [${key}] FROM [${info.SchemaName}].[${info.BaseTable}] WHERE ${conditions.join(' AND ')})` };
38
+ }
39
+ condition(info, criterion) {
40
+ const wanted = criterion.FieldName.trim().toLowerCase();
41
+ const field = info.Fields.find((candidate) => candidate.Name.toLowerCase() === wanted);
42
+ if (!field) {
43
+ return { Problem: `'${info.Name}' has no field '${criterion.FieldName}'` };
44
+ }
45
+ if (field.IsVirtual || info.ParentEntityFieldNames?.has(field.Name)) {
46
+ return { Problem: `${info.Name}.${field.Name} is not a column of table ${info.BaseTable}` };
47
+ }
48
+ if (typeof criterion.Value !== 'string') {
49
+ const nested = this.Build(criterion.Value);
50
+ return nested.Problem !== undefined ? nested : { Sql: `[${field.Name}] = ${nested.Sql}` };
51
+ }
52
+ if (criterion.Value.trim().toLowerCase() === 'null') {
53
+ return { Sql: `[${field.Name}] IS NULL` };
54
+ }
55
+ const literal = criterionLiteral(field, criterion.Value);
56
+ if (literal === null) {
57
+ return { Problem: `the value of ${info.Name}.${field.Name} cannot be written as a ${field.Type} literal` };
58
+ }
59
+ return { Sql: `[${field.Name}] = ${literal}` };
60
+ }
61
+ }
62
+ /** The SQL Server literal for a criterion value, or null when the value does not fit the field type. */
63
+ function criterionLiteral(field, value) {
64
+ const escaped = value.replace(/'/g, "''");
65
+ if (!field.NeedsQuotes) {
66
+ const type = field.Type.trim().toLowerCase();
67
+ const lower = value.trim().toLowerCase();
68
+ if (type === 'bit' || type === 'boolean') {
69
+ if (lower === 'true' || lower === '1') {
70
+ return '1';
71
+ }
72
+ if (lower === 'false' || lower === '0') {
73
+ return '0';
74
+ }
75
+ return null;
76
+ }
77
+ return /^-?\d+(\.\d+)?$/.test(value.trim()) ? value.trim() : null;
78
+ }
79
+ if (IsUuidSQLType(field.Type) || IsDateSQLType(field.Type)) {
80
+ return `'${escaped}'`;
81
+ }
82
+ return `N'${escaped}'`;
83
+ }
84
+ /**
85
+ * Replaces the logged SET lines of lookup-sourced fields with subqueries, for one push. Registrations
86
+ * are keyed by the save-call hash of the record (`GenericDatabaseProvider.SaveCallVariableHash`), so
87
+ * only the SET line of that record's save is rewritten. A registration stays for the whole push.
88
+ */
89
+ export class LookupSqlRewriter {
90
+ constructor(builder) {
91
+ this.linesByHash = new Map();
92
+ this.warnings = new Set();
93
+ /**
94
+ * The `SqlLoggingOptions.TransformStatement` function. It can run many times on the same text and
95
+ * on text that is never written. On an unexpected error it returns the text unchanged, so the log
96
+ * keeps the literal IDs, and keeps a warning.
97
+ */
98
+ this.Transform = (sql) => {
99
+ try {
100
+ return this.replaceSetLines(sql);
101
+ }
102
+ catch {
103
+ this.warnings.add('SQL log: a statement kept its literal lookup IDs because the rewrite to subqueries failed.');
104
+ return sql;
105
+ }
106
+ };
107
+ this.builder = builder;
108
+ }
109
+ /**
110
+ * Registers one field of one save call. `value` is the resolved ID the statement holds. Returns
111
+ * false (and keeps a warning) when no subquery can be built, so the log keeps the literal ID.
112
+ */
113
+ Register(entityName, fieldName, codeName, value, lookup, saveCallHash) {
114
+ const literals = SqlLiteralsOf(value);
115
+ if (literals.length === 0) {
116
+ return false;
117
+ }
118
+ const subquery = this.builder.Build(lookup);
119
+ if (subquery.Problem !== undefined || subquery.Sql === undefined) {
120
+ this.warnings.add(`SQL log: ${entityName}.${fieldName} is written with this database's ID because ${subquery.Problem} (${lookup.Expression}). A database where that row has another ID will not match it.`);
121
+ return false;
122
+ }
123
+ const hash = saveCallHash.toLowerCase();
124
+ let lines = this.linesByHash.get(hash);
125
+ if (!lines) {
126
+ lines = new Map();
127
+ this.linesByHash.set(hash, lines);
128
+ }
129
+ const key = [codeName, ...literals].join('\u0000');
130
+ // Case-insensitive: SQL Server returns upper-case UUIDs where a lookup can return either case
131
+ lines.set(key, { Pattern: SetLinePattern(codeName, hash, literals, 'gmi'), Subquery: subquery.Sql });
132
+ return true;
133
+ }
134
+ /**
135
+ * Registers each lookup-sourced field of the save statement of `entity`. A primary key is
136
+ * registered at every IS-A level, because each level's save sets it; another field at the level
137
+ * whose table holds it. Call it right before the save, so the hash uses the key values the
138
+ * provider hashes. `lookups` keys are matched without regard to case.
139
+ */
140
+ RegisterEntityLookups(entity, lookups) {
141
+ if (!lookups || lookups.size === 0) {
142
+ return;
143
+ }
144
+ const keyNames = new Set(entity.EntityInfo.PrimaryKeys.map((pk) => pk.Name));
145
+ for (const [key, lookup] of lookups) {
146
+ const fieldName = entity.GetFieldByName(key)?.Name;
147
+ if (!fieldName) {
148
+ continue;
149
+ }
150
+ const levels = keyNames.has(fieldName) ? IsALevels(entity) : [OwningLevel(entity, fieldName)];
151
+ for (const level of levels) {
152
+ this.registerLevelField(level, fieldName, lookup);
153
+ }
154
+ }
155
+ }
156
+ /**
157
+ * Registers the primary key of a record that a `@lookup:…?create` made, keyed by the lookup that
158
+ * made it, and keeps a warning: where no row matches, the replay passes a NULL ID, so the
159
+ * `IF NOT EXISTS` guard inserts the row with an ID that database generates.
160
+ */
161
+ RegisterLookupCreate(created, lookup) {
162
+ if (created.EntityInfo.PrimaryKeys.length !== 1) {
163
+ return;
164
+ }
165
+ // Criteria that were lookups themselves are foreign keys the create sets: they nest too
166
+ const nested = new Map();
167
+ for (const criterion of lookup.Criteria) {
168
+ if (typeof criterion.Value !== 'string') {
169
+ nested.set(criterion.FieldName, criterion.Value);
170
+ }
171
+ }
172
+ this.RegisterEntityLookups(created, nested);
173
+ const keyName = created.EntityInfo.FirstPrimaryKey.Name; // first-pk-ok: a single-column key was checked above
174
+ let registered = false;
175
+ for (const level of IsALevels(created)) {
176
+ registered = this.registerLevelField(level, keyName, lookup) || registered;
177
+ }
178
+ if (registered) {
179
+ this.warnings.add(`SQL log: the ${created.EntityInfo.Name} row that ${lookup.Expression} created is logged keyed by that lookup. On a database with no matching row the replay passes a NULL ID, so the IF NOT EXISTS guard inserts the row with an ID that database generates.`);
180
+ }
181
+ }
182
+ /** The warnings of this run, each once. */
183
+ Warnings() {
184
+ return [...this.warnings];
185
+ }
186
+ registerLevelField(level, fieldName, lookup) {
187
+ const field = level.GetFieldByName(fieldName);
188
+ if (!field) {
189
+ return false;
190
+ }
191
+ const levelInfo = level.EntityInfo;
192
+ const keyValues = level.PrimaryKey?.KeyValuePairs?.map((pair) => pair.Value) ?? [];
193
+ const saveCallHash = GenericDatabaseProvider.SaveCallVariableHash(levelInfo.SchemaName, levelInfo.BaseTable, keyValues);
194
+ return this.Register(levelInfo.Name, fieldName, field.EntityFieldInfo.CodeName, level.Get(fieldName), lookup, saveCallHash);
195
+ }
196
+ replaceSetLines(sql) {
197
+ if (this.linesByHash.size === 0) {
198
+ return sql;
199
+ }
200
+ let result = sql;
201
+ for (const hash of SaveCallHashesIn(sql)) {
202
+ for (const line of this.linesByHash.get(hash)?.values() ?? []) {
203
+ result = result.replace(line.Pattern, (_line, indent, variable) => `${indent}SET ${variable} = ${line.Subquery}`);
204
+ }
205
+ }
206
+ return result;
207
+ }
208
+ }
209
+ /**
210
+ * The lookup rewriter of one open SQL log, with the `SyncEngine.BeforeLookupCreateSaveSource` hook
211
+ * that registers each record a `?create` lookup makes.
212
+ */
213
+ export class LookupSqlLog {
214
+ constructor(engine) {
215
+ this.engine = engine;
216
+ this.Rewriter = new LookupSqlRewriter(new LookupSubqueryBuilder((name) => engine.GetEntityInfo(name)));
217
+ this.hook = (created, lookup) => this.Rewriter.RegisterLookupCreate(created, lookup);
218
+ engine.BeforeLookupCreateSaveSource = this.hook;
219
+ }
220
+ /** Clears the engine's hook when it is still this log's hook. */
221
+ Close() {
222
+ if (this.engine.BeforeLookupCreateSaveSource === this.hook) {
223
+ this.engine.BeforeLookupCreateSaveSource = null;
224
+ }
225
+ }
226
+ }
227
+ /** One `TransformStatement` that runs each transform in turn. */
228
+ export function ComposeStatementTransforms(...transforms) {
229
+ return (sql) => transforms.reduce((text, transform) => transform(text), sql);
230
+ }
231
+ //# sourceMappingURL=lookup-sql.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lookup-sql.js","sourceRoot":"","sources":["../../src/lib/lookup-sql.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * @fileoverview Pre-flight for a create that a unique constraint would reject (#4400).
3
+ *
4
+ * A seed record with a literal primary key that is not in the database is created with that key.
5
+ * When the database already has a row with the same natural key under another ID (CodeGen and
6
+ * other server-side code create rows with database-generated IDs), the create violates the unique
7
+ * constraint and the push stops on a driver error. This check runs first: for each unique key of
8
+ * the table, it looks the record's values up and, on a hit, reports the existing row's key so the
9
+ * seed can be re-keyed with `@lookup`.
10
+ * @module natural-key-preflight
11
+ */
12
+ import { type BaseEntity, type DatabaseProviderBase, type EntityFieldInfo, type EntityInfo, type IMetadataProvider, type UserInfo } from '@memberjunction/core';
13
+ /** One unique key of a table: its name and its columns in key order. */
14
+ export interface UniqueKeyColumns {
15
+ Name: string;
16
+ Columns: string[];
17
+ }
18
+ /** A row that already holds the natural key a create would add. */
19
+ export interface NaturalKeyConflict {
20
+ /** Unique constraint or index name. */
21
+ KeyName: string;
22
+ /** The entity's field names that make up the key. */
23
+ Fields: string[];
24
+ /** The primary key of the existing row. */
25
+ ExistingKey: Record<string, unknown>;
26
+ }
27
+ /** Reads unique keys and finds rows by them. */
28
+ export interface NaturalKeyReader {
29
+ /** The unique keys of the entity's table, other than the primary key. */
30
+ UniqueKeys(info: EntityInfo): Promise<UniqueKeyColumns[]>;
31
+ /** The primary key of a row whose fields equal `values`, or null when there is none. */
32
+ FindKey(info: EntityInfo, values: ReadonlyArray<{
33
+ Field: EntityFieldInfo;
34
+ Value: unknown;
35
+ }>): Promise<Record<string, unknown> | null>;
36
+ }
37
+ /** Finds the existing row, if any, that a create of `entity` would collide with on a unique key. */
38
+ export declare class NaturalKeyPreflight {
39
+ private readonly reader;
40
+ constructor(reader: NaturalKeyReader);
41
+ /**
42
+ * The first unique key whose values another row already holds, or null. A key is checked only
43
+ * when every one of its columns is a field of the entity with a non-null value and no column is
44
+ * part of the primary key.
45
+ */
46
+ Check(entity: BaseEntity): Promise<NaturalKeyConflict | null>;
47
+ private keyValues;
48
+ }
49
+ /** Row shape of the unique-key catalog queries. */
50
+ interface UniqueKeyCatalogRow {
51
+ IndexName: string;
52
+ ColumnName: string;
53
+ }
54
+ /**
55
+ * Reads unique keys from the database catalog (unfiltered unique indexes and unique constraints,
56
+ * not the primary key) and finds rows with `RunView` on the record's provider, so the lookup sees
57
+ * rows the push transaction wrote. Catalog reads go to the pool and are never logged. A catalog
58
+ * read that fails gives no keys, so the check is skipped rather than failing the push.
59
+ */
60
+ export declare class DatabaseNaturalKeyReader implements NaturalKeyReader {
61
+ private readonly provider;
62
+ private readonly contextUser;
63
+ private readonly rowProvider;
64
+ private readonly cache;
65
+ /**
66
+ * `rowProvider` is the provider rows are found on (the record's, so the read joins its
67
+ * transaction); `cache` (entity name → keys) can be shared by readers that find rows on
68
+ * different providers.
69
+ */
70
+ constructor(provider: DatabaseProviderBase, contextUser: UserInfo, rowProvider?: IMetadataProvider, cache?: Map<string, Promise<UniqueKeyColumns[]>>);
71
+ UniqueKeys(info: EntityInfo): Promise<UniqueKeyColumns[]>;
72
+ FindKey(info: EntityInfo, values: ReadonlyArray<{
73
+ Field: EntityFieldInfo;
74
+ Value: unknown;
75
+ }>): Promise<Record<string, unknown> | null>;
76
+ private readUniqueKeys;
77
+ private literal;
78
+ }
79
+ /** The catalog query listing a table's unfiltered unique keys other than the primary key, one row per column in key order. */
80
+ export declare function UniqueKeyCatalogSQL(platformKey: string, schemaName: string, tableName: string): string;
81
+ /** Catalog rows grouped into keys, in row order. */
82
+ export declare function GroupUniqueKeyRows(rows: ReadonlyArray<UniqueKeyCatalogRow>): UniqueKeyColumns[];
83
+ /** The pre-flight error for a conflict. `recordKey` is the record's key as it may be shown. */
84
+ export declare function NaturalKeyConflictMessage(entityName: string, recordKey: string, conflict: NaturalKeyConflict): string;
85
+ export {};
86
+ //# sourceMappingURL=natural-key-preflight.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"natural-key-preflight.d.ts","sourceRoot":"","sources":["../../src/lib/natural-key-preflight.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAW,KAAK,UAAU,EAAE,KAAK,oBAAoB,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAE,KAAK,iBAAiB,EAAE,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEzK,wEAAwE;AACxE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,mEAAmE;AACnE,MAAM,WAAW,kBAAkB;IACjC,uCAAuC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,2CAA2C;IAC3C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,gDAAgD;AAChD,MAAM,WAAW,gBAAgB;IAC/B,yEAAyE;IACzE,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAC1D,wFAAwF;IACxF,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,CAAC;QAAE,KAAK,EAAE,eAAe,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;CACvI;AAED,oGAAoG;AACpG,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;gBAE9B,MAAM,EAAE,gBAAgB;IAIpC;;;;OAIG;IACG,KAAK,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;IAoBnE,OAAO,CAAC,SAAS;CAkBlB;AASD,mDAAmD;AACnD,UAAU,mBAAmB;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,qBAAa,wBAAyB,YAAW,gBAAgB;IAC/D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuB;IAChD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAW;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAgC;IAC5D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA2C;IAEjE;;;;OAIG;gBAED,QAAQ,EAAE,oBAAoB,EAC9B,WAAW,EAAE,QAAQ,EACrB,WAAW,CAAC,EAAE,iBAAiB,EAC/B,KAAK,GAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAa;IAQ7D,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IASnD,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,CAAC;QAAE,KAAK,EAAE,eAAe,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;YAiB7H,cAAc;IAc5B,OAAO,CAAC,OAAO;CAahB;AAED,8HAA8H;AAC9H,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAsBtG;AAED,oDAAoD;AACpD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,CAAC,mBAAmB,CAAC,GAAG,gBAAgB,EAAE,CAQ/F;AAED,+FAA+F;AAC/F,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,kBAAkB,GAAG,MAAM,CAYrH"}
@@ -0,0 +1,173 @@
1
+ /**
2
+ * @fileoverview Pre-flight for a create that a unique constraint would reject (#4400).
3
+ *
4
+ * A seed record with a literal primary key that is not in the database is created with that key.
5
+ * When the database already has a row with the same natural key under another ID (CodeGen and
6
+ * other server-side code create rows with database-generated IDs), the create violates the unique
7
+ * constraint and the push stops on a driver error. This check runs first: for each unique key of
8
+ * the table, it looks the record's values up and, on a hit, reports the existing row's key so the
9
+ * seed can be re-keyed with `@lookup`.
10
+ * @module natural-key-preflight
11
+ */
12
+ import { RunView } from '@memberjunction/core';
13
+ /** Finds the existing row, if any, that a create of `entity` would collide with on a unique key. */
14
+ export class NaturalKeyPreflight {
15
+ constructor(reader) {
16
+ this.reader = reader;
17
+ }
18
+ /**
19
+ * The first unique key whose values another row already holds, or null. A key is checked only
20
+ * when every one of its columns is a field of the entity with a non-null value and no column is
21
+ * part of the primary key.
22
+ */
23
+ async Check(entity) {
24
+ const info = entity.EntityInfo;
25
+ const keys = await this.reader.UniqueKeys(info);
26
+ if (keys.length === 0) {
27
+ return null;
28
+ }
29
+ const keyNames = new Set(info.PrimaryKeys.map((pk) => pk.Name.toLowerCase()));
30
+ for (const key of keys) {
31
+ const values = this.keyValues(entity, key, keyNames);
32
+ if (!values) {
33
+ continue;
34
+ }
35
+ const existing = await this.reader.FindKey(info, values);
36
+ if (existing && !samePrimaryKey(entity, existing)) {
37
+ return { KeyName: key.Name, Fields: values.map((value) => value.Field.Name), ExistingKey: existing };
38
+ }
39
+ }
40
+ return null;
41
+ }
42
+ keyValues(entity, key, keyNames) {
43
+ if (key.Columns.length === 0) {
44
+ return null;
45
+ }
46
+ const values = [];
47
+ for (const column of key.Columns) {
48
+ if (keyNames.has(column.toLowerCase())) {
49
+ return null;
50
+ }
51
+ const field = entity.EntityInfo.Fields.find((candidate) => candidate.Name.toLowerCase() === column.toLowerCase());
52
+ const value = field ? entity.Get(field.Name) : undefined;
53
+ if (!field || value === null || value === undefined) {
54
+ return null;
55
+ }
56
+ values.push({ Field: field, Value: value });
57
+ }
58
+ return values;
59
+ }
60
+ }
61
+ /** True when `existing` is the entity's own key (compared without regard to case). */
62
+ function samePrimaryKey(entity, existing) {
63
+ return entity.EntityInfo.PrimaryKeys.every((pk) => String(entity.Get(pk.Name) ?? '').trim().toLowerCase() === String(existing[pk.Name] ?? '').trim().toLowerCase());
64
+ }
65
+ /**
66
+ * Reads unique keys from the database catalog (unfiltered unique indexes and unique constraints,
67
+ * not the primary key) and finds rows with `RunView` on the record's provider, so the lookup sees
68
+ * rows the push transaction wrote. Catalog reads go to the pool and are never logged. A catalog
69
+ * read that fails gives no keys, so the check is skipped rather than failing the push.
70
+ */
71
+ export class DatabaseNaturalKeyReader {
72
+ /**
73
+ * `rowProvider` is the provider rows are found on (the record's, so the read joins its
74
+ * transaction); `cache` (entity name → keys) can be shared by readers that find rows on
75
+ * different providers.
76
+ */
77
+ constructor(provider, contextUser, rowProvider, cache = new Map()) {
78
+ this.provider = provider;
79
+ this.contextUser = contextUser;
80
+ this.rowProvider = rowProvider;
81
+ this.cache = cache;
82
+ }
83
+ UniqueKeys(info) {
84
+ let keys = this.cache.get(info.Name);
85
+ if (!keys) {
86
+ keys = this.readUniqueKeys(info);
87
+ this.cache.set(info.Name, keys);
88
+ }
89
+ return keys;
90
+ }
91
+ async FindKey(info, values) {
92
+ // Quoted, so a reserved-word or spaced column name still filters (rather than failing the read)
93
+ const filter = values.map(({ Field, Value }) => `${this.provider.QuoteIdentifier(Field.Name)} = ${this.literal(Field, Value)}`).join(' AND ');
94
+ const rv = this.rowProvider ? RunView.FromMetadataProvider(this.rowProvider) : new RunView();
95
+ const result = await rv.RunView({
96
+ EntityName: info.Name,
97
+ ExtraFilter: filter,
98
+ Fields: info.PrimaryKeys.map((pk) => pk.Name),
99
+ MaxRows: 1,
100
+ ResultType: 'simple',
101
+ }, this.contextUser);
102
+ return result.Success && result.Results.length > 0 ? result.Results[0] : null;
103
+ }
104
+ async readUniqueKeys(info) {
105
+ try {
106
+ const rows = await this.provider.ExecuteSQL(UniqueKeyCatalogSQL(this.provider.PlatformKey, info.SchemaName, info.BaseTable), undefined, { ignoreLogging: true, isMutation: false, ignoreAmbientTransaction: true }, this.contextUser);
107
+ return GroupUniqueKeyRows(rows);
108
+ }
109
+ catch {
110
+ return [];
111
+ }
112
+ }
113
+ literal(field, value) {
114
+ if (value instanceof Date) {
115
+ return `'${value.toISOString()}'`;
116
+ }
117
+ if (typeof value === 'boolean') {
118
+ if (this.provider.PlatformKey === 'postgresql') {
119
+ return value ? 'true' : 'false';
120
+ }
121
+ return value ? '1' : '0';
122
+ }
123
+ const text = String(value).replace(/'/g, "''");
124
+ return field.NeedsQuotes ? `'${text}'` : text;
125
+ }
126
+ }
127
+ /** The catalog query listing a table's unfiltered unique keys other than the primary key, one row per column in key order. */
128
+ export function UniqueKeyCatalogSQL(platformKey, schemaName, tableName) {
129
+ const schema = schemaName.replace(/'/g, "''");
130
+ const table = tableName.replace(/'/g, "''");
131
+ if (platformKey === 'postgresql') {
132
+ return `SELECT ix.relname AS "IndexName", a.attname AS "ColumnName"
133
+ FROM pg_index x
134
+ JOIN pg_class t ON t.oid = x.indrelid
135
+ JOIN pg_namespace n ON n.oid = t.relnamespace
136
+ JOIN pg_class ix ON ix.oid = x.indexrelid
137
+ JOIN LATERAL unnest(x.indkey) WITH ORDINALITY AS k(attnum, ord) ON true
138
+ JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = k.attnum
139
+ WHERE x.indisunique AND NOT x.indisprimary AND x.indpred IS NULL
140
+ AND n.nspname = '${schema}' AND t.relname = '${table}'
141
+ ORDER BY ix.relname, k.ord`;
142
+ }
143
+ return `SELECT i.name AS IndexName, c.name AS ColumnName
144
+ FROM sys.indexes i
145
+ JOIN sys.index_columns ic ON ic.object_id = i.object_id AND ic.index_id = i.index_id
146
+ JOIN sys.columns c ON c.object_id = ic.object_id AND c.column_id = ic.column_id
147
+ WHERE i.object_id = OBJECT_ID(N'[${schema.replace(/]/g, ']]')}].[${table.replace(/]/g, ']]')}]')
148
+ AND i.is_unique = 1 AND i.is_primary_key = 0 AND i.has_filter = 0 AND ic.is_included_column = 0
149
+ ORDER BY i.name, ic.key_ordinal`;
150
+ }
151
+ /** Catalog rows grouped into keys, in row order. */
152
+ export function GroupUniqueKeyRows(rows) {
153
+ const byName = new Map();
154
+ for (const row of rows) {
155
+ const columns = byName.get(row.IndexName) ?? [];
156
+ columns.push(row.ColumnName);
157
+ byName.set(row.IndexName, columns);
158
+ }
159
+ return [...byName].map(([Name, Columns]) => ({ Name, Columns }));
160
+ }
161
+ /** The pre-flight error for a conflict. `recordKey` is the record's key as it may be shown. */
162
+ export function NaturalKeyConflictMessage(entityName, recordKey, conflict) {
163
+ const existing = Object.entries(conflict.ExistingKey)
164
+ .map(([field, value]) => `${field}=${String(value)}`)
165
+ .join(', ');
166
+ const fields = conflict.Fields.join(', ');
167
+ const example = conflict.Fields.map((field) => `${field}=…`).join('&');
168
+ return (`${entityName}: no row has primary key {${recordKey}}, but row {${existing}} already has the same ${fields} ` +
169
+ `(unique key ${conflict.KeyName}), so creating this record would violate it. That row's ID was made by this ` +
170
+ `database, so a literal ID in metadata cannot match it. Re-key the record's primaryKey with @lookup on ` +
171
+ `${fields}, for example "@lookup:${entityName}.${example}".`);
172
+ }
173
+ //# sourceMappingURL=natural-key-preflight.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"natural-key-preflight.js","sourceRoot":"","sources":["../../src/lib/natural-key-preflight.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"provider-utils.d.ts","sourceRoot":"","sources":["../../src/lib/provider-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAG1C,OAAO,EAAE,oBAAoB,EAA6D,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAgBjI;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAkBxF;AAED,kDAAkD;AAClD,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAExF;AAwGD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CA2BrD;AAED,+CAA+C;AAC/C,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAErD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,IAAI,QAAQ,CAqBxC;AAED,6CAA6C;AAC7C,wBAAgB,aAAa,IAAI,QAAQ,CAExC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,IAAI,oBAAoB,GAAG,IAAI,CAE7D;AAED,+CAA+C;AAC/C,wBAAgB,eAAe,IAAI,oBAAoB,GAAG,IAAI,CAE7D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,MAAM,EACX,WAAW,CAAC,EAAE,MAAM,EACpB,cAAc,CAAC,EAAE,MAAM,EAAE,EACzB,iBAAiB,CAAC,EAAE,MAAM,EAAE,EAC5B,aAAa,CAAC,EAAE,MAAM,EAAE,EACxB,aAAa,CAAC,EAAE,MAAM,EAAE,GACvB,MAAM,EAAE,CAwGV;AAED,qDAAqD;AACrD,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,MAAM,EACX,WAAW,CAAC,EAAE,MAAM,EACpB,cAAc,CAAC,EAAE,MAAM,EAAE,EACzB,iBAAiB,CAAC,EAAE,MAAM,EAAE,EAC5B,aAAa,CAAC,EAAE,MAAM,EAAE,EACxB,aAAa,CAAC,EAAE,MAAM,EAAE,GACvB,MAAM,EAAE,CAEV"}
1
+ {"version":3,"file":"provider-utils.d.ts","sourceRoot":"","sources":["../../src/lib/provider-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAG1C,OAAO,EAAE,oBAAoB,EAA6D,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAiBjI;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAkBxF;AAED,kDAAkD;AAClD,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAExF;AAwGD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CA2BrD;AAED,+CAA+C;AAC/C,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAErD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,IAAI,QAAQ,CAqBxC;AAED,6CAA6C;AAC7C,wBAAgB,aAAa,IAAI,QAAQ,CAExC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,IAAI,oBAAoB,GAAG,IAAI,CAE7D;AAED,+CAA+C;AAC/C,wBAAgB,eAAe,IAAI,oBAAoB,GAAG,IAAI,CAE7D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,MAAM,EACX,WAAW,CAAC,EAAE,MAAM,EACpB,cAAc,CAAC,EAAE,MAAM,EAAE,EACzB,iBAAiB,CAAC,EAAE,MAAM,EAAE,EAC5B,aAAa,CAAC,EAAE,MAAM,EAAE,EACxB,aAAa,CAAC,EAAE,MAAM,EAAE,GACvB,MAAM,EAAE,CAqGV;AAED,qDAAqD;AACrD,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,MAAM,EACX,WAAW,CAAC,EAAE,MAAM,EACpB,cAAc,CAAC,EAAE,MAAM,EAAE,EACzB,iBAAiB,CAAC,EAAE,MAAM,EAAE,EAC5B,aAAa,CAAC,EAAE,MAAM,EAAE,EACxB,aAAa,CAAC,EAAE,MAAM,EAAE,GACvB,MAAM,EAAE,CAEV"}