@smart-data-engines/sde 0.1.0-dev.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 (131) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +13 -0
  3. package/README.md +153 -0
  4. package/bin/weather.mjs +32 -0
  5. package/dist/_usage.d.ts +30 -0
  6. package/dist/_usage.js +194 -0
  7. package/dist/_usage.js.map +1 -0
  8. package/dist/bulk.d.ts +9 -0
  9. package/dist/bulk.js +83 -0
  10. package/dist/bulk.js.map +1 -0
  11. package/dist/canonical.d.ts +46 -0
  12. package/dist/canonical.js +150 -0
  13. package/dist/canonical.js.map +1 -0
  14. package/dist/capabilities.d.ts +48 -0
  15. package/dist/capabilities.js +62 -0
  16. package/dist/capabilities.js.map +1 -0
  17. package/dist/cutover.d.ts +36 -0
  18. package/dist/cutover.js +219 -0
  19. package/dist/cutover.js.map +1 -0
  20. package/dist/demo/model.d.ts +28 -0
  21. package/dist/demo/model.js +40 -0
  22. package/dist/demo/model.js.map +1 -0
  23. package/dist/demo/project.d.ts +19 -0
  24. package/dist/demo/project.js +128 -0
  25. package/dist/demo/project.js.map +1 -0
  26. package/dist/demo/weather.d.ts +73 -0
  27. package/dist/demo/weather.js +334 -0
  28. package/dist/demo/weather.js.map +1 -0
  29. package/dist/engines/_clickhouse-connection.d.ts +17 -0
  30. package/dist/engines/_clickhouse-connection.js +182 -0
  31. package/dist/engines/_clickhouse-connection.js.map +1 -0
  32. package/dist/engines/_tls-peer-identity.d.ts +2 -0
  33. package/dist/engines/_tls-peer-identity.js +23 -0
  34. package/dist/engines/_tls-peer-identity.js.map +1 -0
  35. package/dist/engines/_write-fences.d.ts +51 -0
  36. package/dist/engines/_write-fences.js +189 -0
  37. package/dist/engines/_write-fences.js.map +1 -0
  38. package/dist/engines/clickhouse.d.ts +193 -0
  39. package/dist/engines/clickhouse.js +899 -0
  40. package/dist/engines/clickhouse.js.map +1 -0
  41. package/dist/engines/postgres.d.ts +293 -0
  42. package/dist/engines/postgres.js +981 -0
  43. package/dist/engines/postgres.js.map +1 -0
  44. package/dist/errors.d.ts +89 -0
  45. package/dist/errors.js +90 -0
  46. package/dist/errors.js.map +1 -0
  47. package/dist/frozen-verification.d.ts +26 -0
  48. package/dist/frozen-verification.js +67 -0
  49. package/dist/frozen-verification.js.map +1 -0
  50. package/dist/generation.d.ts +34 -0
  51. package/dist/generation.js +81 -0
  52. package/dist/generation.js.map +1 -0
  53. package/dist/groups.d.ts +17 -0
  54. package/dist/groups.js +66 -0
  55. package/dist/groups.js.map +1 -0
  56. package/dist/hashing.d.ts +68 -0
  57. package/dist/hashing.js +146 -0
  58. package/dist/hashing.js.map +1 -0
  59. package/dist/in-place-index.d.ts +43 -0
  60. package/dist/in-place-index.js +272 -0
  61. package/dist/in-place-index.js.map +1 -0
  62. package/dist/index.d.ts +79 -0
  63. package/dist/index.js +64 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/inspection.d.ts +19 -0
  66. package/dist/inspection.js +31 -0
  67. package/dist/inspection.js.map +1 -0
  68. package/dist/internal.d.ts +42 -0
  69. package/dist/internal.js +56 -0
  70. package/dist/internal.js.map +1 -0
  71. package/dist/layout.d.ts +36 -0
  72. package/dist/layout.js +62 -0
  73. package/dist/layout.js.map +1 -0
  74. package/dist/migration.d.ts +197 -0
  75. package/dist/migration.js +592 -0
  76. package/dist/migration.js.map +1 -0
  77. package/dist/model.d.ts +93 -0
  78. package/dist/model.js +313 -0
  79. package/dist/model.js.map +1 -0
  80. package/dist/physical.d.ts +128 -0
  81. package/dist/physical.js +421 -0
  82. package/dist/physical.js.map +1 -0
  83. package/dist/placement.d.ts +157 -0
  84. package/dist/placement.js +651 -0
  85. package/dist/placement.js.map +1 -0
  86. package/dist/provisioning.d.ts +6 -0
  87. package/dist/provisioning.js +45 -0
  88. package/dist/provisioning.js.map +1 -0
  89. package/dist/query.d.ts +68 -0
  90. package/dist/query.js +340 -0
  91. package/dist/query.js.map +1 -0
  92. package/dist/routing.d.ts +25 -0
  93. package/dist/routing.js +35 -0
  94. package/dist/routing.js.map +1 -0
  95. package/dist/schema.d.ts +110 -0
  96. package/dist/schema.js +337 -0
  97. package/dist/schema.js.map +1 -0
  98. package/dist/session.d.ts +195 -0
  99. package/dist/session.js +870 -0
  100. package/dist/session.js.map +1 -0
  101. package/dist/shapes.d.ts +30 -0
  102. package/dist/shapes.js +112 -0
  103. package/dist/shapes.js.map +1 -0
  104. package/dist/staging.d.ts +29 -0
  105. package/dist/staging.js +214 -0
  106. package/dist/staging.js.map +1 -0
  107. package/dist/telemetry.d.ts +468 -0
  108. package/dist/telemetry.js +872 -0
  109. package/dist/telemetry.js.map +1 -0
  110. package/dist/testing/loader.d.ts +38 -0
  111. package/dist/testing/loader.js +86 -0
  112. package/dist/testing/loader.js.map +1 -0
  113. package/dist/testing/memory.d.ts +131 -0
  114. package/dist/testing/memory.js +311 -0
  115. package/dist/testing/memory.js.map +1 -0
  116. package/dist/timestamp.d.ts +20 -0
  117. package/dist/timestamp.js +89 -0
  118. package/dist/timestamp.js.map +1 -0
  119. package/dist/types.d.ts +79 -0
  120. package/dist/types.js +100 -0
  121. package/dist/types.js.map +1 -0
  122. package/dist/verification.d.ts +41 -0
  123. package/dist/verification.js +169 -0
  124. package/dist/verification.js.map +1 -0
  125. package/dist/watermark.d.ts +103 -0
  126. package/dist/watermark.js +170 -0
  127. package/dist/watermark.js.map +1 -0
  128. package/dist/write-fence.d.ts +58 -0
  129. package/dist/write-fence.js +225 -0
  130. package/dist/write-fence.js.map +1 -0
  131. package/package.json +86 -0
