uql-orm 0.56.0 → 0.57.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/README.md +1 -1
- package/dist/browser/uql-browser.min.js +2 -2
- package/dist/browser/uql-browser.min.js.map +4 -4
- package/dist/cockroachdb/cockroachDialect.d.ts +5 -2
- package/dist/cockroachdb/cockroachDialect.js +2 -10
- package/dist/d1/d1SqliteDialect.d.ts +1 -0
- package/dist/d1/d1SqliteDialect.js +2 -0
- package/dist/dialect/abstractSqlDialect.d.ts +195 -32
- package/dist/dialect/abstractSqlDialect.js +406 -199
- package/dist/dialect/aliases.d.ts +10 -7
- package/dist/dialect/aliases.js +12 -7
- package/dist/dialect/hydrateColumn.d.ts +8 -2
- package/dist/dialect/hydrateColumn.js +33 -1
- package/dist/dialect/jsonSql.d.ts +13 -5
- package/dist/dialect/jsonSql.js +24 -7
- package/dist/dialect/mysqlLikeSqlDialect.d.ts +30 -2
- package/dist/dialect/mysqlLikeSqlDialect.js +56 -4
- package/dist/dialect/pgLikeSqlDialect.d.ts +20 -20
- package/dist/dialect/pgLikeSqlDialect.js +23 -48
- package/dist/dialect/pgVectorMetrics.d.ts +13 -0
- package/dist/dialect/pgVectorMetrics.js +17 -0
- package/dist/dialect/queryContext.d.ts +3 -7
- package/dist/dialect/queryContext.js +13 -8
- package/dist/dialect/queryJoins.d.ts +8 -4
- package/dist/dialect/queryJoins.js +25 -11
- package/dist/dialect/vectorSqlDialect.d.ts +2 -2
- package/dist/dialect/vectorSqlDialect.js +2 -3
- package/dist/entity/metadata/definition.js +3 -3
- package/dist/maria/mariaDialect.d.ts +13 -6
- package/dist/maria/mariaDialect.js +29 -9
- package/dist/migrate/cli.d.ts +2 -3
- package/dist/migrate/cli.js +2 -2
- package/dist/migrate/ddl/index.d.ts +1 -5
- package/dist/migrate/ddl/index.js +14 -25
- package/dist/migrate/ddl/indexDdl.d.ts +11 -2
- package/dist/migrate/ddl/indexDdl.js +17 -1
- package/dist/migrate/ddl/mssqlIndexDdl.d.ts +10 -0
- package/dist/migrate/ddl/mssqlIndexDdl.js +10 -0
- package/dist/migrate/ddl/mysqlIndexDdl.d.ts +10 -17
- package/dist/migrate/ddl/mysqlIndexDdl.js +16 -27
- package/dist/migrate/ddl/pgIndexDdl.d.ts +18 -8
- package/dist/migrate/ddl/pgIndexDdl.js +29 -12
- package/dist/migrate/migrator.d.ts +2 -2
- package/dist/migrate/schemaGenerator.d.ts +3 -3
- package/dist/migrate/schemaGenerator.js +5 -7
- package/dist/migrate/schemaGeneratorAsync.d.ts +2 -3
- package/dist/mongo/mongoDialect.d.ts +30 -17
- package/dist/mongo/mongoDialect.js +143 -101
- package/dist/mongo/mongodbQuerier.d.ts +10 -17
- package/dist/mongo/mongodbQuerier.js +31 -105
- package/dist/mssql/mssqlDialect.d.ts +16 -0
- package/dist/mssql/mssqlDialect.js +26 -4
- package/dist/mysql/mysqlDialect.d.ts +2 -0
- package/dist/mysql/mysqlDialect.js +4 -0
- package/dist/querier/abstractQuerier.d.ts +20 -36
- package/dist/querier/abstractQuerier.js +35 -129
- package/dist/querier/abstractQuerierPool.d.ts +2 -2
- package/dist/querier/abstractSqlQuerier.d.ts +4 -17
- package/dist/querier/abstractSqlQuerier.js +34 -44
- package/dist/schema/canonicalType.js +4 -4
- package/dist/schema/indexDifferences.js +4 -4
- package/dist/schema/schemaASTBuilder.js +31 -2
- package/dist/schema/schemaASTDiffer.js +5 -5
- package/dist/sqlite/sqliteDialect.d.ts +20 -1
- package/dist/sqlite/sqliteDialect.js +38 -7
- package/dist/turso/tursoDialect.d.ts +2 -0
- package/dist/turso/tursoDialect.js +2 -0
- package/dist/type/config.d.ts +2 -2
- package/dist/type/dialect.d.ts +4 -5
- package/dist/type/entity.d.ts +2 -1
- package/dist/type/migratorDialect.d.ts +4 -0
- package/dist/type/querier.d.ts +6 -6
- package/dist/type/query.d.ts +25 -48
- package/dist/type/query.js +10 -5
- package/dist/type/queryAggregate.d.ts +10 -10
- package/dist/type/queryAggregate.js +1 -1
- package/dist/type/universalQuerier.d.ts +4 -4
- package/dist/util/dialect.util.d.ts +1 -1
- package/dist/util/field.util.d.ts +5 -0
- package/dist/util/field.util.js +19 -0
- package/dist/util/object.util.d.ts +2 -0
- package/dist/util/object.util.js +4 -0
- package/dist/util/relationQuery.util.d.ts +12 -65
- package/dist/util/relationQuery.util.js +27 -81
- package/dist/util/rowKey.util.d.ts +1 -11
- package/dist/util/rowKey.util.js +1 -13
- package/package.json +1 -1
- package/dist/querier/relationCount.d.ts +0 -16
- package/dist/querier/relationCount.js +0 -121
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { EntityMeta, Query, QuerySortMap, RelationMeta, Type } from '../type/index.js';
|
|
2
|
-
import { type RelationQuery } from '../util/index.js';
|
|
1
|
+
import type { EntityMeta, Query, QuerySortMap, RelationMeta, RelationQuery, Type } from '../type/index.js';
|
|
3
2
|
/**
|
|
4
3
|
* One relation a statement joins, keyed by the alias its columns are addressed by (`tax`,
|
|
5
4
|
* `tax.category`). `projected` tells a `$populate` join, whose columns are selected, from one only
|
|
@@ -8,8 +7,10 @@ import { type RelationQuery } from '../util/index.js';
|
|
|
8
7
|
export type QueryJoin = {
|
|
9
8
|
/** The relation key on its parent, which is how MongoDB names the field a `$lookup` adds. */
|
|
10
9
|
readonly key: string;
|
|
11
|
-
/** Dotted path from the queried entity, which is
|
|
10
|
+
/** Dotted path from the queried entity, which is what a joined row's columns answer under. */
|
|
12
11
|
readonly path: string;
|
|
12
|
+
/** The alias the statement reads it through: its path, unless another table of the statement took it. */
|
|
13
|
+
readonly alias: string;
|
|
13
14
|
readonly entity: Type<object>;
|
|
14
15
|
readonly meta: EntityMeta<object>;
|
|
15
16
|
readonly relation: RelationMeta;
|
|
@@ -38,14 +39,17 @@ export type QuerySortOptions = {
|
|
|
38
39
|
* related column needs that relation joined just as much as selecting it does. The two sources meet
|
|
39
40
|
* here, so the columns, the `ORDER BY` and the row lock cannot disagree about what is in the
|
|
40
41
|
* statement. `$sort` contributes to-one relations only; the rest is rejected where it is rendered.
|
|
42
|
+
* `claimAlias` names each join's table, parents first.
|
|
41
43
|
*/
|
|
42
|
-
export declare function resolveQueryJoins<E>(meta: EntityMeta<E>, q: Query<E
|
|
44
|
+
export declare function resolveQueryJoins<E>(meta: EntityMeta<E>, q: Query<E>, claimAlias?: (path: string) => string): QueryJoins;
|
|
43
45
|
/**
|
|
44
46
|
* Whether a join drops parents that have no match, which is the one thing a join does to *how many*
|
|
45
47
|
* rows a read returns rather than how wide they are. A count that skips the joins has to be told, or
|
|
46
48
|
* it counts the parents the read will never hand back.
|
|
47
49
|
*/
|
|
48
50
|
export declare function hasRequiredJoin<E>(meta: EntityMeta<E>, q: Query<E>): boolean;
|
|
51
|
+
/** Whether a statement aggregates a relation's rows: a to-many off its own row, or off a row it joins. */
|
|
52
|
+
export declare function aggregatesRelations<E>(meta: EntityMeta<E>, q: Query<E>): boolean;
|
|
49
53
|
/**
|
|
50
54
|
* The join an ordering may address at `path`, with the relation's own sort map, or why it may not.
|
|
51
55
|
* Every backend answers this the same way - a to-many has no single value to order by, a relation
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import { getMeta, relationOf } from '../entity/index.js';
|
|
2
|
-
import { getKeys, getRelationRequestSummary, isToManyRelation, parseRelationAtKey
|
|
2
|
+
import { getKeys, getRelationRequestSummary, isToManyRelation, parseRelationAtKey } from '../util/index.js';
|
|
3
3
|
export const NO_JOINS = new Map();
|
|
4
4
|
/**
|
|
5
5
|
* What the statement joins, from the whole query rather than from `$populate` alone: ordering by a
|
|
6
6
|
* related column needs that relation joined just as much as selecting it does. The two sources meet
|
|
7
7
|
* here, so the columns, the `ORDER BY` and the row lock cannot disagree about what is in the
|
|
8
8
|
* statement. `$sort` contributes to-one relations only; the rest is rejected where it is rendered.
|
|
9
|
+
* `claimAlias` names each join's table, parents first.
|
|
9
10
|
*/
|
|
10
|
-
export function resolveQueryJoins(meta, q) {
|
|
11
|
+
export function resolveQueryJoins(meta, q, claimAlias = (path) => path) {
|
|
11
12
|
if (!q.$populate && !q.$sort) {
|
|
12
13
|
return NO_JOINS;
|
|
13
14
|
}
|
|
14
15
|
const joins = new Map();
|
|
15
|
-
addPopulateJoins(joins, meta, q.$populate);
|
|
16
|
-
addSortJoins(joins, meta, q.$sort);
|
|
16
|
+
addPopulateJoins(joins, claimAlias, meta, q.$populate);
|
|
17
|
+
addSortJoins(joins, claimAlias, meta, q.$sort);
|
|
17
18
|
return joins;
|
|
18
19
|
}
|
|
19
20
|
/**
|
|
@@ -29,7 +30,19 @@ export function hasRequiredJoin(meta, q) {
|
|
|
29
30
|
}
|
|
30
31
|
return false;
|
|
31
32
|
}
|
|
32
|
-
|
|
33
|
+
/** Whether a statement aggregates a relation's rows: a to-many off its own row, or off a row it joins. */
|
|
34
|
+
export function aggregatesRelations(meta, q) {
|
|
35
|
+
if (getRelationRequestSummary(meta, q.$populate).toManyKeys.length) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
for (const join of resolveQueryJoins(meta, q).values()) {
|
|
39
|
+
if (getRelationRequestSummary(join.meta, join.query.$populate).toManyKeys.length) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
function addJoin(joins, claimAlias, parent, key, relation, query, required, projected) {
|
|
33
46
|
const path = parent ? `${parent.path}.${key}` : key;
|
|
34
47
|
const existing = joins.get(path);
|
|
35
48
|
// `$populate` runs first, so an already-joined relation keeps its columns and its `$required`
|
|
@@ -41,6 +54,7 @@ function addJoin(joins, parent, key, relation, query, required, projected) {
|
|
|
41
54
|
const join = {
|
|
42
55
|
key,
|
|
43
56
|
path,
|
|
57
|
+
alias: claimAlias(path),
|
|
44
58
|
entity,
|
|
45
59
|
meta: getMeta(entity),
|
|
46
60
|
relation,
|
|
@@ -52,15 +66,15 @@ function addJoin(joins, parent, key, relation, query, required, projected) {
|
|
|
52
66
|
joins.set(path, join);
|
|
53
67
|
return join;
|
|
54
68
|
}
|
|
55
|
-
function addPopulateJoins(joins, meta, populate, parent) {
|
|
69
|
+
function addPopulateJoins(joins, claimAlias, meta, populate, parent) {
|
|
56
70
|
for (const key of getRelationRequestSummary(meta, populate).joinableKeys) {
|
|
57
71
|
const relation = relationOf(meta, key);
|
|
58
72
|
const { query, required } = parseRelationAtKey(key, populate);
|
|
59
|
-
const join = addJoin(joins, parent, key, relation, query, required, true);
|
|
60
|
-
addPopulateJoins(joins, join.meta, query.$populate, join);
|
|
73
|
+
const join = addJoin(joins, claimAlias, parent, key, relation, query, required, true);
|
|
74
|
+
addPopulateJoins(joins, claimAlias, join.meta, query.$populate, join);
|
|
61
75
|
}
|
|
62
76
|
}
|
|
63
|
-
function addSortJoins(joins, meta, sort, parent) {
|
|
77
|
+
function addSortJoins(joins, claimAlias, meta, sort, parent) {
|
|
64
78
|
if (!sort) {
|
|
65
79
|
return;
|
|
66
80
|
}
|
|
@@ -72,8 +86,8 @@ function addSortJoins(joins, meta, sort, parent) {
|
|
|
72
86
|
if (!relation || isToManyRelation(relation) || !isSortMap(value)) {
|
|
73
87
|
continue;
|
|
74
88
|
}
|
|
75
|
-
const join = addJoin(joins, parent, key, relation, {}, false, false);
|
|
76
|
-
addSortJoins(joins, join.meta, value, join);
|
|
89
|
+
const join = addJoin(joins, claimAlias, parent, key, relation, {}, false, false);
|
|
90
|
+
addSortJoins(joins, claimAlias, join.meta, value, join);
|
|
77
91
|
}
|
|
78
92
|
}
|
|
79
93
|
/**
|
|
@@ -78,8 +78,8 @@ export declare abstract class VectorSqlDialect extends AbstractDialect {
|
|
|
78
78
|
*/
|
|
79
79
|
supportedVectorType(cast: VectorCast): VectorCast;
|
|
80
80
|
/**
|
|
81
|
-
*
|
|
82
|
-
*
|
|
81
|
+
* The distance a vector `$sort` projects, which the projection names after `$project`. Delegates to
|
|
82
|
+
* `appendVectorSort` so each dialect's distance syntax is written once.
|
|
83
83
|
*/
|
|
84
84
|
protected appendVectorProjection<E>(ctx: QueryContext, meta: EntityMeta<E>, key: string, search: QueryVectorSearch): void;
|
|
85
85
|
/**
|
|
@@ -96,8 +96,8 @@ export class VectorSqlDialect extends AbstractDialect {
|
|
|
96
96
|
return this.hasNarrowVectorTypes ? cast : 'vector';
|
|
97
97
|
}
|
|
98
98
|
/**
|
|
99
|
-
*
|
|
100
|
-
*
|
|
99
|
+
* The distance a vector `$sort` projects, which the projection names after `$project`. Delegates to
|
|
100
|
+
* `appendVectorSort` so each dialect's distance syntax is written once.
|
|
101
101
|
*/
|
|
102
102
|
appendVectorProjection(ctx, meta, key, search) {
|
|
103
103
|
const alias = search.$project;
|
|
@@ -108,7 +108,6 @@ export class VectorSqlDialect extends AbstractDialect {
|
|
|
108
108
|
throw new TypeError(`$project '${alias}' collides with a field of '${entityName(meta)}'`);
|
|
109
109
|
}
|
|
110
110
|
this.appendVectorSort(ctx, meta, key, search);
|
|
111
|
-
ctx.append(` AS ${this.escapeId(alias)}`);
|
|
112
111
|
}
|
|
113
112
|
/**
|
|
114
113
|
* The distance expression, in whichever of the two shapes this dialect spells it. One method for
|
|
@@ -337,9 +337,9 @@ function fillRelations(meta) {
|
|
|
337
337
|
function fillOwningSide(at, meta, relKey, relOpts) {
|
|
338
338
|
const relMeta = ensureMeta(relOpts.entity());
|
|
339
339
|
if (relOpts.through) {
|
|
340
|
-
// Both columns live on the junction, whatever the cardinality: `
|
|
341
|
-
//
|
|
342
|
-
//
|
|
340
|
+
// Both columns live on the junction, whatever the cardinality: `deleteRelations` and every dialect
|
|
341
|
+
// read them as junction columns. A composite key contributes one pair per column of it, which is
|
|
342
|
+
// what makes the join address a whole key rather than part.
|
|
343
343
|
relOpts.references = [
|
|
344
344
|
...meta.ids.map((key) => ({ local: junctionColumn(meta, key), foreign: key })),
|
|
345
345
|
...relMeta.ids.map((key) => ({ local: junctionColumn(relMeta, key), foreign: key })),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { type RelationRows } from '../dialect/abstractSqlDialect.js';
|
|
1
2
|
import { MysqlLikeSqlDialect } from '../dialect/mysqlLikeSqlDialect.js';
|
|
2
|
-
import type { DialectFeatures, EntityMeta, FieldOptions, Query, QueryContext,
|
|
3
|
+
import type { DialectFeatures, EntityMeta, FieldOptions, Query, QueryContext, Type, VectorDistance, VectorMetric } from '../type/index.js';
|
|
3
4
|
export declare class MariaDialect extends MysqlLikeSqlDialect {
|
|
4
5
|
readonly dialectName = "mariadb";
|
|
5
6
|
readonly insertIdSource = "returning";
|
|
@@ -10,6 +11,11 @@ export declare class MariaDialect extends MysqlLikeSqlDialect {
|
|
|
10
11
|
* and `CREATE INDEX` takes `IF NOT EXISTS` - which MySQL's grammar has no place for.
|
|
11
12
|
*/
|
|
12
13
|
protected readonly featureOverrides: Partial<DialectFeatures>;
|
|
14
|
+
/**
|
|
15
|
+
* A derived table here reads no column of the statement around it, so the aggregate reads the
|
|
16
|
+
* related table itself, and orders and pages inside `JSON_ARRAYAGG`, which takes both.
|
|
17
|
+
*/
|
|
18
|
+
protected appendRelationArray(ctx: QueryContext, { entity, query, alias, joins }: RelationRows): void;
|
|
13
19
|
protected upsertReturning<E>(meta: EntityMeta<E>): string;
|
|
14
20
|
/**
|
|
15
21
|
* MariaDB supports neither MySQL's `->`/`->>` shorthand nor the base's chained form. `JSON_VALUE`
|
|
@@ -36,12 +42,13 @@ export declare class MariaDialect extends MysqlLikeSqlDialect {
|
|
|
36
42
|
*/
|
|
37
43
|
protected appendVectorValue(ctx: QueryContext, value: readonly unknown[]): void;
|
|
38
44
|
/**
|
|
39
|
-
* `
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* from `vectorTuningStatements`, which is Postgres's `SET LOCAL` shape.
|
|
45
|
+
* `mhnsw_ef_search` too, where a vector search is tuned. A setting scoped to one statement needs
|
|
46
|
+
* neither a transaction nor a restore, and cannot leak to the next query on this pooled connection,
|
|
47
|
+
* which is why the tuning is not `vectorTuningStatements`, Postgres's `SET LOCAL` shape.
|
|
43
48
|
*/
|
|
44
|
-
|
|
49
|
+
protected statementSettings<E>(entity: Type<E>, q: Query<E>): string[];
|
|
50
|
+
/** `SET STATEMENT ... FOR`, which scopes a variable to the statement it prefixes. */
|
|
51
|
+
protected applySettings(sql: string, settings: readonly string[]): string;
|
|
45
52
|
/** The reverse: selecting a `VECTOR` column raw yields that blob, so it is read back as text. */
|
|
46
53
|
protected selectFieldExpr(escapedColumn: string, field: FieldOptions): string;
|
|
47
54
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { relationTermKey } from '../dialect/abstractSqlDialect.js';
|
|
1
2
|
import { jsonPath } from '../dialect/jsonSql.js';
|
|
2
3
|
import { MysqlLikeSqlDialect } from '../dialect/mysqlLikeSqlDialect.js';
|
|
3
4
|
import { getMeta } from '../entity/index.js';
|
|
@@ -18,6 +19,24 @@ export class MariaDialect extends MysqlLikeSqlDialect {
|
|
|
18
19
|
vectorIndexRequiresNotNull: true,
|
|
19
20
|
indexIfNotExists: true,
|
|
20
21
|
};
|
|
22
|
+
/**
|
|
23
|
+
* A derived table here reads no column of the statement around it, so the aggregate reads the
|
|
24
|
+
* related table itself, and orders and pages inside `JSON_ARRAYAGG`, which takes both.
|
|
25
|
+
*/
|
|
26
|
+
appendRelationArray(ctx, { entity, query, alias, joins }) {
|
|
27
|
+
const meta = getMeta(entity);
|
|
28
|
+
const terms = this.projection(ctx, entity, query, { prefix: alias, json: true }, joins);
|
|
29
|
+
const sortOpts = { prefix: alias, joins, distinct: query.$distinct };
|
|
30
|
+
const order = this.buildFragment(ctx, (fragmentCtx) => this.sort(fragmentCtx, entity, query.$sort, sortOpts));
|
|
31
|
+
const page = this.buildFragment(ctx, (fragmentCtx) => this.pager(fragmentCtx, query));
|
|
32
|
+
const from = this.buildFragment(ctx, (fragmentCtx) => {
|
|
33
|
+
this.selectRelationJoins(fragmentCtx, meta, alias, joins);
|
|
34
|
+
this.where(fragmentCtx, entity, query.$where, { prefix: alias });
|
|
35
|
+
});
|
|
36
|
+
const object = this.jsonObject(terms.map((term) => [relationTermKey(term), term.sql]));
|
|
37
|
+
const rows = `${query.$distinct ? 'DISTINCT ' : ''}${object}${order}${page}`;
|
|
38
|
+
ctx.append(`COALESCE((SELECT JSON_ARRAYAGG(${rows}) FROM ${this.tableRef(meta, alias).ref}${from}), JSON_ARRAY())`);
|
|
39
|
+
}
|
|
21
40
|
upsertReturning(meta) {
|
|
22
41
|
const returning = this.returningId(meta);
|
|
23
42
|
return returning ? ` ${returning}` : '';
|
|
@@ -61,18 +80,19 @@ export class MariaDialect extends MysqlLikeSqlDialect {
|
|
|
61
80
|
ctx.append(')');
|
|
62
81
|
}
|
|
63
82
|
/**
|
|
64
|
-
* `
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* from `vectorTuningStatements`, which is Postgres's `SET LOCAL` shape.
|
|
83
|
+
* `mhnsw_ef_search` too, where a vector search is tuned. A setting scoped to one statement needs
|
|
84
|
+
* neither a transaction nor a restore, and cannot leak to the next query on this pooled connection,
|
|
85
|
+
* which is why the tuning is not `vectorTuningStatements`, Postgres's `SET LOCAL` shape.
|
|
68
86
|
*/
|
|
69
|
-
|
|
87
|
+
statementSettings(entity, q) {
|
|
70
88
|
// `$candidates` first: `getMeta` would otherwise be resolved on every read, to discover that
|
|
71
89
|
// almost none of them tune anything.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
90
|
+
const tuned = q.$candidates !== undefined && this.tunedVectorIndex(getMeta(entity), q);
|
|
91
|
+
return [...(tuned ? [`mhnsw_ef_search=${q.$candidates}`] : []), ...super.statementSettings(entity, q)];
|
|
92
|
+
}
|
|
93
|
+
/** `SET STATEMENT ... FOR`, which scopes a variable to the statement it prefixes. */
|
|
94
|
+
applySettings(sql, settings) {
|
|
95
|
+
return `SET STATEMENT ${settings.join(', ')} FOR ${sql}`;
|
|
76
96
|
}
|
|
77
97
|
/** The reverse: selecting a `VECTOR` column raw yields that blob, so it is read back as text. */
|
|
78
98
|
selectFieldExpr(escapedColumn, field) {
|
package/dist/migrate/cli.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import type { AbstractDialect } from '../dialect/index.js';
|
|
3
2
|
import type { ForeignKeyAction } from '../schema/types.js';
|
|
4
|
-
import type { Config } from '../type/index.js';
|
|
3
|
+
import type { Config, MigratorDialect } from '../type/index.js';
|
|
5
4
|
import { Migrator } from './migrator.js';
|
|
6
5
|
import { createSchemaGeneratorAsync } from './schemaGeneratorAsync.js';
|
|
7
6
|
/** Sync helper for SQL dialects only; returns `undefined` for MongoDB - use {@link createSchemaGeneratorAsync}. */
|
|
8
|
-
export declare function getSchemaGenerator(dialect:
|
|
7
|
+
export declare function getSchemaGenerator(dialect: MigratorDialect, defaultForeignKeyAction?: ForeignKeyAction): import("./schemaGenerator.js").SqlSchemaGenerator | undefined;
|
|
9
8
|
export { createSchemaGeneratorAsync };
|
|
10
9
|
export declare function main(args?: string[]): Promise<void>;
|
|
11
10
|
export declare function runUp(migrator: Migrator, args: string[]): Promise<void>;
|
package/dist/migrate/cli.js
CHANGED
|
@@ -308,7 +308,7 @@ function printDriftGroup(title, drifts, icon, showSuggestion) {
|
|
|
308
308
|
console.log(` Expected: ${drift.expected}, Actual: ${drift.actual}`);
|
|
309
309
|
}
|
|
310
310
|
if (showSuggestion) {
|
|
311
|
-
console.log(`
|
|
311
|
+
console.log(` -> ${drift.suggestion}`);
|
|
312
312
|
}
|
|
313
313
|
}
|
|
314
314
|
console.log('');
|
|
@@ -358,7 +358,7 @@ Configuration:
|
|
|
358
358
|
Create a uql.config.ts or uql.config.js file in your project root.
|
|
359
359
|
You can also specify a custom config path using --config or -c.
|
|
360
360
|
The CLI requires pool.dialect (dialect id = pool.dialect.dialectName).
|
|
361
|
-
See the repo README section "Driver
|
|
361
|
+
See the repo README section "Driver -> pool -> dialect class".
|
|
362
362
|
|
|
363
363
|
export default {
|
|
364
364
|
pool: new PgQuerierPool({ ... }),
|
|
@@ -2,15 +2,11 @@ import type { AbstractSqlDialect } from '../../dialect/abstractSqlDialect.js';
|
|
|
2
2
|
import { IndexDdl } from './indexDdl.js';
|
|
3
3
|
import { TableDdl } from './tableDdl.js';
|
|
4
4
|
export { IndexDdl } from './indexDdl.js';
|
|
5
|
+
export { MsSqlIndexDdl } from './mssqlIndexDdl.js';
|
|
5
6
|
export { MsSqlTableDdl } from './mssqlTableDdl.js';
|
|
6
7
|
export { MariaIndexDdl, MySqlIndexDdl, MysqlLikeIndexDdl } from './mysqlIndexDdl.js';
|
|
7
8
|
export { CockroachIndexDdl, PgIndexDdl } from './pgIndexDdl.js';
|
|
8
9
|
export { TableDdl } from './tableDdl.js';
|
|
9
|
-
/**
|
|
10
|
-
* The index DDL a dialect gets, most specific first. `instanceof` rather than the `dialectName`
|
|
11
|
-
* {@link tableDdlFor} reads, because each family's index DDL is typed to its dialect and the narrowing
|
|
12
|
-
* is what hands it one. Anything else gets the portable form, which is SQLite's.
|
|
13
|
-
*/
|
|
14
10
|
export declare function indexDdlFor(dialect: AbstractSqlDialect): IndexDdl;
|
|
15
11
|
/**
|
|
16
12
|
* The table DDL a dialect gets: SQL Server's, or the portable form every other engine takes. By
|
|
@@ -1,40 +1,29 @@
|
|
|
1
|
-
import { CockroachDialect } from '../../cockroachdb/cockroachDialect.js';
|
|
2
|
-
import { MysqlLikeSqlDialect } from '../../dialect/mysqlLikeSqlDialect.js';
|
|
3
|
-
import { PgLikeSqlDialect } from '../../dialect/pgLikeSqlDialect.js';
|
|
4
|
-
import { MariaDialect } from '../../maria/mariaDialect.js';
|
|
5
|
-
import { MySqlDialect } from '../../mysql/mysqlDialect.js';
|
|
6
1
|
import { IndexDdl } from './indexDdl.js';
|
|
2
|
+
import { MsSqlIndexDdl } from './mssqlIndexDdl.js';
|
|
7
3
|
import { MsSqlTableDdl } from './mssqlTableDdl.js';
|
|
8
|
-
import { MariaIndexDdl, MySqlIndexDdl
|
|
4
|
+
import { MariaIndexDdl, MySqlIndexDdl } from './mysqlIndexDdl.js';
|
|
9
5
|
import { CockroachIndexDdl, PgIndexDdl } from './pgIndexDdl.js';
|
|
10
6
|
import { TableDdl } from './tableDdl.js';
|
|
11
7
|
export { IndexDdl } from './indexDdl.js';
|
|
8
|
+
export { MsSqlIndexDdl } from './mssqlIndexDdl.js';
|
|
12
9
|
export { MsSqlTableDdl } from './mssqlTableDdl.js';
|
|
13
10
|
export { MariaIndexDdl, MySqlIndexDdl, MysqlLikeIndexDdl } from './mysqlIndexDdl.js';
|
|
14
11
|
export { CockroachIndexDdl, PgIndexDdl } from './pgIndexDdl.js';
|
|
15
12
|
export { TableDdl } from './tableDdl.js';
|
|
16
13
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* is what hands it one. Anything else gets the portable form, which is SQLite's.
|
|
14
|
+
* Each engine's index DDL, by the `dialectName` a subclass inherits: by name, so this entry carries no
|
|
15
|
+
* dialect, and exhaustive, so a new engine has to name its own. SQLite's is the portable form.
|
|
20
16
|
*/
|
|
17
|
+
const INDEX_DDL = {
|
|
18
|
+
postgres: PgIndexDdl,
|
|
19
|
+
cockroachdb: CockroachIndexDdl,
|
|
20
|
+
mysql: MySqlIndexDdl,
|
|
21
|
+
mariadb: MariaIndexDdl,
|
|
22
|
+
mssql: MsSqlIndexDdl,
|
|
23
|
+
sqlite: IndexDdl,
|
|
24
|
+
};
|
|
21
25
|
export function indexDdlFor(dialect) {
|
|
22
|
-
|
|
23
|
-
return new CockroachIndexDdl(dialect);
|
|
24
|
-
}
|
|
25
|
-
if (dialect instanceof PgLikeSqlDialect) {
|
|
26
|
-
return new PgIndexDdl(dialect);
|
|
27
|
-
}
|
|
28
|
-
if (dialect instanceof MySqlDialect) {
|
|
29
|
-
return new MySqlIndexDdl(dialect);
|
|
30
|
-
}
|
|
31
|
-
if (dialect instanceof MariaDialect) {
|
|
32
|
-
return new MariaIndexDdl(dialect);
|
|
33
|
-
}
|
|
34
|
-
if (dialect instanceof MysqlLikeSqlDialect) {
|
|
35
|
-
return new MysqlLikeIndexDdl(dialect);
|
|
36
|
-
}
|
|
37
|
-
return new IndexDdl(dialect);
|
|
26
|
+
return new INDEX_DDL[dialect.dialectName](dialect);
|
|
38
27
|
}
|
|
39
28
|
/**
|
|
40
29
|
* The table DDL a dialect gets: SQL Server's, or the portable form every other engine takes. By
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AbstractSqlDialect } from '../../dialect/abstractSqlDialect.js';
|
|
2
|
-
import type
|
|
2
|
+
import { type IndexType } from '../../schema/types.js';
|
|
3
3
|
import { type IndexColumnSchema, type IndexFeature, type IndexJsonArray, type IndexSchema } from '../../type/index.js';
|
|
4
4
|
/**
|
|
5
5
|
* `CREATE INDEX` for SQL dialects: the statement and the fragments each engine spells differently.
|
|
@@ -20,6 +20,15 @@ export declare class IndexDdl<D extends AbstractSqlDialect = AbstractSqlDialect>
|
|
|
20
20
|
* emitted: each of them is a hard error at the server, not a slower plan.
|
|
21
21
|
*/
|
|
22
22
|
protected readonly indexFeatures: ReadonlySet<IndexFeature>;
|
|
23
|
+
/**
|
|
24
|
+
* Index types this dialect's `CREATE INDEX` takes. SQLite's grammar has no `USING` clause, so every
|
|
25
|
+
* type builds the plain index it has there, which is what lets an entity written for Postgres
|
|
26
|
+
* migrate unchanged. An engine that would reject a type narrows this, and the type is refused.
|
|
27
|
+
*/
|
|
28
|
+
protected readonly indexTypes: ReadonlySet<IndexType>;
|
|
29
|
+
/** What to declare instead of a type this dialect lacks, appended to its refusal. */
|
|
30
|
+
protected readonly indexTypeHints: ReadonlyMap<IndexType, string>;
|
|
31
|
+
private assertIndexType;
|
|
23
32
|
private assertIndexFeatures;
|
|
24
33
|
/**
|
|
25
34
|
* Index types this dialect spells as a keyword of their own (`FULLTEXT INDEX`, `VECTOR INDEX`)
|
|
@@ -51,7 +60,7 @@ export declare class IndexDdl<D extends AbstractSqlDialect = AbstractSqlDialect>
|
|
|
51
60
|
protected indexInclude(_index: IndexSchema): string;
|
|
52
61
|
/** ` USING <method>`, which SQLite's grammar has no place for at all. */
|
|
53
62
|
protected indexAccessMethod(_index: IndexSchema): string;
|
|
54
|
-
/** pgvector's ` WITH (m =
|
|
63
|
+
/** What trails the columns: pgvector's ` WITH (m = ...)`, MySQL's ` USING btree`, MariaDB's ` M=8`. */
|
|
55
64
|
protected indexTuning(_index: IndexSchema): string;
|
|
56
65
|
/**
|
|
57
66
|
* The partial-index predicate. Engines without one reject the index in {@link assertIndexFeatures}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsonTypeMode } from '../../dialect/jsonSql.js';
|
|
2
|
+
import { INDEX_TYPES } from '../../schema/types.js';
|
|
2
3
|
import { INDEX_FEATURE_LABELS, } from '../../type/index.js';
|
|
3
4
|
import { getKeys } from '../../util/index.js';
|
|
4
5
|
/**
|
|
@@ -29,6 +30,7 @@ export class IndexDdl {
|
|
|
29
30
|
this.dialect = dialect;
|
|
30
31
|
}
|
|
31
32
|
getCreateIndexStatement(tableName, index, opts = {}) {
|
|
33
|
+
this.assertIndexType(index);
|
|
32
34
|
this.assertIndexFeatures(index);
|
|
33
35
|
const unique = index.unique ? 'UNIQUE ' : '';
|
|
34
36
|
const ifNotExists = (opts.ifNotExists ?? this.dialect.features.indexIfNotExists) ? 'IF NOT EXISTS ' : '';
|
|
@@ -47,6 +49,20 @@ export class IndexDdl {
|
|
|
47
49
|
'partial',
|
|
48
50
|
'jsonPath',
|
|
49
51
|
]);
|
|
52
|
+
/**
|
|
53
|
+
* Index types this dialect's `CREATE INDEX` takes. SQLite's grammar has no `USING` clause, so every
|
|
54
|
+
* type builds the plain index it has there, which is what lets an entity written for Postgres
|
|
55
|
+
* migrate unchanged. An engine that would reject a type narrows this, and the type is refused.
|
|
56
|
+
*/
|
|
57
|
+
indexTypes = new Set(INDEX_TYPES);
|
|
58
|
+
/** What to declare instead of a type this dialect lacks, appended to its refusal. */
|
|
59
|
+
indexTypeHints = new Map();
|
|
60
|
+
assertIndexType(index) {
|
|
61
|
+
if (index.type && !this.indexTypes.has(index.type)) {
|
|
62
|
+
throw new TypeError(`${this.dialect.dialectName} has no ${index.type} index (index "${index.name}")` +
|
|
63
|
+
(this.indexTypeHints.get(index.type) ?? ''));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
50
66
|
assertIndexFeatures(index) {
|
|
51
67
|
for (const feature of getKeys(INDEX_FEATURE_PROBES)) {
|
|
52
68
|
if (INDEX_FEATURE_PROBES[feature](index) && !this.indexFeatures.has(feature)) {
|
|
@@ -111,7 +127,7 @@ export class IndexDdl {
|
|
|
111
127
|
indexAccessMethod(_index) {
|
|
112
128
|
return '';
|
|
113
129
|
}
|
|
114
|
-
/** pgvector's ` WITH (m =
|
|
130
|
+
/** What trails the columns: pgvector's ` WITH (m = ...)`, MySQL's ` USING btree`, MariaDB's ` M=8`. */
|
|
115
131
|
indexTuning(_index) {
|
|
116
132
|
return '';
|
|
117
133
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { IndexDdl } from './indexDdl.js';
|
|
2
|
+
/**
|
|
3
|
+
* SQL Server's `CREATE INDEX` is the portable form minus what 2025 rejects: an expression (Msg 16216),
|
|
4
|
+
* the subquery a JSON path compiles to (Msg 1046), and any type but the plain rowstore B-tree, since
|
|
5
|
+
* the index built in its place fails on a `VECTOR` or `nvarchar(max)` column (Msg 1978).
|
|
6
|
+
*/
|
|
7
|
+
export declare class MsSqlIndexDdl extends IndexDdl {
|
|
8
|
+
protected readonly indexFeatures: Set<"expression" | "include" | "jsonArray" | "jsonPath" | "nullsOrder" | "opsClass" | "partial" | "prefixLength">;
|
|
9
|
+
protected readonly indexTypes: Set<"brin" | "btree" | "fulltext" | "gin" | "gist" | "hash" | "hnsw" | "ivfflat" | "vector" | "vectorSearch">;
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { IndexDdl } from './indexDdl.js';
|
|
2
|
+
/**
|
|
3
|
+
* SQL Server's `CREATE INDEX` is the portable form minus what 2025 rejects: an expression (Msg 16216),
|
|
4
|
+
* the subquery a JSON path compiles to (Msg 1046), and any type but the plain rowstore B-tree, since
|
|
5
|
+
* the index built in its place fails on a `VECTOR` or `nvarchar(max)` column (Msg 1978).
|
|
6
|
+
*/
|
|
7
|
+
export class MsSqlIndexDdl extends IndexDdl {
|
|
8
|
+
indexFeatures = new Set(['partial']);
|
|
9
|
+
indexTypes = new Set(['btree']);
|
|
10
|
+
}
|
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
import type { IndexType } from '../../schema/types.js';
|
|
2
2
|
import type { IndexJsonArray, IndexSchema } from '../../type/index.js';
|
|
3
3
|
import { IndexDdl } from './indexDdl.js';
|
|
4
|
-
/** `CREATE INDEX ... USING btree`, plus the types this family spells as a keyword instead. */
|
|
4
|
+
/** `CREATE INDEX ... (cols) USING btree`, plus the types this family spells as a keyword instead. */
|
|
5
5
|
export declare class MysqlLikeIndexDdl extends IndexDdl {
|
|
6
6
|
protected readonly indexFeatures: Set<"expression" | "include" | "jsonArray" | "jsonPath" | "nullsOrder" | "opsClass" | "partial" | "prefixLength">;
|
|
7
|
+
protected readonly indexTypes: ReadonlySet<IndexType>;
|
|
7
8
|
protected readonly indexTypeKeywords: ReadonlyMap<IndexType, string>;
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
* type that is neither is one this engine has no index for at all, refused here rather than
|
|
11
|
-
* compiled into a ` USING hnsw` the server can only answer with a syntax error: which of them a
|
|
12
|
-
* dialect *does* have is `indexTypeKeywords`, so declaring one there is all it takes to serve it.
|
|
13
|
-
*/
|
|
14
|
-
protected indexAccessMethod(index: IndexSchema): string;
|
|
15
|
-
/** What to do instead, appended to the refusal above. */
|
|
16
|
-
protected readonly vectorIndexHint: string;
|
|
9
|
+
/** ` USING btree|hash` trails the columns: between the table and them, it is a syntax error here. */
|
|
10
|
+
protected indexTuning(index: IndexSchema): string;
|
|
17
11
|
}
|
|
18
12
|
export declare class MySqlIndexDdl extends MysqlLikeIndexDdl {
|
|
19
13
|
/** The multi-valued index is the only JSON index MySQL has - see `IndexFeature` for why. */
|
|
@@ -25,12 +19,10 @@ export declare class MySqlIndexDdl extends MysqlLikeIndexDdl {
|
|
|
25
19
|
*/
|
|
26
20
|
protected jsonArrayIndexExpr(escapedColumn: string, json: IndexJsonArray): string;
|
|
27
21
|
/**
|
|
28
|
-
* MySQL has
|
|
29
|
-
*
|
|
30
|
-
* Verified against 26.7, which does have `VECTOR` columns and `STRING_TO_VECTOR`, but no distance
|
|
31
|
-
* function outside HeatWave - hence nothing to index for.
|
|
22
|
+
* MySQL 26.7 has `VECTOR` columns and `STRING_TO_VECTOR`, but no distance function outside
|
|
23
|
+
* HeatWave, hence no vector index to build: `USING hnsw` is a syntax error, `VECTOR INDEX` MariaDB's.
|
|
32
24
|
*/
|
|
33
|
-
protected readonly
|
|
25
|
+
protected readonly indexTypeHints: Map<"brin" | "btree" | "fulltext" | "gin" | "gist" | "hash" | "hnsw" | "ivfflat" | "vector" | "vectorSearch", string>;
|
|
34
26
|
}
|
|
35
27
|
export declare class MariaIndexDdl extends MysqlLikeIndexDdl {
|
|
36
28
|
/**
|
|
@@ -41,12 +33,13 @@ export declare class MariaIndexDdl extends MysqlLikeIndexDdl {
|
|
|
41
33
|
protected readonly indexFeatures: Set<"expression" | "include" | "jsonArray" | "jsonPath" | "nullsOrder" | "opsClass" | "partial" | "prefixLength">;
|
|
42
34
|
/** The family's, plus a vector index of its own: `CREATE VECTOR INDEX ... ON t (col)`, 11.7+. */
|
|
43
35
|
protected readonly indexTypeKeywords: ReadonlyMap<IndexType, string>;
|
|
36
|
+
protected readonly indexTypes: Set<"brin" | "btree" | "fulltext" | "gin" | "gist" | "hash" | "hnsw" | "ivfflat" | "vector" | "vectorSearch">;
|
|
37
|
+
/** pgvector's names are not access methods it has; `vector` is its own keyword above. */
|
|
38
|
+
protected readonly indexTypeHints: Map<"brin" | "btree" | "fulltext" | "gin" | "gist" | "hash" | "hnsw" | "ivfflat" | "vector" | "vectorSearch", string>;
|
|
44
39
|
/**
|
|
45
40
|
* `M=n DISTANCE=metric`, trailing its `CREATE VECTOR INDEX`. The metric names are MariaDB's own
|
|
46
41
|
* (`euclidean`, not `l2`), and an unsupported one throws rather than being dropped, which would
|
|
47
42
|
* silently build the index on euclidean - its default - instead of what the entity asked for.
|
|
48
43
|
*/
|
|
49
44
|
protected indexTuning(index: IndexSchema): string;
|
|
50
|
-
/** `vector` is its own keyword above; pgvector's names are not access methods it has. */
|
|
51
|
-
protected readonly vectorIndexHint = "; declare type: 'vector' instead";
|
|
52
45
|
}
|
|
@@ -1,34 +1,21 @@
|
|
|
1
1
|
import { jsonPath } from '../../dialect/jsonSql.js';
|
|
2
2
|
import { MARIA_VECTOR_METRICS } from '../../maria/mariaVectorMetrics.js';
|
|
3
|
-
import {
|
|
3
|
+
import { unsupportedVectorMetric, VECTOR_INDEX_TYPES } from '../../type/vector.js';
|
|
4
4
|
import { IndexDdl } from './indexDdl.js';
|
|
5
5
|
/**
|
|
6
6
|
* A full-text index is its own keyword here (`CREATE FULLTEXT INDEX ... (cols)`); `USING fulltext` is
|
|
7
7
|
* a syntax error, so it is the keyword that changes rather than the access method.
|
|
8
8
|
*/
|
|
9
9
|
const MYSQL_LIKE_INDEX_KEYWORDS = new Map([['fulltext', 'FULLTEXT INDEX']]);
|
|
10
|
-
/** `CREATE INDEX ... USING btree`, plus the types this family spells as a keyword instead. */
|
|
10
|
+
/** `CREATE INDEX ... (cols) USING btree`, plus the types this family spells as a keyword instead. */
|
|
11
11
|
export class MysqlLikeIndexDdl extends IndexDdl {
|
|
12
12
|
indexFeatures = new Set(['expression', 'prefixLength']);
|
|
13
|
+
indexTypes = new Set(['btree', 'hash', 'fulltext']);
|
|
13
14
|
indexTypeKeywords = MYSQL_LIKE_INDEX_KEYWORDS;
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
* compiled into a ` USING hnsw` the server can only answer with a syntax error: which of them a
|
|
18
|
-
* dialect *does* have is `indexTypeKeywords`, so declaring one there is all it takes to serve it.
|
|
19
|
-
*/
|
|
20
|
-
indexAccessMethod(index) {
|
|
21
|
-
const type = index.type;
|
|
22
|
-
if (!type || this.indexTypeKeywords.has(type)) {
|
|
23
|
-
return '';
|
|
24
|
-
}
|
|
25
|
-
if (isVectorIndexType(type)) {
|
|
26
|
-
throw new TypeError(`${this.dialect.dialectName} has no ${type} index (index "${index.name}")${this.vectorIndexHint}`);
|
|
27
|
-
}
|
|
28
|
-
return ` USING ${type}`;
|
|
15
|
+
/** ` USING btree|hash` trails the columns: between the table and them, it is a syntax error here. */
|
|
16
|
+
indexTuning(index) {
|
|
17
|
+
return index.type && !this.indexTypeKeywords.has(index.type) ? ` USING ${index.type}` : '';
|
|
29
18
|
}
|
|
30
|
-
/** What to do instead, appended to the refusal above. */
|
|
31
|
-
vectorIndexHint = '';
|
|
32
19
|
}
|
|
33
20
|
export class MySqlIndexDdl extends MysqlLikeIndexDdl {
|
|
34
21
|
/** The multi-valued index is the only JSON index MySQL has - see `IndexFeature` for why. */
|
|
@@ -43,12 +30,10 @@ export class MySqlIndexDdl extends MysqlLikeIndexDdl {
|
|
|
43
30
|
return `CAST(${source} AS ${arrayCastType(json)} ARRAY)`;
|
|
44
31
|
}
|
|
45
32
|
/**
|
|
46
|
-
* MySQL has
|
|
47
|
-
*
|
|
48
|
-
* Verified against 26.7, which does have `VECTOR` columns and `STRING_TO_VECTOR`, but no distance
|
|
49
|
-
* function outside HeatWave - hence nothing to index for.
|
|
33
|
+
* MySQL 26.7 has `VECTOR` columns and `STRING_TO_VECTOR`, but no distance function outside
|
|
34
|
+
* HeatWave, hence no vector index to build: `USING hnsw` is a syntax error, `VECTOR INDEX` MariaDB's.
|
|
50
35
|
*/
|
|
51
|
-
|
|
36
|
+
indexTypeHints = new Map(VECTOR_INDEX_TYPES.map((type) => [type, '. Vector search on MySQL needs HeatWave']));
|
|
52
37
|
}
|
|
53
38
|
export class MariaIndexDdl extends MysqlLikeIndexDdl {
|
|
54
39
|
/**
|
|
@@ -62,13 +47,19 @@ export class MariaIndexDdl extends MysqlLikeIndexDdl {
|
|
|
62
47
|
...MYSQL_LIKE_INDEX_KEYWORDS,
|
|
63
48
|
['vector', 'VECTOR INDEX'],
|
|
64
49
|
]);
|
|
50
|
+
indexTypes = new Set(['btree', 'hash', 'fulltext', 'vector']);
|
|
51
|
+
/** pgvector's names are not access methods it has; `vector` is its own keyword above. */
|
|
52
|
+
indexTypeHints = new Map([
|
|
53
|
+
['hnsw', "; declare type: 'vector' instead"],
|
|
54
|
+
['ivfflat', "; declare type: 'vector' instead"],
|
|
55
|
+
]);
|
|
65
56
|
/**
|
|
66
57
|
* `M=n DISTANCE=metric`, trailing its `CREATE VECTOR INDEX`. The metric names are MariaDB's own
|
|
67
58
|
* (`euclidean`, not `l2`), and an unsupported one throws rather than being dropped, which would
|
|
68
59
|
* silently build the index on euclidean - its default - instead of what the entity asked for.
|
|
69
60
|
*/
|
|
70
61
|
indexTuning(index) {
|
|
71
|
-
let tuning = index.m === undefined ? '' : ` M=${index.m}
|
|
62
|
+
let tuning = super.indexTuning(index) + (index.m === undefined ? '' : ` M=${index.m}`);
|
|
72
63
|
if (index.distance) {
|
|
73
64
|
const metric = MARIA_VECTOR_METRICS.get(index.distance);
|
|
74
65
|
if (!metric) {
|
|
@@ -78,8 +69,6 @@ export class MariaIndexDdl extends MysqlLikeIndexDdl {
|
|
|
78
69
|
}
|
|
79
70
|
return tuning;
|
|
80
71
|
}
|
|
81
|
-
/** `vector` is its own keyword above; pgvector's names are not access methods it has. */
|
|
82
|
-
vectorIndexHint = "; declare type: 'vector' instead";
|
|
83
72
|
}
|
|
84
73
|
/**
|
|
85
74
|
* MySQL's `CAST(... AS <type> ARRAY)` targets, the closed list its multi-valued index takes: no
|