@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.
- package/LICENSE +201 -0
- package/NOTICE +13 -0
- package/README.md +153 -0
- package/bin/weather.mjs +32 -0
- package/dist/_usage.d.ts +30 -0
- package/dist/_usage.js +194 -0
- package/dist/_usage.js.map +1 -0
- package/dist/bulk.d.ts +9 -0
- package/dist/bulk.js +83 -0
- package/dist/bulk.js.map +1 -0
- package/dist/canonical.d.ts +46 -0
- package/dist/canonical.js +150 -0
- package/dist/canonical.js.map +1 -0
- package/dist/capabilities.d.ts +48 -0
- package/dist/capabilities.js +62 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/cutover.d.ts +36 -0
- package/dist/cutover.js +219 -0
- package/dist/cutover.js.map +1 -0
- package/dist/demo/model.d.ts +28 -0
- package/dist/demo/model.js +40 -0
- package/dist/demo/model.js.map +1 -0
- package/dist/demo/project.d.ts +19 -0
- package/dist/demo/project.js +128 -0
- package/dist/demo/project.js.map +1 -0
- package/dist/demo/weather.d.ts +73 -0
- package/dist/demo/weather.js +334 -0
- package/dist/demo/weather.js.map +1 -0
- package/dist/engines/_clickhouse-connection.d.ts +17 -0
- package/dist/engines/_clickhouse-connection.js +182 -0
- package/dist/engines/_clickhouse-connection.js.map +1 -0
- package/dist/engines/_tls-peer-identity.d.ts +2 -0
- package/dist/engines/_tls-peer-identity.js +23 -0
- package/dist/engines/_tls-peer-identity.js.map +1 -0
- package/dist/engines/_write-fences.d.ts +51 -0
- package/dist/engines/_write-fences.js +189 -0
- package/dist/engines/_write-fences.js.map +1 -0
- package/dist/engines/clickhouse.d.ts +193 -0
- package/dist/engines/clickhouse.js +899 -0
- package/dist/engines/clickhouse.js.map +1 -0
- package/dist/engines/postgres.d.ts +293 -0
- package/dist/engines/postgres.js +981 -0
- package/dist/engines/postgres.js.map +1 -0
- package/dist/errors.d.ts +89 -0
- package/dist/errors.js +90 -0
- package/dist/errors.js.map +1 -0
- package/dist/frozen-verification.d.ts +26 -0
- package/dist/frozen-verification.js +67 -0
- package/dist/frozen-verification.js.map +1 -0
- package/dist/generation.d.ts +34 -0
- package/dist/generation.js +81 -0
- package/dist/generation.js.map +1 -0
- package/dist/groups.d.ts +17 -0
- package/dist/groups.js +66 -0
- package/dist/groups.js.map +1 -0
- package/dist/hashing.d.ts +68 -0
- package/dist/hashing.js +146 -0
- package/dist/hashing.js.map +1 -0
- package/dist/in-place-index.d.ts +43 -0
- package/dist/in-place-index.js +272 -0
- package/dist/in-place-index.js.map +1 -0
- package/dist/index.d.ts +79 -0
- package/dist/index.js +64 -0
- package/dist/index.js.map +1 -0
- package/dist/inspection.d.ts +19 -0
- package/dist/inspection.js +31 -0
- package/dist/inspection.js.map +1 -0
- package/dist/internal.d.ts +42 -0
- package/dist/internal.js +56 -0
- package/dist/internal.js.map +1 -0
- package/dist/layout.d.ts +36 -0
- package/dist/layout.js +62 -0
- package/dist/layout.js.map +1 -0
- package/dist/migration.d.ts +197 -0
- package/dist/migration.js +592 -0
- package/dist/migration.js.map +1 -0
- package/dist/model.d.ts +93 -0
- package/dist/model.js +313 -0
- package/dist/model.js.map +1 -0
- package/dist/physical.d.ts +128 -0
- package/dist/physical.js +421 -0
- package/dist/physical.js.map +1 -0
- package/dist/placement.d.ts +157 -0
- package/dist/placement.js +651 -0
- package/dist/placement.js.map +1 -0
- package/dist/provisioning.d.ts +6 -0
- package/dist/provisioning.js +45 -0
- package/dist/provisioning.js.map +1 -0
- package/dist/query.d.ts +68 -0
- package/dist/query.js +340 -0
- package/dist/query.js.map +1 -0
- package/dist/routing.d.ts +25 -0
- package/dist/routing.js +35 -0
- package/dist/routing.js.map +1 -0
- package/dist/schema.d.ts +110 -0
- package/dist/schema.js +337 -0
- package/dist/schema.js.map +1 -0
- package/dist/session.d.ts +195 -0
- package/dist/session.js +870 -0
- package/dist/session.js.map +1 -0
- package/dist/shapes.d.ts +30 -0
- package/dist/shapes.js +112 -0
- package/dist/shapes.js.map +1 -0
- package/dist/staging.d.ts +29 -0
- package/dist/staging.js +214 -0
- package/dist/staging.js.map +1 -0
- package/dist/telemetry.d.ts +468 -0
- package/dist/telemetry.js +872 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/testing/loader.d.ts +38 -0
- package/dist/testing/loader.js +86 -0
- package/dist/testing/loader.js.map +1 -0
- package/dist/testing/memory.d.ts +131 -0
- package/dist/testing/memory.js +311 -0
- package/dist/testing/memory.js.map +1 -0
- package/dist/timestamp.d.ts +20 -0
- package/dist/timestamp.js +89 -0
- package/dist/timestamp.js.map +1 -0
- package/dist/types.d.ts +79 -0
- package/dist/types.js +100 -0
- package/dist/types.js.map +1 -0
- package/dist/verification.d.ts +41 -0
- package/dist/verification.js +169 -0
- package/dist/verification.js.map +1 -0
- package/dist/watermark.d.ts +103 -0
- package/dist/watermark.js +170 -0
- package/dist/watermark.js.map +1 -0
- package/dist/write-fence.d.ts +58 -0
- package/dist/write-fence.js +225 -0
- package/dist/write-fence.js.map +1 -0
- package/package.json +86 -0
package/dist/schema.d.ts
ADDED
|
@@ -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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"}
|