@@ -0,0 +1,110 @@
1
+ /**
2
+ * DDL as a value, not as a side effect.
3
+ *
4
+ * A layout and a set of keys in, statements out, and no connection anywhere. This module lives
5
+ * outside `src/engines/` deliberately: that directory is the part of the library that opens
6
+ * sockets, and rendering DDL needs a driver about as much as printing a receipt needs a bank. The
7
+ * reference implementation draws the same line, and the control plane's import allowlist refuses
8
+ * its engine package by name.
9
+ *
10
+ * Being pure is also what makes this the home of the `schema/` conformance vectors. Two libraries
11
+ * that agree on a map and disagree on the DDL place one entity in tables with different columns -
12
+ * and nothing raises, because each of them created a table successfully.
13
+ *
14
+ * **Identifiers are sorted with `compareCodePoints`, never with the default sort.** That is not
15
+ * style. `Array.prototype.sort` orders by UTF-16 code unit, Python's `sorted` orders by code
16
+ * point, and the two disagree for any identifier outside the Basic Multilingual Plane - so a
17
+ * column named with an astral character would come out in a different order here than in the
18
+ * reference, giving two `CREATE TABLE` statements for one layout. This is the second call site of
19
+ * the comparator that produced the `canonical/` vector family; the first was object keys in the
20
+ * IR. `schema/003` pins it.
21
+ */
22
+ import type { PhysicalLayout } from './placement.js';
23
+ /** Every dialect this library renders DDL for, sorted. */
24
+ export declare const DIALECTS: readonly ["clickhouse", "orderbook", "postgres"];
25
+ export type Dialect = (typeof DIALECTS)[number];
26
+ /**
27
+ * Dialects whose physical schema the engine imposes rather than accepting from us.
28
+ *
29
+ * A named set rather than a test on the dialect string, so the places that have to behave
30
+ * differently agree by construction instead of each checking for one name.
31
+ */
32
+ export declare const FIXED_SCHEMA: ReadonlySet<string>;
33
+ /**
34
+ * Quoting per dialect. The adapters bind their own from here instead of keeping a copy: two
35
+ * implementations of an escaping rule is how one of them ends up missing the doubling.
36
+ *
37
+ * A fixed-schema engine has no entry, and the absence is deliberate rather than an omission: it
38
+ * emits no DDL, and its query language takes the symbol and the exchange as string *literals*
39
+ * rather than identifiers. A no-op function entered here for symmetry would look usable and
40
+ * silently escape nothing the day somebody reached for it.
41
+ */
42
+ export declare const QUOTE: Readonly<Record<string, (identifier: string) => string>>;
43
+ export interface SchemaOptions {
44
+ readonly keys: Readonly<Record<string, readonly string[]>>;
45
+ readonly dialect: string;
46
+ }
47
+ /**
48
+ * Whether this engine imposes its own schema, so an empty statement list means "nothing to do".
49
+ *
50
+ * The one public way to tell that apart from "no tables in this layout". A caller that never asks
51
+ * still behaves correctly - creating nothing is right - but a caller *reporting* what the client
52
+ * must do has a different sentence to write.
53
+ */
54
+ export declare function schemaIsFixed(dialect: string): boolean;
55
+ /**
56
+ * The statements that would create this layout, in the order they must run.
57
+ *
58
+ * Idempotent by construction - every statement is `IF NOT EXISTS` - because an application
59
+ * restarting must not reapply DDL and two instances starting at once must not race. Anything
60
+ * beyond creation is a migration, which carries a rollback path and a safety classification.
61
+ *
62
+ * An unknown dialect throws. Falling back to ANSI would emit a statement that looks right, runs on
63
+ * the wrong engine, and creates a table with the wrong storage semantics.
64
+ */
65
+ export declare function schemaStatements(layout: PhysicalLayout, options: SchemaOptions): readonly string[];
66
+ /**
67
+ * What can stand under a table's old name after a group has moved, and what cannot.
68
+ *
69
+ * `notPossible` is the field worth reading, because in this library's engine set it is usually the
70
+ * populated one, and the reasons are structural rather than missing work. A caller who gets an
71
+ * empty list back cannot tell "nothing needed" from "nothing thought about".
72
+ */
73
+ export interface CompatibilityViews {
74
+ /** Statements to run on the **target** when reads switch, in order. */
75
+ readonly create: readonly string[];
76
+ /** Statements to run when the source is dropped, so the view goes with what it replaced. */
77
+ readonly drop: readonly string[];
78
+ /** `[entity, why]` for each table that cannot have one, sorted by entity. */
79
+ readonly notPossible: readonly (readonly [string, string])[];
80
+ /** Whether every table of the group got one. False is ordinary - see above. */
81
+ readonly complete: boolean;
82
+ }
83
+ export interface ViewOptions {
84
+ /** Entity name to the table name it had in the engine it left. */
85
+ readonly was: Readonly<Record<string, string>>;
86
+ readonly dialect: string;
87
+ }
88
+ /**
89
+ * Views on the target under the table names the group had in the engine it left.
90
+ *
91
+ * **A view cannot cross engines, and that is why this renders on the target.** The old table is in
92
+ * the old engine, and no dialect here has a way to select from another server. So what this offers
93
+ * is for the case where somebody re-points their tool at the new engine and their SQL still says
94
+ * the old table name.
95
+ *
96
+ * Columns are listed rather than `SELECT *`, so the view names exactly what the map names, and
97
+ * they are listed **sorted by name, exactly as `CREATE TABLE` sorts them**, because a view whose
98
+ * columns came out differently would break anything reading them by position, which is most of
99
+ * what a hand-written query does with `SELECT *`. The reference implementation read the order off
100
+ * the layout document instead, on the stated grounds that the document was already sorted - it is
101
+ * not, since a foreign-key column is appended per relation, so any entity with a relation had a
102
+ * view and a table that disagreed. `schema/009` feeds a document whose columns are out of order on
103
+ * purpose, which makes both sorts separately mutable.
104
+ *
105
+ * For ClickHouse the view reads `FINAL`, and that is the substance rather than a detail: the table
106
+ * is a `ReplacingMergeTree`, so a plain read returns rows the declared key should have collapsed
107
+ * until a background merge happens. A query moved verbatim to the target returns numbers that are
108
+ * too big, silently. A view that quietly reproduced that would be worse than no view.
109
+ */
110
+ export declare function compatibilityViews(layout: PhysicalLayout, options: ViewOptions): CompatibilityViews;
package/dist/schema.js ADDED
@@ -0,0 +1,337 @@
1
+ /**
2
+ * DDL as a value, not as a side effect.
3
+ *
4
+ * A layout and a set of keys in, statements out, and no connection anywhere. This module lives
5
+ * outside `src/engines/` deliberately: that directory is the part of the library that opens
6
+ * sockets, and rendering DDL needs a driver about as much as printing a receipt needs a bank. The
7
+ * reference implementation draws the same line, and the control plane's import allowlist refuses
8
+ * its engine package by name.
9
+ *
10
+ * Being pure is also what makes this the home of the `schema/` conformance vectors. Two libraries
11
+ * that agree on a map and disagree on the DDL place one entity in tables with different columns -
12
+ * and nothing raises, because each of them created a table successfully.
13
+ *
14
+ * **Identifiers are sorted with `compareCodePoints`, never with the default sort.** That is not
15
+ * style. `Array.prototype.sort` orders by UTF-16 code unit, Python's `sorted` orders by code
16
+ * point, and the two disagree for any identifier outside the Basic Multilingual Plane - so a
17
+ * column named with an astral character would come out in a different order here than in the
18
+ * reference, giving two `CREATE TABLE` statements for one layout. This is the second call site of
19
+ * the comparator that produced the `canonical/` vector family; the first was object keys in the
20
+ * IR. `schema/003` pins it.
21
+ */
22
+ import { compareCodePoints } from './canonical.js';
23
+ import { EngineError } from './errors.js';
24
+ import { CLICKHOUSE_METHODS, POSTGRES_METHODS, effectiveKey, indexMethod, partitionExpression, } from './physical.js';
25
+ /** Every dialect this library renders DDL for, sorted. */
26
+ export const DIALECTS = ['clickhouse', 'orderbook', 'postgres'];
27
+ /**
28
+ * Dialects whose physical schema the engine imposes rather than accepting from us.
29
+ *
30
+ * A named set rather than a test on the dialect string, so the places that have to behave
31
+ * differently agree by construction instead of each checking for one name.
32
+ */
33
+ export const FIXED_SCHEMA = new Set(['orderbook']);
34
+ function quoteAnsi(identifier) {
35
+ return '"' + identifier.replaceAll('"', '""') + '"';
36
+ }
37
+ /**
38
+ * Backticks, with a backslash escape for the backtick **and for the backslash**.
39
+ *
40
+ * ClickHouse accepts double quotes too. Backticks are the idiomatic form and, more usefully, they
41
+ * make a generated statement obviously ClickHouse when it turns up in a log next to a PostgreSQL
42
+ * one.
43
+ *
44
+ * PostgreSQL is the other way round: a backslash is literal inside `"..."` and doubling the quote
45
+ * is the whole rule, and backslash-escaping the quote there is a syntax error. Measured. So the
46
+ * two dialects genuinely differ and there is no escaper to share, which is why `QUOTE` has two.
47
+ */
48
+ function quoteBacktick(identifier) {
49
+ // One pass, escaping the backtick **and** the backslash. Doubling the backtick alone left the
50
+ // backslash as an escape introducer, so a field called `a\nb` reached the server as a column
51
+ // called `a`, a newline and `b` - a different name, accepted in silence. Measured against a real
52
+ // server by reading the name back out of `system.columns`. Two `replaceAll` calls would have to
53
+ // be sequenced correctly; one pass cannot be sequenced wrongly. See the reference's docstring.
54
+ let out = '`';
55
+ for (const character of identifier) {
56
+ if (character === '\\' || character === '`')
57
+ out += '\\';
58
+ out += character;
59
+ }
60
+ return out + '`';
61
+ }
62
+ /**
63
+ * Quoting per dialect. The adapters bind their own from here instead of keeping a copy: two
64
+ * implementations of an escaping rule is how one of them ends up missing the doubling.
65
+ *
66
+ * A fixed-schema engine has no entry, and the absence is deliberate rather than an omission: it
67
+ * emits no DDL, and its query language takes the symbol and the exchange as string *literals*
68
+ * rather than identifiers. A no-op function entered here for symmetry would look usable and
69
+ * silently escape nothing the day somebody reached for it.
70
+ */
71
+ export const QUOTE = {
72
+ clickhouse: quoteBacktick,
73
+ postgres: quoteAnsi,
74
+ };
75
+ function sorted(values) {
76
+ return [...values].sort(compareCodePoints);
77
+ }
78
+ /** `[entity, table]` in code point order of the entity name. See the module docstring. */
79
+ function tablesInOrder(layout) {
80
+ return Object.entries(layout.tables).sort(([a], [b]) => compareCodePoints(a, b));
81
+ }
82
+ function columnsAndKey(layout, entity, keys) {
83
+ const cols = layout.columns[entity] ?? {};
84
+ const columns = sorted(Object.keys(cols));
85
+ if (columns.length === 0)
86
+ throw new EngineError(`the layout gives no columns for '${entity}'`);
87
+ const key = keys[entity] ?? [];
88
+ if (key.length === 0) {
89
+ throw new EngineError(`no key for '${entity}'; a table without one cannot be addressed`);
90
+ }
91
+ return { columns, key };
92
+ }
93
+ function typeOf(layout, entity, column) {
94
+ const declared = (layout.columns[entity] ?? {})[column];
95
+ // Unreachable through `columnsAndKey`, which reads the same object. Present because the
96
+ // alternative under `noUncheckedIndexedAccess` is a non-null assertion, and an assertion is a
97
+ // claim with no message on the day it is wrong.
98
+ if (declared === undefined) {
99
+ throw new EngineError(`the layout gives no type for ${entity}.${column}`);
100
+ }
101
+ return declared;
102
+ }
103
+ /** Indexes in code point order of their name (§7a), whatever order the document gave. */
104
+ function sortedIndexes(layout) {
105
+ return [...layout.indexes].sort((a, b) => compareCodePoints(String(a['name']), String(b['name'])));
106
+ }
107
+ function postgresStatements(layout, options) {
108
+ const partitioned = sorted(Object.keys(layout.partitionBy));
109
+ if (partitioned.length > 0) {
110
+ throw new EngineError(`the layout partitions ${JSON.stringify(partitioned)} and PostgreSQL partitioning is not ` +
111
+ 'rendered by this library: every partition would have to exist before a row arrived, ' +
112
+ 'which is a lifecycle this product does not manage. An unpartitioned table under a map ' +
113
+ 'that says otherwise would be a silent drop, so this refuses.');
114
+ }
115
+ const statements = [];
116
+ for (const [entity, table] of tablesInOrder(layout)) {
117
+ const { columns, key } = columnsAndKey(layout, entity, options.keys);
118
+ const ordered = effectiveKey(`table '${table}'`, entity, key, layout.keyOrder ?? {}, EngineError);
119
+ const defs = columns.map((c) => `${quoteAnsi(c)} ${typeOf(layout, entity, c)}`).join(', ');
120
+ const pk = ordered.map(quoteAnsi).join(', ');
121
+ statements.push(`CREATE TABLE IF NOT EXISTS ${quoteAnsi(table)} (${defs}, PRIMARY KEY (${pk}))`);
122
+ }
123
+ for (const index of sortedIndexes(layout)) {
124
+ const entity = String(index['entity']);
125
+ const table = layout.tables[entity];
126
+ if (table === undefined)
127
+ continue;
128
+ const name = String(index['name']);
129
+ const method = indexMethod(index);
130
+ if (!POSTGRES_METHODS.includes(method)) {
131
+ throw new EngineError(`index '${name}' is a ${method} index, which is a ClickHouse data-skipping index; ` +
132
+ `PostgreSQL has ${JSON.stringify(POSTGRES_METHODS)}. This map was designed for another ` +
133
+ 'dialect.');
134
+ }
135
+ const declared = index['columns'];
136
+ if (!Array.isArray(declared) || declared.length === 0) {
137
+ throw new EngineError(`the index '${name}' on '${entity}' names no columns. An index over nothing is not a ` +
138
+ `narrower index, it is a statement that will not parse.`);
139
+ }
140
+ const cols = declared.map((c) => quoteAnsi(String(c))).join(', ');
141
+ // A B-tree keeps the bytes every earlier map produced: no USING clause.
142
+ const using = method === 'btree' ? '' : `USING ${method} `;
143
+ statements.push(`CREATE INDEX IF NOT EXISTS ${quoteAnsi(name)} ON ${quoteAnsi(table)} ${using}(${cols})`);
144
+ }
145
+ return statements;
146
+ }
147
+ function skipIndexType(index) {
148
+ const method = indexMethod(index);
149
+ return method === 'set' ? `set(${String(index['max_rows'])})` : method;
150
+ }
151
+ function clickhouseStatements(layout, options) {
152
+ const legacy = layout.indexes.filter((index) => !CLICKHOUSE_METHODS.includes(indexMethod(index)));
153
+ if (legacy.length > 0) {
154
+ throw new EngineError(`the layout carries ${legacy.length} index definitions and this engine has no ` +
155
+ `B-tree to put them in. A ClickHouse index is a data-skipping index with a type and a ` +
156
+ `granularity, so this map was built for another dialect.`);
157
+ }
158
+ const statements = [];
159
+ for (const [entity, table] of tablesInOrder(layout)) {
160
+ const { columns, key } = columnsAndKey(layout, entity, options.keys);
161
+ const missing = key.filter((column) => !columns.includes(column));
162
+ if (missing.length > 0) {
163
+ throw new EngineError(`the key of '${entity}' names columns the layout does not have: ` +
164
+ `[${missing.map((m) => `'${m}'`).join(', ')}]. In ClickHouse the key becomes ORDER BY, ` +
165
+ `so this would produce a table that cannot be created rather than one with a missing ` +
166
+ `constraint.`);
167
+ }
168
+ const where = `table '${table}'`;
169
+ // ORDER BY is the key, in declared order unless the layout gives a physical order. That order
170
+ // is positional and carries meaning: it decides which prefixes of the key can prune granules,
171
+ // so sorting it would change the table's performance behind an identical map.
172
+ const ordered = effectiveKey(where, entity, key, layout.keyOrder ?? {}, EngineError);
173
+ const spec = Object.hasOwn(layout.partitionBy, entity) ? layout.partitionBy[entity] : undefined;
174
+ const partition = partitionExpression(where, entity, key, spec, EngineError);
175
+ const parts = columns.map((c) => `${quoteBacktick(c)} ${typeOf(layout, entity, c)}`);
176
+ // Indexes inline: `CREATE TABLE IF NOT EXISTS` never adds one to an existing table, and a
177
+ // separate ALTER would be a mutation over every existing part.
178
+ for (const index of sortedIndexes(layout)) {
179
+ if (String(index['entity']) !== entity)
180
+ continue;
181
+ const indexed = index['columns'];
182
+ if (!Array.isArray(indexed) || indexed.length !== 1) {
183
+ throw new EngineError(`the data-skipping index '${String(index['name'])}' must summarise exactly one column`);
184
+ }
185
+ parts.push(`INDEX ${quoteBacktick(String(index['name']))} ${quoteBacktick(String(indexed[0]))} ` +
186
+ `TYPE ${skipIndexType(index)} GRANULARITY ${String(index['granularity'])}`);
187
+ }
188
+ const partitionClause = partition === null ? '' : `PARTITION BY ${partition[0]}(${quoteBacktick(partition[1])}) `;
189
+ const order = ordered.map(quoteBacktick).join(', ');
190
+ statements.push(`CREATE TABLE IF NOT EXISTS ${quoteBacktick(table)} (${parts.join(', ')}) ` +
191
+ `ENGINE = ReplacingMergeTree ${partitionClause}ORDER BY (${order})`);
192
+ }
193
+ return statements;
194
+ }
195
+ /**
196
+ * No DDL, because this engine's schema is not ours to create.
197
+ *
198
+ * An empty list rather than a throw, and the difference matters. "Run nothing" is the *correct*
199
+ * action for a caller preparing a fixed-schema engine: the storage exists the moment the engine
200
+ * opens its data directory. Throwing would push the branch out to every caller. What the empty
201
+ * list loses is the *explanation*, and {@link schemaIsFixed} supplies that to anybody printing
202
+ * one - "here is the schema we chose for you: (nothing)" needs a sentence after it.
203
+ */
204
+ function noStatements(layout) {
205
+ if (Object.keys(layout.keyOrder ?? {}).length > 0 ||
206
+ Object.keys(layout.partitionBy).length > 0 ||
207
+ layout.indexes.length > 0) {
208
+ throw new EngineError("this engine's schema is fixed in its own source, so a physical design - key order, " +
209
+ 'partition or indexes - cannot be applied to it. A map that declared one and got the ' +
210
+ 'fixed table anyway would be a storage decision silently dropped.');
211
+ }
212
+ return [];
213
+ }
214
+ const BY_DIALECT = {
215
+ clickhouse: clickhouseStatements,
216
+ orderbook: noStatements,
217
+ postgres: postgresStatements,
218
+ };
219
+ /**
220
+ * Whether this engine imposes its own schema, so an empty statement list means "nothing to do".
221
+ *
222
+ * The one public way to tell that apart from "no tables in this layout". A caller that never asks
223
+ * still behaves correctly - creating nothing is right - but a caller *reporting* what the client
224
+ * must do has a different sentence to write.
225
+ */
226
+ export function schemaIsFixed(dialect) {
227
+ if (!(dialect in BY_DIALECT)) {
228
+ throw new EngineError(`unknown dialect '${dialect}'; this library renders [${DIALECTS.join(', ')}]. Answering ` +
229
+ `false would say 'that engine takes DDL from us' about an engine it has never heard of.`);
230
+ }
231
+ return FIXED_SCHEMA.has(dialect);
232
+ }
233
+ /**
234
+ * The statements that would create this layout, in the order they must run.
235
+ *
236
+ * Idempotent by construction - every statement is `IF NOT EXISTS` - because an application
237
+ * restarting must not reapply DDL and two instances starting at once must not race. Anything
238
+ * beyond creation is a migration, which carries a rollback path and a safety classification.
239
+ *
240
+ * An unknown dialect throws. Falling back to ANSI would emit a statement that looks right, runs on
241
+ * the wrong engine, and creates a table with the wrong storage semantics.
242
+ */
243
+ export function schemaStatements(layout, options) {
244
+ const build = BY_DIALECT[options.dialect];
245
+ if (build === undefined) {
246
+ throw new EngineError(`no DDL for dialect '${options.dialect}'; this library renders [${DIALECTS.join(', ')}]. ` +
247
+ `Refusing rather than falling back to ANSI: a statement that looks right on the wrong ` +
248
+ `engine creates a table with the wrong storage semantics.`);
249
+ }
250
+ return build(layout, options);
251
+ }
252
+ /**
253
+ * Views on the target under the table names the group had in the engine it left.
254
+ *
255
+ * **A view cannot cross engines, and that is why this renders on the target.** The old table is in
256
+ * the old engine, and no dialect here has a way to select from another server. So what this offers
257
+ * is for the case where somebody re-points their tool at the new engine and their SQL still says
258
+ * the old table name.
259
+ *
260
+ * Columns are listed rather than `SELECT *`, so the view names exactly what the map names, and
261
+ * they are listed **sorted by name, exactly as `CREATE TABLE` sorts them**, because a view whose
262
+ * columns came out differently would break anything reading them by position, which is most of
263
+ * what a hand-written query does with `SELECT *`. The reference implementation read the order off
264
+ * the layout document instead, on the stated grounds that the document was already sorted - it is
265
+ * not, since a foreign-key column is appended per relation, so any entity with a relation had a
266
+ * view and a table that disagreed. `schema/009` feeds a document whose columns are out of order on
267
+ * purpose, which makes both sorts separately mutable.
268
+ *
269
+ * For ClickHouse the view reads `FINAL`, and that is the substance rather than a detail: the table
270
+ * is a `ReplacingMergeTree`, so a plain read returns rows the declared key should have collapsed
271
+ * until a background merge happens. A query moved verbatim to the target returns numbers that are
272
+ * too big, silently. A view that quietly reproduced that would be worse than no view.
273
+ */
274
+ export function compatibilityViews(layout, options) {
275
+ const { was, dialect } = options;
276
+ if (!(dialect in BY_DIALECT)) {
277
+ throw new EngineError(`no compatibility view for dialect '${dialect}'; this library renders ` +
278
+ `[${DIALECTS.join(', ')}].`);
279
+ }
280
+ const entities = tablesInOrder(layout);
281
+ if (FIXED_SCHEMA.has(dialect)) {
282
+ const notPossible = entities.map(([entity, table]) => [
283
+ entity,
284
+ `${dialect} imposes its own schema and accepts no DDL from this library, so there is ` +
285
+ `nowhere to put a view. Its table is '${table}' and the old name was ` +
286
+ `'${was[entity]}': a query naming the old one has to be edited.`,
287
+ ]);
288
+ return { create: [], drop: [], notPossible, complete: notPossible.length === 0 };
289
+ }
290
+ const quote = QUOTE[dialect];
291
+ // Unreachable: `FIXED_SCHEMA` is the only dialect without an entry and it returned above. A
292
+ // narrowing check rather than a non-null assertion, because the assertion is the version that
293
+ // has no message on the day the two tables stop agreeing.
294
+ if (quote === undefined)
295
+ throw new EngineError(`no quoting rule for dialect '${dialect}'`);
296
+ // Idempotent like every statement `schemaStatements` renders, and the two dialects spell that
297
+ // differently - measured against both servers rather than assumed. PostgreSQL has no
298
+ // `CREATE VIEW IF NOT EXISTS`: it is a syntax error.
299
+ const opening = dialect === 'postgres' ? 'CREATE OR REPLACE VIEW' : 'CREATE VIEW IF NOT EXISTS';
300
+ const final = dialect === 'clickhouse' ? ' FINAL' : '';
301
+ const create = [];
302
+ const drop = [];
303
+ const notPossible = [];
304
+ for (const [entity, table] of entities) {
305
+ const old = was[entity];
306
+ if (old === undefined) {
307
+ notPossible.push([
308
+ entity,
309
+ `the source layout gives no table for '${entity}', so there is no old name to stand in for.`,
310
+ ]);
311
+ continue;
312
+ }
313
+ if (old === table) {
314
+ notPossible.push([
315
+ entity,
316
+ `both engines call this table '${table}', so the name is not what moved - the dialect is.` +
317
+ (dialect === 'clickhouse'
318
+ ? ` A query moved here verbatim must read \`FROM ${quote(table)} FINAL\`: the table ` +
319
+ `is a ReplacingMergeTree, so without it a row written twice under one key is ` +
320
+ `counted twice until a background merge collapses it - measured, two rows against ` +
321
+ `one.`
322
+ : ''),
323
+ ]);
324
+ continue;
325
+ }
326
+ const cols = sorted(Object.keys(layout.columns[entity] ?? {}));
327
+ if (cols.length === 0)
328
+ throw new EngineError(`the layout gives no columns for '${entity}'`);
329
+ // Sorted by name, which is what `CREATE TABLE` does. See the docstring: reading this order off
330
+ // the document instead was wrong, and wrong in a way one fixture could not show.
331
+ const selected = cols.map(quote).join(', ');
332
+ create.push(`${opening} ${quote(old)} AS SELECT ${selected} FROM ${quote(table)}${final}`);
333
+ drop.push(`DROP VIEW IF EXISTS ${quote(old)}`);
334
+ }
335
+ return { create, drop, notPossible, complete: notPossible.length === 0 };
336
+ }
337
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}