@zmdb/singlestore 1.0.0-beta.1
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 +6 -0
- package/README.md +115 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/introspect.d.ts +5 -0
- package/dist/introspect.d.ts.map +1 -0
- package/dist/introspect.js +120 -0
- package/dist/introspect.js.map +1 -0
- package/dist/migrations.d.ts +7 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +185 -0
- package/dist/migrations.js.map +1 -0
- package/package.json +60 -0
- package/src/index.ts +62 -0
- package/src/introspect.ts +178 -0
- package/src/migrations.ts +257 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
|
|
3
|
+
This package is part of zmdb and is licensed under the GNU General Public
|
|
4
|
+
License, version 3 or (at your option) any later version. The complete license
|
|
5
|
+
text is distributed in the root LICENSE file of the source repository:
|
|
6
|
+
https://github.com/ambasta/zmdb/blob/main/LICENSE
|
package/README.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# @zmdb/singlestore
|
|
2
|
+
|
|
3
|
+
The SingleStore vertical owns the immutable `singlestore` dialect, its migration hooks and catalog introspector, and the `singlestoreDriver` adapter. The shared query compiler comes from `@zmdb/sql`;
|
|
4
|
+
repository and driver contracts come from `@zmdb/orm`. `singlestoreVertical` pairs this dialect with its driver factory.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
yarn add @zmdb/singlestore@1.0.0-beta.1 @zmdb/sql@1.0.0-beta.1 @zmdb/migrations@1.0.0-beta.1 mysql2@^3.24.3
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
For the TypeScript snippets, install the declaration inputs used by the packed consumer:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
yarn add --dev typescript@7.0.2 @types/node@26.4.1
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Use Node.js 26+ and ESM. Keep the required `@zmdb/sql` and `@zmdb/orm` peers aligned with this package's version; npm resolves those peers. An application already using `zmdb` adds its selected
|
|
19
|
+
database package and client rather than replacing the product facade.
|
|
20
|
+
|
|
21
|
+
`mysql2` is an optional peer in package metadata; install it explicitly for this recipe. The package depends on its MySQL-family parent and accepts an application-owned client.
|
|
22
|
+
|
|
23
|
+
## Configure
|
|
24
|
+
|
|
25
|
+
The snippets below are successive steps in one module.
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { createPool } from 'mysql2/promise';
|
|
29
|
+
import { singlestore, singlestoreDriver } from '@zmdb/singlestore';
|
|
30
|
+
|
|
31
|
+
const address = process.env.DATABASE_URL;
|
|
32
|
+
if (address === undefined) throw new Error('DATABASE_URL is required');
|
|
33
|
+
const client = createPool(address);
|
|
34
|
+
const driver = singlestoreDriver(client);
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The application owns the client and closes it with `await client.end()` in a `finally` block after its work. Hosted services that accept this client's protocol are connection recipes, not additional
|
|
38
|
+
official database packages or automatically qualified server variants.
|
|
39
|
+
|
|
40
|
+
## Compile
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { createQueryCompiler } from '@zmdb/sql';
|
|
44
|
+
|
|
45
|
+
const compiler = createQueryCompiler(singlestore);
|
|
46
|
+
const query = compiler.selectFrom('users').where('id', '=', 7).compile();
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Compilation is pure: the result carries SQL text and a separate parameter array. It does not create the `users` table or open a connection.
|
|
50
|
+
|
|
51
|
+
## Migrate
|
|
52
|
+
|
|
53
|
+
Use `singlestore.migrations.emitUp(operation)` to obtain this database's DDL and `singlestore.migrations.connection(driver)` to create its migration connection. Pass reviewed migration records to `up`
|
|
54
|
+
and `down` from `@zmdb/migrations`; transactional behavior follows the capability table below. The [complete installed workflow](../../fixtures/consumer-database-publication/runtime.mjs) creates a
|
|
55
|
+
fresh table, applies and rolls back its migration, and closes the supplied client.
|
|
56
|
+
|
|
57
|
+
## Introspect
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
const snapshot = await singlestore.introspector.snapshot(driver);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The snapshot comes from the selected database's real catalog through the same driver. `singlestoreIntrospector` is also exported directly. Introspection and generated DDL use this vertical's
|
|
64
|
+
semantics.
|
|
65
|
+
|
|
66
|
+
## Execute
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
const rows = await driver.execute(query);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Run this after migrating or otherwise creating the table. Use `driver.transaction(async transaction => ...)` for a pinned transactional driver; the application decides whether to retry. Query values
|
|
73
|
+
travel as parameters, not SQL string interpolation.
|
|
74
|
+
|
|
75
|
+
## Capabilities
|
|
76
|
+
|
|
77
|
+
These entries describe `singlestore.capabilities`; a true entry can still require the client support described below.
|
|
78
|
+
|
|
79
|
+
| Capability | Advertised support |
|
|
80
|
+
| ------------------------------------- | ------------------ |
|
|
81
|
+
| INSERT/upsert/UPDATE/DELETE returning | no |
|
|
82
|
+
| Transactional DDL | no |
|
|
83
|
+
| Schemas | yes |
|
|
84
|
+
| Sequences | no |
|
|
85
|
+
| Generated columns | yes |
|
|
86
|
+
| Partial indexes | no |
|
|
87
|
+
| Foreign keys | no |
|
|
88
|
+
| Row-level security | no |
|
|
89
|
+
| Streaming | no |
|
|
90
|
+
| Server-side cancellation | no |
|
|
91
|
+
|
|
92
|
+
## Refusals and ownership
|
|
93
|
+
|
|
94
|
+
Generated tables must declare a shard key or rowstore storage. Foreign keys, incompatible unique keys, check constraints, unsupported explicit index methods, sort keys on rowstore tables, storage
|
|
95
|
+
transitions and MySQL routine declarations are refused. `RETURNING`, transactional DDL, sequences, partial indexes, row-level security, streaming and server-side cancellation are not advertised.
|
|
96
|
+
|
|
97
|
+
SingleStore is a one-way child of `@zmdb/mysql`. It reuses public MySQL-family factories and owns SingleStore storage, migration, type and catalog overrides. The parent never depends on its child; the
|
|
98
|
+
shared mysql2 client does not make a MySQL server a SingleStore substitute.
|
|
99
|
+
|
|
100
|
+
`serial` emits `BIGINT AUTO_INCREMENT`, timestamps use `DATETIME(6)`, and full-text matching uses `MATCH(column) AGAINST(?)`. Shard keys, sort keys and rowstore storage are part of this vertical's
|
|
101
|
+
schema round trip.
|
|
102
|
+
|
|
103
|
+
## Testing evidence
|
|
104
|
+
|
|
105
|
+
The [database publication qualification](../../fixtures/consumer-database-publication) builds real npm archives and installs this selected package in an independent consumer. Its public workflow
|
|
106
|
+
covers strict declarations, package/client ownership, parameterized CRUD, transaction rollback, migration application/rollback and catalog introspection. The
|
|
107
|
+
[SingleStore consumer](../../fixtures/database-singlestore) adds the database-specific capability and refusal checks.
|
|
108
|
+
|
|
109
|
+
Issue [#676](https://github.com/ambasta/zmdb/issues/676) records the completed installed workflows. Those observations are scoped to their recorded clients and servers; they do not certify every
|
|
110
|
+
compatible hosted service. Qualification reports identify their source, archive and server inputs. See the [SingleStore guide](../../docs-site/content/dialect-singlestore.md) for the detailed
|
|
111
|
+
contract.
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
GNU General Public License v3.0 or later (GPL-3.0-or-later) — see [LICENSE](./LICENSE).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type MysqlConnection, type MysqlDriver, type MysqlExecutionResult, type MysqlOptions, type MysqlParameter, type MysqlPool, type MysqlQueryable, type MysqlQueryResult, type MysqlResultHeader } from '@zmdb/mysql';
|
|
2
|
+
import { type DatabaseVertical } from '@zmdb/orm';
|
|
3
|
+
import { type SqlDialect } from '@zmdb/sql';
|
|
4
|
+
import { singlestoreIntrospector } from './introspect.js';
|
|
5
|
+
import { singlestoreMigrations } from './migrations.js';
|
|
6
|
+
export type { MysqlConnection, MysqlDriver, MysqlExecutionResult, MysqlOptions, MysqlParameter, MysqlPool, MysqlQueryable, MysqlQueryResult, MysqlResultHeader, };
|
|
7
|
+
export { singlestoreIntrospector, singlestoreMigrations };
|
|
8
|
+
export declare const singlestore: SqlDialect<'singlestore'>;
|
|
9
|
+
export declare function singlestoreDriver(client: MysqlQueryable, options?: MysqlOptions): MysqlDriver<'singlestore'>;
|
|
10
|
+
export declare const singlestoreVertical: DatabaseVertical<'singlestore', MysqlQueryable, MysqlOptions>;
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACvB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAClD,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAE9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAA8B,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAEpF,YAAY,EACV,eAAe,EACf,WAAW,EACX,oBAAoB,EACpB,YAAY,EACZ,cAAc,EACd,SAAS,EACT,cAAc,EACd,gBAAgB,EAChB,iBAAiB,GAClB,CAAC;AACF,OAAO,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,CAAC;AAU1D,eAAO,MAAM,WAAW,EAAE,UAAU,CAAC,aAAa,CAYhD,CAAC;AAEH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,WAAW,CAAC,aAAa,CAAC,CAE5G;AAED,eAAO,MAAM,mBAAmB,EAAE,gBAAgB,CAAC,aAAa,EAAE,cAAc,EAAE,YAAY,CAG5F,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { mysql, mysqlFamilyDriver, } from '@zmdb/mysql';
|
|
2
|
+
import {} from '@zmdb/orm';
|
|
3
|
+
import { extendSqlDialect } from '@zmdb/sql';
|
|
4
|
+
import { singlestoreIntrospector } from './introspect.js';
|
|
5
|
+
import { SINGLESTORE_TYPE_OVERRIDES, singlestoreMigrations } from './migrations.js';
|
|
6
|
+
export { singlestoreIntrospector, singlestoreMigrations };
|
|
7
|
+
const outbox = Object.freeze({
|
|
8
|
+
createTable: 'CREATE ROWSTORE TABLE',
|
|
9
|
+
pendingIndex: 'full',
|
|
10
|
+
epochLiteral: "'1970-01-01 00:00:00.000000'",
|
|
11
|
+
createdAtDefault: 'CURRENT_TIMESTAMP(6)',
|
|
12
|
+
boundedTextType: (length) => `VARCHAR(${String(length)})`,
|
|
13
|
+
});
|
|
14
|
+
export const singlestore = extendSqlDialect(mysql, {
|
|
15
|
+
name: 'singlestore',
|
|
16
|
+
traits: {
|
|
17
|
+
fts: 'matchPlain',
|
|
18
|
+
types: SINGLESTORE_TYPE_OVERRIDES,
|
|
19
|
+
},
|
|
20
|
+
capabilities: {
|
|
21
|
+
foreignKeys: false,
|
|
22
|
+
},
|
|
23
|
+
migrations: singlestoreMigrations,
|
|
24
|
+
introspector: singlestoreIntrospector,
|
|
25
|
+
outbox,
|
|
26
|
+
});
|
|
27
|
+
export function singlestoreDriver(client, options) {
|
|
28
|
+
return mysqlFamilyDriver(singlestore, client, options);
|
|
29
|
+
}
|
|
30
|
+
export const singlestoreVertical = Object.freeze({
|
|
31
|
+
dialect: singlestore,
|
|
32
|
+
driver: singlestoreDriver,
|
|
33
|
+
});
|
|
34
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EACL,iBAAiB,GAUlB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAyB,MAAM,WAAW,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAmB,MAAM,WAAW,CAAC;AAE9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAapF,OAAO,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,CAAC;AAE1D,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAC3B,WAAW,EAAE,uBAAuB;IACpC,YAAY,EAAE,MAAe;IAC7B,YAAY,EAAE,8BAA8B;IAC5C,gBAAgB,EAAE,sBAAsB;IACxC,eAAe,EAAE,CAAC,MAAc,EAAE,EAAE,CAAC,WAAW,MAAM,CAAC,MAAM,CAAC,GAAG;CAClE,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,WAAW,GAA8B,gBAAgB,CAAC,KAAK,EAAE;IAC5E,IAAI,EAAE,aAAa;IACnB,MAAM,EAAE;QACN,GAAG,EAAE,YAAY;QACjB,KAAK,EAAE,0BAA0B;KAClC;IACD,YAAY,EAAE;QACZ,WAAW,EAAE,KAAK;KACnB;IACD,UAAU,EAAE,qBAAqB;IACjC,YAAY,EAAE,uBAAuB;IACrC,MAAM;CACP,CAAC,CAAC;AAEH,MAAM,UAAU,iBAAiB,CAAC,MAAsB,EAAE,OAAsB;IAC9E,OAAO,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAkE,MAAM,CAAC,MAAM,CAAC;IAC9G,OAAO,EAAE,WAAW;IACpB,MAAM,EAAE,iBAAiB;CAC1B,CAAC,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type CatalogSchemaSnapshot } from '@zmdb/migrations/introspect/runtime';
|
|
2
|
+
import { type IntrospectionDriver, type Introspector, type IntrospectOptions } from '@zmdb/sql';
|
|
3
|
+
export declare function singlestoreSnapshot(driver: IntrospectionDriver, options: IntrospectOptions | undefined, parent: (driver: IntrospectionDriver, options?: IntrospectOptions) => Promise<CatalogSchemaSnapshot>): Promise<CatalogSchemaSnapshot>;
|
|
4
|
+
export declare const singlestoreIntrospector: Introspector<'singlestore'>;
|
|
5
|
+
//# sourceMappingURL=introspect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"introspect.d.ts","sourceRoot":"","sources":["../src/introspect.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,qBAAqB,EAE3B,MAAM,qCAAqC,CAAC;AAE7C,OAAO,EAGL,KAAK,mBAAmB,EACxB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACvB,MAAM,WAAW,CAAC;AAyGnB,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,iBAAiB,YAAK,EAC/B,MAAM,EAAE,CAAC,MAAM,EAAE,mBAAmB,EAAE,OAAO,CAAC,EAAE,iBAAiB,KAAK,OAAO,CAAC,qBAAqB,CAAC,GACnG,OAAO,CAAC,qBAAqB,CAAC,CA0ChC;AAMD,eAAO,MAAM,uBAAuB,EAAE,YAAY,CAAC,aAAa,CAK9D,CAAC"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { query, sortByName, textField, } from '@zmdb/migrations/introspect/runtime';
|
|
2
|
+
import { mysql, mysqlFamilyIntrospector } from '@zmdb/mysql';
|
|
3
|
+
import { quoteIdentifier, } from '@zmdb/sql';
|
|
4
|
+
function placeholders(count) {
|
|
5
|
+
return Array.from({ length: count }, () => '?').join(', ');
|
|
6
|
+
}
|
|
7
|
+
function schemaFilter(options) {
|
|
8
|
+
const schemas = options.schemas;
|
|
9
|
+
if (schemas === undefined || schemas.length === 0)
|
|
10
|
+
return { sql: 'TABLE_SCHEMA = DATABASE()', parameters: [] };
|
|
11
|
+
const distinct = [...new Set(schemas)].toSorted();
|
|
12
|
+
return {
|
|
13
|
+
sql: `TABLE_SCHEMA IN (${placeholders(distinct.length)})`,
|
|
14
|
+
parameters: distinct,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function mysqlCompatibleCatalog(driver) {
|
|
18
|
+
return {
|
|
19
|
+
execute(compiled) {
|
|
20
|
+
if (!compiled.text.includes('FROM information_schema.STATISTICS'))
|
|
21
|
+
return driver.execute(compiled);
|
|
22
|
+
const text = compiled.text.replace('COLUMN_NAME, EXPRESSION, INDEX_TYPE', 'COLUMN_NAME, NULL AS EXPRESSION, INDEX_TYPE');
|
|
23
|
+
return driver.execute({ ...compiled, text });
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function identifierList(clause) {
|
|
28
|
+
const identifiers = [];
|
|
29
|
+
for (const match of clause.matchAll(/`((?:``|[^`])+)`/gu)) {
|
|
30
|
+
const identifier = match[1];
|
|
31
|
+
if (identifier !== undefined)
|
|
32
|
+
identifiers.push(identifier.replaceAll('``', '`'));
|
|
33
|
+
}
|
|
34
|
+
return identifiers;
|
|
35
|
+
}
|
|
36
|
+
function tableOptions(createTable, storage) {
|
|
37
|
+
const shard = /\bSHARD\s+KEY(?:\s+`(?:``|[^`])+`)?\s*\(([^)]*)\)/iu.exec(createTable);
|
|
38
|
+
const sort = /\bSORT\s+KEY(?:\s+`(?:``|[^`])+`)?\s*\(([^)]*)\)/iu.exec(createTable);
|
|
39
|
+
const rowstore = storage === 'INMEMORY_ROWSTORE' || storage === 'ROWSTORE';
|
|
40
|
+
if (!rowstore && storage !== 'COLUMNSTORE') {
|
|
41
|
+
throw new TypeError(`singlestore information_schema.TABLES returned unknown STORAGE_TYPE ${JSON.stringify(storage)}`);
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
...(shard?.[1] === undefined ? {} : { shardKey: identifierList(shard[1]) }),
|
|
45
|
+
...(sort?.[1] === undefined ? {} : { sortKey: identifierList(sort[1]) }),
|
|
46
|
+
...(rowstore ? { rowstore: true } : {}),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function showCreateText(row, index) {
|
|
50
|
+
const direct = Reflect.get(row, 'Create Table');
|
|
51
|
+
if (typeof direct === 'string')
|
|
52
|
+
return direct;
|
|
53
|
+
return textField(row, 'CREATE_TABLE', 'singlestore SHOW CREATE TABLE', index);
|
|
54
|
+
}
|
|
55
|
+
function physicalIndexes(table) {
|
|
56
|
+
return table.indexes.filter(index => index.method !== 'shard' && index.method !== 'clustered columnstore');
|
|
57
|
+
}
|
|
58
|
+
function computedColumns(table) {
|
|
59
|
+
return table.columns.map(column => column.generated === undefined
|
|
60
|
+
? column
|
|
61
|
+
: {
|
|
62
|
+
...column,
|
|
63
|
+
generated: {
|
|
64
|
+
...column.generated,
|
|
65
|
+
stored: /\bPERSISTED\b/iu.test(column.catalogType),
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
function exactTimestampWarnings(base) {
|
|
70
|
+
const exact = new Set(base.tables.flatMap(table => table.columns
|
|
71
|
+
.filter(column => column.catalogType.toLowerCase() === 'datetime(6)')
|
|
72
|
+
.map(column => `${table.name}\0${column.name}`)));
|
|
73
|
+
return base.warnings.filter(warning => warning.column === undefined ||
|
|
74
|
+
!exact.has(`${warning.table}\0${warning.column}`) ||
|
|
75
|
+
!warning.reason.includes('forward DDL emits DATETIME(3)'));
|
|
76
|
+
}
|
|
77
|
+
export async function singlestoreSnapshot(driver, options = {}, parent) {
|
|
78
|
+
const base = await parent(mysqlCompatibleCatalog(driver), options);
|
|
79
|
+
const filter = schemaFilter(options);
|
|
80
|
+
const storageRows = await driver.execute(query(`SELECT TABLE_SCHEMA, TABLE_NAME, STORAGE_TYPE FROM information_schema.TABLES ` +
|
|
81
|
+
`WHERE ${filter.sql} AND TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_SCHEMA, TABLE_NAME`, filter.parameters));
|
|
82
|
+
const storage = storageRows.map((row, index) => ({
|
|
83
|
+
schema: textField(row, 'TABLE_SCHEMA', 'singlestore information_schema.TABLES', index),
|
|
84
|
+
table: textField(row, 'TABLE_NAME', 'singlestore information_schema.TABLES', index),
|
|
85
|
+
storage: textField(row, 'STORAGE_TYPE', 'singlestore information_schema.TABLES', index),
|
|
86
|
+
}));
|
|
87
|
+
const byTable = new Map(storage.map(row => [row.table, row]));
|
|
88
|
+
const tables = [];
|
|
89
|
+
for (const table of base.tables) {
|
|
90
|
+
const metadata = byTable.get(table.name);
|
|
91
|
+
if (metadata === undefined) {
|
|
92
|
+
throw new TypeError(`singlestore catalog has no storage metadata for table "${table.name}"`);
|
|
93
|
+
}
|
|
94
|
+
const rows = await driver.execute(query(`SHOW CREATE TABLE ${quoteIdentifier(mysql, metadata.schema)}.${quoteIdentifier(mysql, metadata.table)}`, []));
|
|
95
|
+
const row = rows[0];
|
|
96
|
+
if (row === undefined)
|
|
97
|
+
throw new TypeError(`singlestore SHOW CREATE TABLE returned no row for "${table.name}"`);
|
|
98
|
+
tables.push({
|
|
99
|
+
...table,
|
|
100
|
+
columns: computedColumns(table),
|
|
101
|
+
indexes: physicalIndexes(table),
|
|
102
|
+
tableOptions: tableOptions(showCreateText(row, 0), metadata.storage),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
...base,
|
|
107
|
+
tables: sortByName(tables),
|
|
108
|
+
warnings: exactTimestampWarnings(base),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const family = mysqlFamilyIntrospector('singlestore', {
|
|
112
|
+
snapshot: singlestoreSnapshot,
|
|
113
|
+
});
|
|
114
|
+
export const singlestoreIntrospector = Object.freeze({
|
|
115
|
+
...family,
|
|
116
|
+
normalizeForDrift(snapshot, role) {
|
|
117
|
+
return family.normalizeForDrift(snapshot, role);
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
//# sourceMappingURL=introspect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"introspect.js","sourceRoot":"","sources":["../src/introspect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type MigrationDialect } from '@zmdb/sql';
|
|
2
|
+
export declare const SINGLESTORE_TYPE_OVERRIDES: Readonly<{
|
|
3
|
+
serial: "BIGINT AUTO_INCREMENT";
|
|
4
|
+
timestamp: "DATETIME(6)";
|
|
5
|
+
}>;
|
|
6
|
+
export declare const singlestoreMigrations: MigrationDialect<'singlestore'>;
|
|
7
|
+
//# sourceMappingURL=migrations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../src/migrations.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,gBAAgB,EAKtB,MAAM,WAAW,CAAC;AAEnB,eAAO,MAAM,0BAA0B;;;EAGrC,CAAC;AA6MH,eAAO,MAAM,qBAAqB,EAAE,gBAAgB,CAAC,aAAa,CAmChE,CAAC"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { mysql, mysqlFamilyMigrations } from '@zmdb/mysql';
|
|
2
|
+
import { UnsupportedFeatureError, quoteIdentifier, } from '@zmdb/sql';
|
|
3
|
+
export const SINGLESTORE_TYPE_OVERRIDES = Object.freeze({
|
|
4
|
+
serial: 'BIGINT AUTO_INCREMENT',
|
|
5
|
+
timestamp: 'DATETIME(6)',
|
|
6
|
+
});
|
|
7
|
+
function unsupported(feature, message) {
|
|
8
|
+
throw new UnsupportedFeatureError(feature, 'singlestore', message);
|
|
9
|
+
}
|
|
10
|
+
function columnsExist(table, label, columns) {
|
|
11
|
+
if (columns === undefined)
|
|
12
|
+
return;
|
|
13
|
+
if (columns.length === 0)
|
|
14
|
+
throw new TypeError(`${label} on "${table.name}" must name at least one column`);
|
|
15
|
+
if (new Set(columns).size !== columns.length) {
|
|
16
|
+
throw new TypeError(`${label} on "${table.name}" must not repeat a column`);
|
|
17
|
+
}
|
|
18
|
+
const available = new Set(table.columns.map(column => column.name));
|
|
19
|
+
for (const column of columns) {
|
|
20
|
+
if (!available.has(column)) {
|
|
21
|
+
throw new TypeError(`${label} on "${table.name}" names unknown column "${column}"`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function includesShardKey(key, shardKey) {
|
|
26
|
+
const columns = new Set(key);
|
|
27
|
+
return shardKey.every(column => columns.has(column));
|
|
28
|
+
}
|
|
29
|
+
function validateUniqueKeys(table, shardKey) {
|
|
30
|
+
if (shardKey !== undefined && table.primaryKey.length > 0 && !includesShardKey(table.primaryKey, shardKey)) {
|
|
31
|
+
unsupported(`primary key outside the shard key on "${table.name}"`, `singlestore cannot enforce the primary key on "${table.name}" unless it includes the whole shard key ` +
|
|
32
|
+
`(${shardKey.join(', ')}); change the key or storage declaration before execution`);
|
|
33
|
+
}
|
|
34
|
+
for (const column of table.columns) {
|
|
35
|
+
const inlinePrimary = table.primaryKey.length === 1 && table.primaryKey[0] === column.name;
|
|
36
|
+
const standaloneUnique = column.unique === true && column.type !== 'serial' && !inlinePrimary;
|
|
37
|
+
const unkeyedSerial = column.type === 'serial' && !column.primaryKey;
|
|
38
|
+
if (!standaloneUnique && !unkeyedSerial)
|
|
39
|
+
continue;
|
|
40
|
+
if (shardKey !== undefined && includesShardKey([column.name], shardKey))
|
|
41
|
+
continue;
|
|
42
|
+
unsupported(`unique column "${column.name}" outside the shard key`, `singlestore cannot enforce UNIQUE on "${table.name}"."${column.name}" unless that index includes the ` +
|
|
43
|
+
`whole shard key (${shardKey?.join(', ') ?? 'none declared'}); change the shard key or enforce uniqueness ` +
|
|
44
|
+
'in the application');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function validateTable(table) {
|
|
48
|
+
if (table.foreignKeys.length > 0) {
|
|
49
|
+
unsupported('foreign keys', `@zmdb/singlestore does not qualify foreign-key DDL; remove the ${table.foreignKeys.length === 1 ? 'constraint' : 'constraints'} from "${table.name}" before execution`);
|
|
50
|
+
}
|
|
51
|
+
const options = table.tableOptions;
|
|
52
|
+
const shardKey = options?.shardKey;
|
|
53
|
+
const sortKey = options?.sortKey;
|
|
54
|
+
if (shardKey === undefined && options?.rowstore !== true) {
|
|
55
|
+
unsupported('table options', `singlestore table "${table.name}" must declare ShardKey<…> or Rowstore; ` +
|
|
56
|
+
'leaving both absent makes storage and distribution an accidental default');
|
|
57
|
+
}
|
|
58
|
+
if (options?.rowstore === true && sortKey !== undefined) {
|
|
59
|
+
unsupported(`sort key on rowstore table "${table.name}"`, `singlestore cannot declare SORT KEY on explicit ROWSTORE table "${table.name}"; ` +
|
|
60
|
+
'use an ordinary rowstore index or remove Rowstore to create a columnstore table');
|
|
61
|
+
}
|
|
62
|
+
columnsExist(table, 'shard key', shardKey);
|
|
63
|
+
columnsExist(table, 'sort key', sortKey);
|
|
64
|
+
validateUniqueKeys(table, shardKey);
|
|
65
|
+
}
|
|
66
|
+
function tableShape(operation) {
|
|
67
|
+
return {
|
|
68
|
+
name: operation.table,
|
|
69
|
+
columns: operation.columns,
|
|
70
|
+
primaryKey: operation.primaryKey,
|
|
71
|
+
foreignKeys: operation.foreignKeys,
|
|
72
|
+
...(operation.tableOptions === undefined ? {} : { tableOptions: operation.tableOptions }),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const parent = mysqlFamilyMigrations('singlestore', {
|
|
76
|
+
types: SINGLESTORE_TYPE_OVERRIDES,
|
|
77
|
+
ledger: {
|
|
78
|
+
createPrefix: 'CREATE ROWSTORE TABLE',
|
|
79
|
+
definitions: ['SHARD KEY (`version`)'],
|
|
80
|
+
},
|
|
81
|
+
table: {
|
|
82
|
+
createPrefix(operation) {
|
|
83
|
+
return operation.tableOptions?.rowstore === true ? 'CREATE ROWSTORE TABLE' : 'CREATE TABLE';
|
|
84
|
+
},
|
|
85
|
+
definitions(operation, helpers) {
|
|
86
|
+
const options = operation.tableOptions;
|
|
87
|
+
const definitions = [];
|
|
88
|
+
if (options?.shardKey !== undefined) {
|
|
89
|
+
definitions.push(`SHARD KEY (${helpers.keyColumns(options.shardKey)})`);
|
|
90
|
+
}
|
|
91
|
+
if (options?.sortKey !== undefined) {
|
|
92
|
+
definitions.push(`SORT KEY (${helpers.keyColumns(options.sortKey)})`);
|
|
93
|
+
}
|
|
94
|
+
return definitions;
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
function validateSnapshot(snapshot) {
|
|
99
|
+
for (const table of snapshot.tables)
|
|
100
|
+
validateTable(table);
|
|
101
|
+
}
|
|
102
|
+
function validateOperation(operation) {
|
|
103
|
+
switch (operation.kind) {
|
|
104
|
+
case 'create_table':
|
|
105
|
+
validateTable(tableShape(operation));
|
|
106
|
+
return;
|
|
107
|
+
case 'add_foreign_key':
|
|
108
|
+
case 'drop_foreign_key':
|
|
109
|
+
unsupported('foreign keys', `@zmdb/singlestore refuses foreign-key operation "${operation.kind}" on "${operation.table}" before execution`);
|
|
110
|
+
case 'add_column':
|
|
111
|
+
if (operation.column.unique === true || operation.column.type === 'serial') {
|
|
112
|
+
unsupported(`unique column "${operation.column.name}" without shard-key evidence`, `singlestore cannot add unique column "${operation.table}"."${operation.column.name}" because the ` +
|
|
113
|
+
'operation does not carry the table shard key');
|
|
114
|
+
}
|
|
115
|
+
return;
|
|
116
|
+
case 'alter_primary_key':
|
|
117
|
+
unsupported(`altering the primary key of "${operation.table}"`, `singlestore cannot alter the primary key of "${operation.table}" without proving its shard-key compatibility`);
|
|
118
|
+
default:
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function validateSchemaObject(operation) {
|
|
123
|
+
switch (operation.kind) {
|
|
124
|
+
case 'create_routine':
|
|
125
|
+
case 'drop_routine':
|
|
126
|
+
case 'replace_routine':
|
|
127
|
+
unsupported('stored routine DDL', 'singlestore routine declarations do not share MySQL grammar; use a reviewed hand-written migration');
|
|
128
|
+
case 'create_index':
|
|
129
|
+
if (operation.definition.unique === true) {
|
|
130
|
+
unsupported(`unique index "${operation.definition.name}" without shard-key evidence`, `singlestore cannot emit unique index "${operation.definition.name}" because the operation does not carry ` +
|
|
131
|
+
'the table shard key');
|
|
132
|
+
}
|
|
133
|
+
if (operation.definition.method !== undefined) {
|
|
134
|
+
unsupported(`index method ${operation.definition.method} without table-storage evidence`, `singlestore cannot emit explicit ${operation.definition.method.toUpperCase()} index ` +
|
|
135
|
+
`"${operation.definition.name}" because method support depends on rowstore versus columnstore storage; ` +
|
|
136
|
+
'omit the method or use a reviewed hand-written migration');
|
|
137
|
+
}
|
|
138
|
+
return;
|
|
139
|
+
case 'check_constraint':
|
|
140
|
+
unsupported(`check constraint "${operation.name}"`, `singlestore does not support CHECK constraint "${operation.name}" on "${operation.table}"`);
|
|
141
|
+
default:
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function generatedColumn(operation) {
|
|
146
|
+
const definition = operation.definition;
|
|
147
|
+
return (`${quoteIdentifier(mysql, definition.name)} AS (${definition.expression})` +
|
|
148
|
+
`${definition.stored === true ? ' PERSISTED' : ''} ${definition.type}`);
|
|
149
|
+
}
|
|
150
|
+
export const singlestoreMigrations = Object.freeze({
|
|
151
|
+
name: 'singlestore',
|
|
152
|
+
foreignKeyMode: 'deferred',
|
|
153
|
+
embedded: false,
|
|
154
|
+
validateSnapshot(snapshot) {
|
|
155
|
+
validateSnapshot(snapshot);
|
|
156
|
+
parent.validateSnapshot(snapshot);
|
|
157
|
+
},
|
|
158
|
+
validatePlan(plan) {
|
|
159
|
+
validateSnapshot(plan.before);
|
|
160
|
+
validateSnapshot(plan.after);
|
|
161
|
+
for (const operation of plan.operations)
|
|
162
|
+
validateOperation(operation);
|
|
163
|
+
parent.validatePlan(plan);
|
|
164
|
+
},
|
|
165
|
+
ddlType(column) {
|
|
166
|
+
return parent.ddlType(column);
|
|
167
|
+
},
|
|
168
|
+
emitUp(operation) {
|
|
169
|
+
validateOperation(operation);
|
|
170
|
+
return parent.emitUp(operation);
|
|
171
|
+
},
|
|
172
|
+
emitDown(operation) {
|
|
173
|
+
if (operation.kind === 'add_foreign_key' || operation.kind === 'drop_foreign_key')
|
|
174
|
+
validateOperation(operation);
|
|
175
|
+
return parent.emitDown(operation);
|
|
176
|
+
},
|
|
177
|
+
emitSchemaObject(operation) {
|
|
178
|
+
validateSchemaObject(operation);
|
|
179
|
+
return operation.kind === 'generated_column' ? [generatedColumn(operation)] : parent.emitSchemaObject(operation);
|
|
180
|
+
},
|
|
181
|
+
connection(driver, options) {
|
|
182
|
+
return parent.connection(driver, options);
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
//# sourceMappingURL=migrations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrations.js","sourceRoot":"","sources":["../src/migrations.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zmdb/singlestore",
|
|
3
|
+
"version": "1.0.0-beta.1",
|
|
4
|
+
"description": "SingleStore vertical for zmdb: MySQL-family compilation, storage-aware migrations, catalog introspection, and mysql2 driver binding.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"database",
|
|
7
|
+
"migrations",
|
|
8
|
+
"mysql",
|
|
9
|
+
"orm",
|
|
10
|
+
"singlestore",
|
|
11
|
+
"typescript",
|
|
12
|
+
"zmdb"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://github.com/ambasta/zmdb#readme",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/ambasta/zmdb/issues"
|
|
17
|
+
},
|
|
18
|
+
"license": "GPL-3.0-or-later",
|
|
19
|
+
"author": "zmdb contributors",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/ambasta/zmdb.git",
|
|
23
|
+
"directory": "packages/singlestore"
|
|
24
|
+
},
|
|
25
|
+
"type": "module",
|
|
26
|
+
"sideEffects": false,
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public",
|
|
35
|
+
"tag": "beta"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "node ../../scripts/build-package.mjs",
|
|
39
|
+
"test": "vitest run"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@zmdb/migrations": "1.0.0-beta.1",
|
|
43
|
+
"@zmdb/mysql": "1.0.0-beta.1"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"@zmdb/orm": "1.0.0-beta.1",
|
|
47
|
+
"@zmdb/sql": "1.0.0-beta.1",
|
|
48
|
+
"mysql2": "^3.24.3"
|
|
49
|
+
},
|
|
50
|
+
"peerDependenciesMeta": {
|
|
51
|
+
"mysql2": {
|
|
52
|
+
"optional": true
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=26"
|
|
57
|
+
},
|
|
58
|
+
"main": "./dist/index.js",
|
|
59
|
+
"types": "./dist/index.d.ts"
|
|
60
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mysql,
|
|
3
|
+
mysqlFamilyDriver,
|
|
4
|
+
type MysqlConnection,
|
|
5
|
+
type MysqlDriver,
|
|
6
|
+
type MysqlExecutionResult,
|
|
7
|
+
type MysqlOptions,
|
|
8
|
+
type MysqlParameter,
|
|
9
|
+
type MysqlPool,
|
|
10
|
+
type MysqlQueryable,
|
|
11
|
+
type MysqlQueryResult,
|
|
12
|
+
type MysqlResultHeader,
|
|
13
|
+
} from '@zmdb/mysql';
|
|
14
|
+
import { type DatabaseVertical } from '@zmdb/orm';
|
|
15
|
+
import { extendSqlDialect, type SqlDialect } from '@zmdb/sql';
|
|
16
|
+
|
|
17
|
+
import { singlestoreIntrospector } from './introspect.js';
|
|
18
|
+
import { SINGLESTORE_TYPE_OVERRIDES, singlestoreMigrations } from './migrations.js';
|
|
19
|
+
|
|
20
|
+
export type {
|
|
21
|
+
MysqlConnection,
|
|
22
|
+
MysqlDriver,
|
|
23
|
+
MysqlExecutionResult,
|
|
24
|
+
MysqlOptions,
|
|
25
|
+
MysqlParameter,
|
|
26
|
+
MysqlPool,
|
|
27
|
+
MysqlQueryable,
|
|
28
|
+
MysqlQueryResult,
|
|
29
|
+
MysqlResultHeader,
|
|
30
|
+
};
|
|
31
|
+
export { singlestoreIntrospector, singlestoreMigrations };
|
|
32
|
+
|
|
33
|
+
const outbox = Object.freeze({
|
|
34
|
+
createTable: 'CREATE ROWSTORE TABLE',
|
|
35
|
+
pendingIndex: 'full' as const,
|
|
36
|
+
epochLiteral: "'1970-01-01 00:00:00.000000'",
|
|
37
|
+
createdAtDefault: 'CURRENT_TIMESTAMP(6)',
|
|
38
|
+
boundedTextType: (length: number) => `VARCHAR(${String(length)})`,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
export const singlestore: SqlDialect<'singlestore'> = extendSqlDialect(mysql, {
|
|
42
|
+
name: 'singlestore',
|
|
43
|
+
traits: {
|
|
44
|
+
fts: 'matchPlain',
|
|
45
|
+
types: SINGLESTORE_TYPE_OVERRIDES,
|
|
46
|
+
},
|
|
47
|
+
capabilities: {
|
|
48
|
+
foreignKeys: false,
|
|
49
|
+
},
|
|
50
|
+
migrations: singlestoreMigrations,
|
|
51
|
+
introspector: singlestoreIntrospector,
|
|
52
|
+
outbox,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
export function singlestoreDriver(client: MysqlQueryable, options?: MysqlOptions): MysqlDriver<'singlestore'> {
|
|
56
|
+
return mysqlFamilyDriver(singlestore, client, options);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const singlestoreVertical: DatabaseVertical<'singlestore', MysqlQueryable, MysqlOptions> = Object.freeze({
|
|
60
|
+
dialect: singlestore,
|
|
61
|
+
driver: singlestoreDriver,
|
|
62
|
+
});
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import type { SchemaSnapshot } from '@zmdb/migrations';
|
|
2
|
+
import {
|
|
3
|
+
query,
|
|
4
|
+
sortByName,
|
|
5
|
+
textField,
|
|
6
|
+
type CatalogSchemaSnapshot,
|
|
7
|
+
type CatalogTableSnapshot,
|
|
8
|
+
} from '@zmdb/migrations/introspect/runtime';
|
|
9
|
+
import { mysql, mysqlFamilyIntrospector } from '@zmdb/mysql';
|
|
10
|
+
import {
|
|
11
|
+
quoteIdentifier,
|
|
12
|
+
type CompiledQuery,
|
|
13
|
+
type IntrospectionDriver,
|
|
14
|
+
type Introspector,
|
|
15
|
+
type IntrospectOptions,
|
|
16
|
+
} from '@zmdb/sql';
|
|
17
|
+
|
|
18
|
+
interface StorageRow {
|
|
19
|
+
readonly schema: string;
|
|
20
|
+
readonly table: string;
|
|
21
|
+
readonly storage: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function placeholders(count: number): string {
|
|
25
|
+
return Array.from({ length: count }, () => '?').join(', ');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function schemaFilter(options: IntrospectOptions): {
|
|
29
|
+
readonly sql: string;
|
|
30
|
+
readonly parameters: readonly unknown[];
|
|
31
|
+
} {
|
|
32
|
+
const schemas = options.schemas;
|
|
33
|
+
if (schemas === undefined || schemas.length === 0) return { sql: 'TABLE_SCHEMA = DATABASE()', parameters: [] };
|
|
34
|
+
const distinct = [...new Set(schemas)].toSorted();
|
|
35
|
+
return {
|
|
36
|
+
sql: `TABLE_SCHEMA IN (${placeholders(distinct.length)})`,
|
|
37
|
+
parameters: distinct,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function mysqlCompatibleCatalog(driver: IntrospectionDriver): IntrospectionDriver {
|
|
42
|
+
return {
|
|
43
|
+
execute(compiled: CompiledQuery): Promise<readonly Record<string, unknown>[]> {
|
|
44
|
+
if (!compiled.text.includes('FROM information_schema.STATISTICS')) return driver.execute(compiled);
|
|
45
|
+
const text = compiled.text.replace(
|
|
46
|
+
'COLUMN_NAME, EXPRESSION, INDEX_TYPE',
|
|
47
|
+
'COLUMN_NAME, NULL AS EXPRESSION, INDEX_TYPE',
|
|
48
|
+
);
|
|
49
|
+
return driver.execute({ ...compiled, text });
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function identifierList(clause: string): readonly string[] {
|
|
55
|
+
const identifiers: string[] = [];
|
|
56
|
+
for (const match of clause.matchAll(/`((?:``|[^`])+)`/gu)) {
|
|
57
|
+
const identifier = match[1];
|
|
58
|
+
if (identifier !== undefined) identifiers.push(identifier.replaceAll('``', '`'));
|
|
59
|
+
}
|
|
60
|
+
return identifiers;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function tableOptions(createTable: string, storage: string): TableSnapshotOptions {
|
|
64
|
+
const shard = /\bSHARD\s+KEY(?:\s+`(?:``|[^`])+`)?\s*\(([^)]*)\)/iu.exec(createTable);
|
|
65
|
+
const sort = /\bSORT\s+KEY(?:\s+`(?:``|[^`])+`)?\s*\(([^)]*)\)/iu.exec(createTable);
|
|
66
|
+
const rowstore = storage === 'INMEMORY_ROWSTORE' || storage === 'ROWSTORE';
|
|
67
|
+
if (!rowstore && storage !== 'COLUMNSTORE') {
|
|
68
|
+
throw new TypeError(
|
|
69
|
+
`singlestore information_schema.TABLES returned unknown STORAGE_TYPE ${JSON.stringify(storage)}`,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
...(shard?.[1] === undefined ? {} : { shardKey: identifierList(shard[1]) }),
|
|
74
|
+
...(sort?.[1] === undefined ? {} : { sortKey: identifierList(sort[1]) }),
|
|
75
|
+
...(rowstore ? { rowstore: true as const } : {}),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
type TableSnapshotOptions = NonNullable<CatalogTableSnapshot['tableOptions']>;
|
|
80
|
+
|
|
81
|
+
function showCreateText(row: Readonly<Record<string, unknown>>, index: number): string {
|
|
82
|
+
const direct = Reflect.get(row, 'Create Table');
|
|
83
|
+
if (typeof direct === 'string') return direct;
|
|
84
|
+
return textField(row, 'CREATE_TABLE', 'singlestore SHOW CREATE TABLE', index);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function physicalIndexes(table: CatalogTableSnapshot): CatalogTableSnapshot['indexes'] {
|
|
88
|
+
return table.indexes.filter(index => index.method !== 'shard' && index.method !== 'clustered columnstore');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function computedColumns(table: CatalogTableSnapshot): CatalogTableSnapshot['columns'] {
|
|
92
|
+
return table.columns.map(column =>
|
|
93
|
+
column.generated === undefined
|
|
94
|
+
? column
|
|
95
|
+
: {
|
|
96
|
+
...column,
|
|
97
|
+
generated: {
|
|
98
|
+
...column.generated,
|
|
99
|
+
stored: /\bPERSISTED\b/iu.test(column.catalogType),
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function exactTimestampWarnings(base: CatalogSchemaSnapshot): CatalogSchemaSnapshot['warnings'] {
|
|
106
|
+
const exact = new Set(
|
|
107
|
+
base.tables.flatMap(table =>
|
|
108
|
+
table.columns
|
|
109
|
+
.filter(column => column.catalogType.toLowerCase() === 'datetime(6)')
|
|
110
|
+
.map(column => `${table.name}\0${column.name}`),
|
|
111
|
+
),
|
|
112
|
+
);
|
|
113
|
+
return base.warnings.filter(
|
|
114
|
+
warning =>
|
|
115
|
+
warning.column === undefined ||
|
|
116
|
+
!exact.has(`${warning.table}\0${warning.column}`) ||
|
|
117
|
+
!warning.reason.includes('forward DDL emits DATETIME(3)'),
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export async function singlestoreSnapshot(
|
|
122
|
+
driver: IntrospectionDriver,
|
|
123
|
+
options: IntrospectOptions = {},
|
|
124
|
+
parent: (driver: IntrospectionDriver, options?: IntrospectOptions) => Promise<CatalogSchemaSnapshot>,
|
|
125
|
+
): Promise<CatalogSchemaSnapshot> {
|
|
126
|
+
const base = await parent(mysqlCompatibleCatalog(driver), options);
|
|
127
|
+
const filter = schemaFilter(options);
|
|
128
|
+
const storageRows = await driver.execute(
|
|
129
|
+
query(
|
|
130
|
+
`SELECT TABLE_SCHEMA, TABLE_NAME, STORAGE_TYPE FROM information_schema.TABLES ` +
|
|
131
|
+
`WHERE ${filter.sql} AND TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_SCHEMA, TABLE_NAME`,
|
|
132
|
+
filter.parameters,
|
|
133
|
+
),
|
|
134
|
+
);
|
|
135
|
+
const storage = storageRows.map((row, index): StorageRow => ({
|
|
136
|
+
schema: textField(row, 'TABLE_SCHEMA', 'singlestore information_schema.TABLES', index),
|
|
137
|
+
table: textField(row, 'TABLE_NAME', 'singlestore information_schema.TABLES', index),
|
|
138
|
+
storage: textField(row, 'STORAGE_TYPE', 'singlestore information_schema.TABLES', index),
|
|
139
|
+
}));
|
|
140
|
+
const byTable = new Map(storage.map(row => [row.table, row]));
|
|
141
|
+
const tables: CatalogTableSnapshot[] = [];
|
|
142
|
+
for (const table of base.tables) {
|
|
143
|
+
const metadata = byTable.get(table.name);
|
|
144
|
+
if (metadata === undefined) {
|
|
145
|
+
throw new TypeError(`singlestore catalog has no storage metadata for table "${table.name}"`);
|
|
146
|
+
}
|
|
147
|
+
const rows = await driver.execute(
|
|
148
|
+
query(
|
|
149
|
+
`SHOW CREATE TABLE ${quoteIdentifier(mysql, metadata.schema)}.${quoteIdentifier(mysql, metadata.table)}`,
|
|
150
|
+
[],
|
|
151
|
+
),
|
|
152
|
+
);
|
|
153
|
+
const row = rows[0];
|
|
154
|
+
if (row === undefined) throw new TypeError(`singlestore SHOW CREATE TABLE returned no row for "${table.name}"`);
|
|
155
|
+
tables.push({
|
|
156
|
+
...table,
|
|
157
|
+
columns: computedColumns(table),
|
|
158
|
+
indexes: physicalIndexes(table),
|
|
159
|
+
tableOptions: tableOptions(showCreateText(row, 0), metadata.storage),
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
...base,
|
|
164
|
+
tables: sortByName(tables),
|
|
165
|
+
warnings: exactTimestampWarnings(base),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const family = mysqlFamilyIntrospector('singlestore', {
|
|
170
|
+
snapshot: singlestoreSnapshot,
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
export const singlestoreIntrospector: Introspector<'singlestore'> = Object.freeze({
|
|
174
|
+
...family,
|
|
175
|
+
normalizeForDrift(snapshot: SchemaSnapshot, role: 'live' | 'declared'): SchemaSnapshot {
|
|
176
|
+
return family.normalizeForDrift(snapshot, role);
|
|
177
|
+
},
|
|
178
|
+
});
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import type { ChangeOp, ColumnSnapshot, SchemaSnapshot, TableOptions } from '@zmdb/migrations';
|
|
2
|
+
import { mysql, mysqlFamilyMigrations, type MysqlTableDdlHelpers } from '@zmdb/mysql';
|
|
3
|
+
import {
|
|
4
|
+
UnsupportedFeatureError,
|
|
5
|
+
quoteIdentifier,
|
|
6
|
+
type MigrationConnection,
|
|
7
|
+
type MigrationDialect,
|
|
8
|
+
type MigrationDriver,
|
|
9
|
+
type MigrationPlan,
|
|
10
|
+
type MigrationTableOptions,
|
|
11
|
+
type SchemaObjectOperation,
|
|
12
|
+
} from '@zmdb/sql';
|
|
13
|
+
|
|
14
|
+
export const SINGLESTORE_TYPE_OVERRIDES = Object.freeze({
|
|
15
|
+
serial: 'BIGINT AUTO_INCREMENT',
|
|
16
|
+
timestamp: 'DATETIME(6)',
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
type CreateTableOperation = Extract<ChangeOp, { readonly kind: 'create_table' }>;
|
|
20
|
+
|
|
21
|
+
interface TableShape {
|
|
22
|
+
readonly name: string;
|
|
23
|
+
readonly columns: readonly ColumnSnapshot[];
|
|
24
|
+
readonly primaryKey: readonly string[];
|
|
25
|
+
readonly foreignKeys: readonly unknown[];
|
|
26
|
+
readonly tableOptions?: TableOptions;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function unsupported(feature: string, message: string): never {
|
|
30
|
+
throw new UnsupportedFeatureError(feature, 'singlestore', message);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function columnsExist(
|
|
34
|
+
table: TableShape,
|
|
35
|
+
label: 'shard key' | 'sort key',
|
|
36
|
+
columns: readonly string[] | undefined,
|
|
37
|
+
): void {
|
|
38
|
+
if (columns === undefined) return;
|
|
39
|
+
if (columns.length === 0) throw new TypeError(`${label} on "${table.name}" must name at least one column`);
|
|
40
|
+
if (new Set(columns).size !== columns.length) {
|
|
41
|
+
throw new TypeError(`${label} on "${table.name}" must not repeat a column`);
|
|
42
|
+
}
|
|
43
|
+
const available = new Set(table.columns.map(column => column.name));
|
|
44
|
+
for (const column of columns) {
|
|
45
|
+
if (!available.has(column)) {
|
|
46
|
+
throw new TypeError(`${label} on "${table.name}" names unknown column "${column}"`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function includesShardKey(key: readonly string[], shardKey: readonly string[]): boolean {
|
|
52
|
+
const columns = new Set(key);
|
|
53
|
+
return shardKey.every(column => columns.has(column));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function validateUniqueKeys(table: TableShape, shardKey: readonly string[] | undefined): void {
|
|
57
|
+
if (shardKey !== undefined && table.primaryKey.length > 0 && !includesShardKey(table.primaryKey, shardKey)) {
|
|
58
|
+
unsupported(
|
|
59
|
+
`primary key outside the shard key on "${table.name}"`,
|
|
60
|
+
`singlestore cannot enforce the primary key on "${table.name}" unless it includes the whole shard key ` +
|
|
61
|
+
`(${shardKey.join(', ')}); change the key or storage declaration before execution`,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
for (const column of table.columns) {
|
|
65
|
+
const inlinePrimary = table.primaryKey.length === 1 && table.primaryKey[0] === column.name;
|
|
66
|
+
const standaloneUnique = column.unique === true && column.type !== 'serial' && !inlinePrimary;
|
|
67
|
+
const unkeyedSerial = column.type === 'serial' && !column.primaryKey;
|
|
68
|
+
if (!standaloneUnique && !unkeyedSerial) continue;
|
|
69
|
+
if (shardKey !== undefined && includesShardKey([column.name], shardKey)) continue;
|
|
70
|
+
unsupported(
|
|
71
|
+
`unique column "${column.name}" outside the shard key`,
|
|
72
|
+
`singlestore cannot enforce UNIQUE on "${table.name}"."${column.name}" unless that index includes the ` +
|
|
73
|
+
`whole shard key (${shardKey?.join(', ') ?? 'none declared'}); change the shard key or enforce uniqueness ` +
|
|
74
|
+
'in the application',
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function validateTable(table: TableShape): void {
|
|
80
|
+
if (table.foreignKeys.length > 0) {
|
|
81
|
+
unsupported(
|
|
82
|
+
'foreign keys',
|
|
83
|
+
`@zmdb/singlestore does not qualify foreign-key DDL; remove the ${
|
|
84
|
+
table.foreignKeys.length === 1 ? 'constraint' : 'constraints'
|
|
85
|
+
} from "${table.name}" before execution`,
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
const options = table.tableOptions;
|
|
89
|
+
const shardKey = options?.shardKey;
|
|
90
|
+
const sortKey = options?.sortKey;
|
|
91
|
+
if (shardKey === undefined && options?.rowstore !== true) {
|
|
92
|
+
unsupported(
|
|
93
|
+
'table options',
|
|
94
|
+
`singlestore table "${table.name}" must declare ShardKey<…> or Rowstore; ` +
|
|
95
|
+
'leaving both absent makes storage and distribution an accidental default',
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
if (options?.rowstore === true && sortKey !== undefined) {
|
|
99
|
+
unsupported(
|
|
100
|
+
`sort key on rowstore table "${table.name}"`,
|
|
101
|
+
`singlestore cannot declare SORT KEY on explicit ROWSTORE table "${table.name}"; ` +
|
|
102
|
+
'use an ordinary rowstore index or remove Rowstore to create a columnstore table',
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
columnsExist(table, 'shard key', shardKey);
|
|
106
|
+
columnsExist(table, 'sort key', sortKey);
|
|
107
|
+
validateUniqueKeys(table, shardKey);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function tableShape(operation: CreateTableOperation): TableShape {
|
|
111
|
+
return {
|
|
112
|
+
name: operation.table,
|
|
113
|
+
columns: operation.columns,
|
|
114
|
+
primaryKey: operation.primaryKey,
|
|
115
|
+
foreignKeys: operation.foreignKeys,
|
|
116
|
+
...(operation.tableOptions === undefined ? {} : { tableOptions: operation.tableOptions }),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const parent = mysqlFamilyMigrations('singlestore', {
|
|
121
|
+
types: SINGLESTORE_TYPE_OVERRIDES,
|
|
122
|
+
ledger: {
|
|
123
|
+
createPrefix: 'CREATE ROWSTORE TABLE',
|
|
124
|
+
definitions: ['SHARD KEY (`version`)'],
|
|
125
|
+
},
|
|
126
|
+
table: {
|
|
127
|
+
createPrefix(operation): string {
|
|
128
|
+
return operation.tableOptions?.rowstore === true ? 'CREATE ROWSTORE TABLE' : 'CREATE TABLE';
|
|
129
|
+
},
|
|
130
|
+
definitions(operation, helpers: MysqlTableDdlHelpers): readonly string[] {
|
|
131
|
+
const options = operation.tableOptions;
|
|
132
|
+
const definitions: string[] = [];
|
|
133
|
+
if (options?.shardKey !== undefined) {
|
|
134
|
+
definitions.push(`SHARD KEY (${helpers.keyColumns(options.shardKey)})`);
|
|
135
|
+
}
|
|
136
|
+
if (options?.sortKey !== undefined) {
|
|
137
|
+
definitions.push(`SORT KEY (${helpers.keyColumns(options.sortKey)})`);
|
|
138
|
+
}
|
|
139
|
+
return definitions;
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
function validateSnapshot(snapshot: SchemaSnapshot): void {
|
|
145
|
+
for (const table of snapshot.tables) validateTable(table);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function validateOperation(operation: ChangeOp): void {
|
|
149
|
+
switch (operation.kind) {
|
|
150
|
+
case 'create_table':
|
|
151
|
+
validateTable(tableShape(operation));
|
|
152
|
+
return;
|
|
153
|
+
case 'add_foreign_key':
|
|
154
|
+
case 'drop_foreign_key':
|
|
155
|
+
unsupported(
|
|
156
|
+
'foreign keys',
|
|
157
|
+
`@zmdb/singlestore refuses foreign-key operation "${operation.kind}" on "${operation.table}" before execution`,
|
|
158
|
+
);
|
|
159
|
+
case 'add_column':
|
|
160
|
+
if (operation.column.unique === true || operation.column.type === 'serial') {
|
|
161
|
+
unsupported(
|
|
162
|
+
`unique column "${operation.column.name}" without shard-key evidence`,
|
|
163
|
+
`singlestore cannot add unique column "${operation.table}"."${operation.column.name}" because the ` +
|
|
164
|
+
'operation does not carry the table shard key',
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
return;
|
|
168
|
+
case 'alter_primary_key':
|
|
169
|
+
unsupported(
|
|
170
|
+
`altering the primary key of "${operation.table}"`,
|
|
171
|
+
`singlestore cannot alter the primary key of "${operation.table}" without proving its shard-key compatibility`,
|
|
172
|
+
);
|
|
173
|
+
default:
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function validateSchemaObject(operation: SchemaObjectOperation): void {
|
|
179
|
+
switch (operation.kind) {
|
|
180
|
+
case 'create_routine':
|
|
181
|
+
case 'drop_routine':
|
|
182
|
+
case 'replace_routine':
|
|
183
|
+
unsupported(
|
|
184
|
+
'stored routine DDL',
|
|
185
|
+
'singlestore routine declarations do not share MySQL grammar; use a reviewed hand-written migration',
|
|
186
|
+
);
|
|
187
|
+
case 'create_index':
|
|
188
|
+
if (operation.definition.unique === true) {
|
|
189
|
+
unsupported(
|
|
190
|
+
`unique index "${operation.definition.name}" without shard-key evidence`,
|
|
191
|
+
`singlestore cannot emit unique index "${operation.definition.name}" because the operation does not carry ` +
|
|
192
|
+
'the table shard key',
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
if (operation.definition.method !== undefined) {
|
|
196
|
+
unsupported(
|
|
197
|
+
`index method ${operation.definition.method} without table-storage evidence`,
|
|
198
|
+
`singlestore cannot emit explicit ${operation.definition.method.toUpperCase()} index ` +
|
|
199
|
+
`"${operation.definition.name}" because method support depends on rowstore versus columnstore storage; ` +
|
|
200
|
+
'omit the method or use a reviewed hand-written migration',
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
return;
|
|
204
|
+
case 'check_constraint':
|
|
205
|
+
unsupported(
|
|
206
|
+
`check constraint "${operation.name}"`,
|
|
207
|
+
`singlestore does not support CHECK constraint "${operation.name}" on "${operation.table}"`,
|
|
208
|
+
);
|
|
209
|
+
default:
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function generatedColumn(operation: Extract<SchemaObjectOperation, { readonly kind: 'generated_column' }>): string {
|
|
215
|
+
const definition = operation.definition;
|
|
216
|
+
return (
|
|
217
|
+
`${quoteIdentifier(mysql, definition.name)} AS (${definition.expression})` +
|
|
218
|
+
`${definition.stored === true ? ' PERSISTED' : ''} ${definition.type}`
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export const singlestoreMigrations: MigrationDialect<'singlestore'> = Object.freeze({
|
|
223
|
+
name: 'singlestore',
|
|
224
|
+
foreignKeyMode: 'deferred',
|
|
225
|
+
embedded: false,
|
|
226
|
+
validateSnapshot(snapshot: SchemaSnapshot): void {
|
|
227
|
+
validateSnapshot(snapshot);
|
|
228
|
+
parent.validateSnapshot(snapshot);
|
|
229
|
+
},
|
|
230
|
+
validatePlan(plan: MigrationPlan): void {
|
|
231
|
+
validateSnapshot(plan.before);
|
|
232
|
+
validateSnapshot(plan.after);
|
|
233
|
+
for (const operation of plan.operations) validateOperation(operation);
|
|
234
|
+
parent.validatePlan(plan);
|
|
235
|
+
},
|
|
236
|
+
ddlType(column: ColumnSnapshot): string {
|
|
237
|
+
return parent.ddlType(column);
|
|
238
|
+
},
|
|
239
|
+
emitUp(operation: ChangeOp): string {
|
|
240
|
+
validateOperation(operation);
|
|
241
|
+
return parent.emitUp(operation);
|
|
242
|
+
},
|
|
243
|
+
emitDown(operation: ChangeOp): string {
|
|
244
|
+
if (operation.kind === 'add_foreign_key' || operation.kind === 'drop_foreign_key') validateOperation(operation);
|
|
245
|
+
return parent.emitDown(operation);
|
|
246
|
+
},
|
|
247
|
+
emitSchemaObject(operation: SchemaObjectOperation): readonly string[] {
|
|
248
|
+
validateSchemaObject(operation);
|
|
249
|
+
return operation.kind === 'generated_column' ? [generatedColumn(operation)] : parent.emitSchemaObject(operation);
|
|
250
|
+
},
|
|
251
|
+
connection(
|
|
252
|
+
driver: MigrationDriver<'singlestore'>,
|
|
253
|
+
options?: MigrationTableOptions,
|
|
254
|
+
): MigrationConnection<'singlestore'> {
|
|
255
|
+
return parent.connection(driver, options);
|
|
256
|
+
},
|
|
257
|
+
});
|