uql-orm 0.56.0 → 0.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/README.md +7 -9
  2. package/dist/browser/uql-browser.min.js +2 -2
  3. package/dist/browser/uql-browser.min.js.map +4 -4
  4. package/dist/cockroachdb/cockroachDialect.d.ts +5 -2
  5. package/dist/cockroachdb/cockroachDialect.js +2 -10
  6. package/dist/d1/d1SqliteDialect.d.ts +1 -0
  7. package/dist/d1/d1SqliteDialect.js +2 -0
  8. package/dist/dialect/abstractSqlDialect.d.ts +196 -33
  9. package/dist/dialect/abstractSqlDialect.js +410 -203
  10. package/dist/dialect/aliases.d.ts +10 -7
  11. package/dist/dialect/aliases.js +12 -7
  12. package/dist/dialect/hydrateColumn.d.ts +8 -2
  13. package/dist/dialect/hydrateColumn.js +33 -1
  14. package/dist/dialect/jsonSql.d.ts +13 -5
  15. package/dist/dialect/jsonSql.js +24 -7
  16. package/dist/dialect/mysqlLikeSqlDialect.d.ts +31 -3
  17. package/dist/dialect/mysqlLikeSqlDialect.js +57 -5
  18. package/dist/dialect/pgLikeSqlDialect.d.ts +20 -20
  19. package/dist/dialect/pgLikeSqlDialect.js +23 -48
  20. package/dist/dialect/pgVectorMetrics.d.ts +13 -0
  21. package/dist/dialect/pgVectorMetrics.js +17 -0
  22. package/dist/dialect/queryContext.d.ts +3 -7
  23. package/dist/dialect/queryContext.js +13 -8
  24. package/dist/dialect/queryJoins.d.ts +8 -4
  25. package/dist/dialect/queryJoins.js +26 -11
  26. package/dist/dialect/vectorSqlDialect.d.ts +2 -2
  27. package/dist/dialect/vectorSqlDialect.js +2 -3
  28. package/dist/entity/decorator/bag.d.ts +2 -2
  29. package/dist/entity/decorator/entity.d.ts +8 -9
  30. package/dist/entity/decorator/entity.js +6 -7
  31. package/dist/entity/decorator/members.d.ts +7 -6
  32. package/dist/entity/decorator/members.js +2 -1
  33. package/dist/entity/metadata/definition.d.ts +16 -11
  34. package/dist/entity/metadata/definition.js +54 -42
  35. package/dist/http/handler.d.ts +2 -2
  36. package/dist/http/handler.js +0 -1
  37. package/dist/maria/mariaDialect.d.ts +13 -6
  38. package/dist/maria/mariaDialect.js +29 -9
  39. package/dist/migrate/cli.d.ts +2 -3
  40. package/dist/migrate/cli.js +2 -2
  41. package/dist/migrate/codegen/entityCodeGenerator.js +6 -4
  42. package/dist/migrate/codegen/entityTypes.d.ts +1 -1
  43. package/dist/migrate/codegen/entityTypes.js +4 -3
  44. package/dist/migrate/codegen/indexDecoratorSource.d.ts +5 -4
  45. package/dist/migrate/codegen/indexDecoratorSource.js +17 -13
  46. package/dist/migrate/codegen/sourceLiteral.d.ts +2 -0
  47. package/dist/migrate/codegen/sourceLiteral.js +4 -0
  48. package/dist/migrate/ddl/index.d.ts +1 -5
  49. package/dist/migrate/ddl/index.js +14 -25
  50. package/dist/migrate/ddl/indexDdl.d.ts +11 -2
  51. package/dist/migrate/ddl/indexDdl.js +17 -1
  52. package/dist/migrate/ddl/mssqlIndexDdl.d.ts +10 -0
  53. package/dist/migrate/ddl/mssqlIndexDdl.js +10 -0
  54. package/dist/migrate/ddl/mysqlIndexDdl.d.ts +10 -17
  55. package/dist/migrate/ddl/mysqlIndexDdl.js +16 -27
  56. package/dist/migrate/ddl/pgIndexDdl.d.ts +18 -8
  57. package/dist/migrate/ddl/pgIndexDdl.js +29 -12
  58. package/dist/migrate/generator/mongoSchemaGenerator.d.ts +3 -3
  59. package/dist/migrate/migrator.d.ts +4 -4
  60. package/dist/migrate/schemaGenerator.d.ts +8 -8
  61. package/dist/migrate/schemaGenerator.js +5 -7
  62. package/dist/migrate/schemaGeneratorAsync.d.ts +2 -3
  63. package/dist/mongo/mongoDialect.d.ts +31 -18
  64. package/dist/mongo/mongoDialect.js +146 -104
  65. package/dist/mongo/mongodbQuerier.d.ts +10 -17
  66. package/dist/mongo/mongodbQuerier.js +31 -106
  67. package/dist/mssql/mssqlDialect.d.ts +16 -0
  68. package/dist/mssql/mssqlDialect.js +26 -4
  69. package/dist/mysql/mysqlDialect.d.ts +2 -0
  70. package/dist/mysql/mysqlDialect.js +4 -0
  71. package/dist/querier/abstractQuerier.d.ts +20 -36
  72. package/dist/querier/abstractQuerier.js +35 -129
  73. package/dist/querier/abstractQuerierPool.d.ts +2 -2
  74. package/dist/querier/abstractSqlQuerier.d.ts +4 -17
  75. package/dist/querier/abstractSqlQuerier.js +40 -50
  76. package/dist/schema/canonicalType.js +4 -4
  77. package/dist/schema/indexDifferences.js +4 -4
  78. package/dist/schema/schemaASTBuilder.d.ts +3 -3
  79. package/dist/schema/schemaASTBuilder.js +32 -3
  80. package/dist/schema/schemaASTDiffer.js +5 -5
  81. package/dist/sqlite/sqliteDialect.d.ts +20 -1
  82. package/dist/sqlite/sqliteDialect.js +38 -7
  83. package/dist/turso/tursoDialect.d.ts +2 -0
  84. package/dist/turso/tursoDialect.js +2 -0
  85. package/dist/type/config.d.ts +3 -3
  86. package/dist/type/dialect.d.ts +4 -5
  87. package/dist/type/entity.d.ts +110 -69
  88. package/dist/type/migration.d.ts +7 -7
  89. package/dist/type/migratorDialect.d.ts +4 -0
  90. package/dist/type/querier.d.ts +6 -6
  91. package/dist/type/querierPool.d.ts +2 -2
  92. package/dist/type/query.d.ts +41 -72
  93. package/dist/type/query.js +10 -5
  94. package/dist/type/queryAggregate.d.ts +43 -34
  95. package/dist/type/queryAggregate.js +1 -1
  96. package/dist/type/queryWhere.d.ts +12 -9
  97. package/dist/type/universalQuerier.d.ts +4 -4
  98. package/dist/util/dialect.util.d.ts +4 -4
  99. package/dist/util/dialect.util.js +24 -15
  100. package/dist/util/field.util.d.ts +5 -0
  101. package/dist/util/field.util.js +19 -0
  102. package/dist/util/object.util.d.ts +2 -0
  103. package/dist/util/object.util.js +4 -0
  104. package/dist/util/relationQuery.util.d.ts +12 -65
  105. package/dist/util/relationQuery.util.js +27 -81
  106. package/dist/util/rowKey.util.d.ts +1 -11
  107. package/dist/util/rowKey.util.js +1 -13
  108. package/package.json +1 -1
  109. package/dist/querier/relationCount.d.ts +0 -16
  110. package/dist/querier/relationCount.js +0 -121
@@ -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: AbstractDialect, defaultForeignKeyAction?: ForeignKeyAction): import("./schemaGenerator.js").SqlSchemaGenerator | undefined;
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>;
@@ -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(` → ${drift.suggestion}`);
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 → pool → dialect class".
361
+ See the repo README section "Driver -> pool -> dialect class".
362
362
 
363
363
  export default {
364
364
  pool: new PgQuerierPool({ ... }),
@@ -11,7 +11,7 @@
11
11
  */
12
12
  import { canonicalToTypeScript } from '../../schema/canonicalType.js';
13
13
  import { DEFAULT_FOREIGN_KEY_ACTION, } from '../../schema/types.js';
14
- import { camelCase, pascalCase, singularize } from '../../util/string.util.js';
14
+ import { camelCase, lowerFirst, pascalCase, singularize } from '../../util/string.util.js';
15
15
  import { buildFieldOptionsSource, fieldNeedsRaw } from './fieldOptionsSource.js';
16
16
  import { buildIndexDecoratorSource, indexNeedsRaw, isPlainFieldIndex } from './indexDecoratorSource.js';
17
17
  /**
@@ -116,8 +116,9 @@ export class EntityCodeGenerator {
116
116
  buildEntityDecorators(table) {
117
117
  const lines = [];
118
118
  if (this.options.includeIndexes) {
119
+ const param = lowerFirst(this.options.classNameTransformer(table.name));
119
120
  for (const index of this.declaredIndexes(table)) {
120
- lines.push(buildIndexDecoratorSource(index, this.options.propertyNameTransformer));
121
+ lines.push(buildIndexDecoratorSource(index, this.options.propertyNameTransformer, param));
121
122
  }
122
123
  }
123
124
  // Entity decorator
@@ -260,9 +261,10 @@ export class EntityCodeGenerator {
260
261
  lines.push(` * Inverse relation from ${rel.from.table.name}`);
261
262
  lines.push(' */');
262
263
  }
263
- // Decorator - includes references to property name
264
+ // The inverse side, mapped by the related class's property that points back at this one.
264
265
  const inverseProp = this.options.propertyNameTransformer(this.options.singularize(table.name));
265
- lines.push(` @${decoratorName}({ entity: () => ${relatedClassName}, references: '${inverseProp}' })`);
266
+ const param = lowerFirst(relatedClassName);
267
+ lines.push(` @${decoratorName}({ entity: () => ${relatedClassName}, mappedBy: (${param}) => ${param}.${inverseProp} })`);
266
268
  // Property
267
269
  if (inverseType === 'OneToMany' || inverseType === 'ManyToMany') {
268
270
  lines.push(` ${propertyName}?: ${relatedClassName}[];`);
@@ -4,4 +4,4 @@ import type { Type } from '../../type/index.js';
4
4
  * compiler too. Interfaces, not the entity classes `generate:from-db` writes: those are the source of
5
5
  * a schema, these describe one already defined elsewhere.
6
6
  */
7
- export declare function entityTypesSource(entities: readonly Type<unknown>[]): string;
7
+ export declare function entityTypesSource(entities: readonly Type<object>[]): string;
@@ -2,6 +2,7 @@ import { getMeta } from '../../entity/index.js';
2
2
  import { canonicalToTypeScript } from '../../schema/canonicalType.js';
3
3
  import { resolveColumnCanonicalType } from '../../schema/schemaASTBuilder.js';
4
4
  import { isToManyRelation, upperFirst } from '../../util/index.js';
5
+ import { isIdentifierName } from './sourceLiteral.js';
5
6
  /**
6
7
  * A `.d.ts` for the entities as registered, so a shape that only exists at runtime reaches the
7
8
  * compiler too. Interfaces, not the entity classes `generate:from-db` writes: those are the source of
@@ -41,12 +42,12 @@ function interfaceNames(metas) {
41
42
  }
42
43
  /** `text` as an identifier: what cannot be in one is dropped, and what cannot start one is prefixed. */
43
44
  function identifier(text) {
44
- const stripped = text.replace(/[^A-Za-z0-9_$]/g, '');
45
- return /^[A-Za-z_$]/.test(stripped) ? stripped : `Entity${stripped}`;
45
+ const stripped = text.replace(/[^\p{ID_Continue}$\u200C\u200D]/gu, '');
46
+ return isIdentifierName(stripped) ? stripped : `Entity${stripped}`;
46
47
  }
47
48
  /** A column name a property cannot hold - `hero-image` - is quoted rather than dropped. */
48
49
  function member(key, type) {
49
- const name = key === identifier(key) ? key : JSON.stringify(key);
50
+ const name = isIdentifierName(key) ? key : JSON.stringify(key);
50
51
  return ` ${name}?: ${type};`;
51
52
  }
52
53
  /**
@@ -2,13 +2,14 @@ import type { IndexNode } from '../../schema/types.js';
2
2
  /**
3
3
  * Whether `@Field({ index })` can carry the whole index. It says only "this column is indexed under
4
4
  * this name", so anything else the index declares - an expression, a predicate, uniqueness, an access
5
- * method, stored columns, a stored order - has to be written out as `@Index([...])` instead.
5
+ * method, stored columns, a stored order - has to be written out as an `@Index` instead.
6
6
  */
7
7
  export declare function isPlainFieldIndex(index: IndexNode): boolean;
8
8
  /**
9
- * One `@Index([...])` as source, for an index no `@Field` can express. Emits `raw(...)` for an
10
- * expression entry, so callers import `raw` when {@link indexNeedsRaw} holds.
9
+ * One `@Index((user) => [...])` as source, for an index no `@Field` can express, its columns read off the
10
+ * key map `param` names. Emits `raw(...)` for an expression entry, so callers import `raw` when
11
+ * {@link indexNeedsRaw} holds.
11
12
  */
12
- export declare function buildIndexDecoratorSource(index: IndexNode, propertyName: (column: string) => string): string;
13
+ export declare function buildIndexDecoratorSource(index: IndexNode, propertyName: (column: string) => string, param: string): string;
13
14
  /** Whether emitting this index needs `raw` imported alongside `Index`. */
14
15
  export declare function indexNeedsRaw(index: IndexNode): boolean;
@@ -1,4 +1,4 @@
1
- import { rawTag } from './sourceLiteral.js';
1
+ import { isIdentifierName, quoted, rawTag } from './sourceLiteral.js';
2
2
  /**
3
3
  * A vector index carries its metric in the operator class pgvector names after it
4
4
  * (`vector_cosine_ops`), which is the only place introspection can recover it from. `@Index` requires
@@ -37,7 +37,7 @@ function significantModifiers(entry) {
37
37
  /**
38
38
  * Whether `@Field({ index })` can carry the whole index. It says only "this column is indexed under
39
39
  * this name", so anything else the index declares - an expression, a predicate, uniqueness, an access
40
- * method, stored columns, a stored order - has to be written out as `@Index([...])` instead.
40
+ * method, stored columns, a stored order - has to be written out as an `@Index` instead.
41
41
  */
42
42
  export function isPlainFieldIndex(index) {
43
43
  const entries = index.entries;
@@ -53,11 +53,12 @@ export function isPlainFieldIndex(index) {
53
53
  significantModifiers(entry).length === 0);
54
54
  }
55
55
  /**
56
- * One `@Index([...])` as source, for an index no `@Field` can express. Emits `raw(...)` for an
57
- * expression entry, so callers import `raw` when {@link indexNeedsRaw} holds.
56
+ * One `@Index((user) => [...])` as source, for an index no `@Field` can express, its columns read off the
57
+ * key map `param` names. Emits `raw(...)` for an expression entry, so callers import `raw` when
58
+ * {@link indexNeedsRaw} holds.
58
59
  */
59
- export function buildIndexDecoratorSource(index, propertyName) {
60
- const entries = index.entries.map((entry) => indexEntrySource(entry, propertyName)).join(', ');
60
+ export function buildIndexDecoratorSource(index, propertyName, param) {
61
+ const entries = index.entries.map((entry) => indexEntrySource(entry, propertyName, param)).join(', ');
61
62
  const isVector = index.type === 'hnsw' || index.type === 'ivfflat';
62
63
  const distance = isVector ? vectorDistance(index) : undefined;
63
64
  const options = [];
@@ -77,21 +78,24 @@ export function buildIndexDecoratorSource(index, propertyName) {
77
78
  if (index.where)
78
79
  options.push(`where: ${rawTag(index.where)}`);
79
80
  if (index.include?.length) {
80
- options.push(`include: [${index.include.map((column) => `'${propertyName(column)}'`).join(', ')}]`);
81
+ const included = index.include.map((column) => memberSource(param, propertyName(column)));
82
+ options.push(`include: (${param}) => [${included.join(', ')}]`);
81
83
  }
82
- return `@Index([${entries}]${options.length > 0 ? `, { ${options.join(', ')} }` : ''})`;
84
+ return `@Index((${param}) => [${entries}]${options.length > 0 ? `, { ${options.join(', ')} }` : ''})`;
83
85
  }
84
86
  /** Whether emitting this index needs `raw` imported alongside `Index`. */
85
87
  export function indexNeedsRaw(index) {
86
88
  return Boolean(index.where) || index.entries.some((entry) => entry.expression);
87
89
  }
88
- function indexEntrySource(entry, propertyName) {
90
+ function indexEntrySource(entry, propertyName, param) {
89
91
  if (entry.expression) {
90
92
  return rawTag(entry.column);
91
93
  }
94
+ const column = memberSource(param, propertyName(entry.column));
92
95
  const modifiers = significantModifiers(entry);
93
- if (modifiers.length === 0) {
94
- return `'${propertyName(entry.column)}'`;
95
- }
96
- return `{ column: '${propertyName(entry.column)}', ${modifiers.join(', ')} }`;
96
+ return modifiers.length === 0 ? column : `{ column: ${column}, ${modifiers.join(', ')} }`;
97
+ }
98
+ /** `user.email`, or `user['first-name']` for a property name that is no identifier. */
99
+ function memberSource(param, property) {
100
+ return isIdentifierName(property) ? `${param}.${property}` : `${param}[${quoted(property)}]`;
97
101
  }
@@ -1,3 +1,5 @@
1
+ /** Whether `text` can name a property unquoted, in any script: `dueño` can, `first-name` cannot. */
2
+ export declare function isIdentifierName(text: string): boolean;
1
3
  /**
2
4
  * A string as single-quoted source. Introspected text is arbitrary - a comment or a default
3
5
  * expression can hold a quote or a backslash - and only escaping both keeps the generated file
@@ -1,3 +1,7 @@
1
+ /** Whether `text` can name a property unquoted, in any script: `dueño` can, `first-name` cannot. */
2
+ export function isIdentifierName(text) {
3
+ return /^[\p{ID_Start}$_][\p{ID_Continue}$\u200C\u200D]*$/u.test(text);
4
+ }
1
5
  /**
2
6
  * A string as single-quoted source. Introspected text is arbitrary - a comment or a default
3
7
  * expression can hold a quote or a backslash - and only escaping both keeps the generated file
@@ -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, MysqlLikeIndexDdl } from './mysqlIndexDdl.js';
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
- * The index DDL a dialect gets, most specific first. `instanceof` rather than the `dialectName`
18
- * {@link tableDdlFor} reads, because each family's index DDL is typed to its dialect and the narrowing
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
- if (dialect instanceof CockroachDialect) {
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 { IndexType } from '../../schema/types.js';
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 = ..., ef_construction = ..., lists = ...)`. */
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 = ..., ef_construction = ..., lists = ...)`. */
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
- * ` USING btree|hash`, for the types this family does *not* spell as a keyword of its own. A vector
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 no vector index of any kind, so one is refused rather than compiled to DDL the server
29
- * rejects: `USING hnsw` is a syntax error, and MariaDB's `VECTOR INDEX` is not MySQL syntax either.
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 vectorIndexHint = ". Vector search on MySQL needs HeatWave";
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 { isVectorIndexType, unsupportedVectorMetric } from '../../type/vector.js';
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
- * ` USING btree|hash`, for the types this family does *not* spell as a keyword of its own. A vector
16
- * type that is neither is one this engine has no index for at all, refused here rather than
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 no vector index of any kind, so one is refused rather than compiled to DDL the server
47
- * rejects: `USING hnsw` is a syntax error, and MariaDB's `VECTOR INDEX` is not MySQL syntax either.
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
- vectorIndexHint = '. Vector search on MySQL needs HeatWave';
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
@@ -1,16 +1,18 @@
1
- import type { PgLikeSqlDialect } from '../../dialect/pgLikeSqlDialect.js';
2
1
  import type { IndexColumnSchema, IndexSchema } from '../../type/index.js';
3
2
  import { IndexDdl } from './indexDdl.js';
4
3
  /** `CREATE INDEX ... USING hnsw ("embedding" vector_cosine_ops) WITH (m = ...)`, pgvector's form. */
5
- export declare class PgIndexDdl extends IndexDdl<PgLikeSqlDialect> {
4
+ export declare class PgIndexDdl extends IndexDdl {
5
+ /** Postgres 18's `pg_am`, with pgvector's two. */
6
+ protected readonly indexTypes: Set<"brin" | "btree" | "fulltext" | "gin" | "gist" | "hash" | "hnsw" | "ivfflat" | "vector" | "vectorSearch">;
7
+ protected readonly indexTypeHints: ReadonlyMap<"brin" | "btree" | "fulltext" | "gin" | "gist" | "hash" | "hnsw" | "ivfflat" | "vector" | "vectorSearch", string>;
6
8
  protected readonly indexFeatures: Set<"expression" | "include" | "jsonArray" | "jsonPath" | "nullsOrder" | "opsClass" | "partial" | "prefixLength">;
9
+ /** The metrics its vector index takes, each naming the operator class it is built with. */
10
+ protected readonly vectorMetrics: ReadonlyMap<import("../../type/vector.js").VectorDistance, {
11
+ readonly op: string;
12
+ readonly opsSuffix: string;
13
+ }>;
7
14
  /** pgvector's own index types; CockroachDB's native one widens this. */
8
15
  protected isVectorIndex(index: IndexSchema): boolean;
9
- /**
10
- * ` USING <method>`. `fulltext` is refused rather than compiled into a ` USING fulltext` the server
11
- * can only answer with a syntax error: `$text` computes its `TO_TSVECTOR` per row, which no index
12
- * over the raw columns serves.
13
- */
14
16
  protected indexAccessMethod(index: IndexSchema): string;
15
17
  /**
16
18
  * A vector index's operator class is named `{type}_{metric}_ops`: an index on a `halfvec` column
@@ -33,9 +35,17 @@ export declare class PgIndexDdl extends IndexDdl<PgLikeSqlDialect> {
33
35
  */
34
36
  export declare class CockroachIndexDdl extends PgIndexDdl {
35
37
  protected readonly indexFeatures: Set<"expression" | "include" | "jsonArray" | "jsonPath" | "nullsOrder" | "opsClass" | "partial" | "prefixLength">;
38
+ /** v26.3 answers `hash` and `brin` "unimplemented", `ivfflat` "unrecognized"; `hnsw` builds its vector index. */
39
+ protected readonly indexTypes: Set<"brin" | "btree" | "fulltext" | "gin" | "gist" | "hash" | "hnsw" | "ivfflat" | "vector" | "vectorSearch">;
40
+ protected readonly indexTypeHints: Map<"brin" | "btree" | "fulltext" | "gin" | "gist" | "hash" | "hnsw" | "ivfflat" | "vector" | "vectorSearch", string>;
41
+ protected readonly vectorMetrics: ReadonlyMap<import("../../type/vector.js").VectorDistance, {
42
+ readonly op: string;
43
+ readonly opsSuffix: string;
44
+ }>;
36
45
  private isNativeVectorIndex;
37
46
  protected isVectorIndex(index: IndexSchema): boolean;
38
47
  protected indexKeyword(index: IndexSchema): string;
39
48
  protected indexAccessMethod(index: IndexSchema): string;
40
- protected indexTuning(index: IndexSchema): string;
49
+ /** None of pgvector's knobs: `WITH (m = 16)` answers "invalid storage parameter", `hnsw` included. */
50
+ protected indexTuning(): string;
41
51
  }