uql-orm 0.51.0 → 0.52.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 (56) hide show
  1. package/README.md +1 -1
  2. package/dist/bunSql/bunSql.util.d.ts +33 -10
  3. package/dist/bunSql/bunSql.util.js +57 -42
  4. package/dist/bunSql/bunSqlQuerier.d.ts +13 -8
  5. package/dist/bunSql/bunSqlQuerier.js +17 -8
  6. package/dist/bunSql/bunSqlQuerierPool.d.ts +10 -2
  7. package/dist/bunSql/bunSqlQuerierPool.js +38 -25
  8. package/dist/bunSql/index.d.ts +1 -3
  9. package/dist/bunSql/index.js +0 -3
  10. package/dist/dialect/abstractSqlDialect.d.ts +58 -4
  11. package/dist/dialect/abstractSqlDialect.js +85 -24
  12. package/dist/dialect/aliases.d.ts +2 -0
  13. package/dist/dialect/aliases.js +2 -0
  14. package/dist/dialect/mergeSqlDialect.d.ts +45 -0
  15. package/dist/dialect/mergeSqlDialect.js +89 -0
  16. package/dist/dialect/mysqlLikeSqlDialect.js +4 -1
  17. package/dist/dialect/pgLikeSqlDialect.js +4 -1
  18. package/dist/migrate/builder/expressions.js +5 -0
  19. package/dist/migrate/introspection/index.d.ts +2 -0
  20. package/dist/migrate/introspection/index.js +2 -0
  21. package/dist/migrate/introspection/mssqlIntrospector.d.ts +63 -0
  22. package/dist/migrate/introspection/mssqlIntrospector.js +198 -0
  23. package/dist/migrate/introspection/registry.d.ts +3 -0
  24. package/dist/migrate/introspection/registry.js +28 -0
  25. package/dist/migrate/migrator.js +2 -21
  26. package/dist/mongo/mongoDialect.js +4 -1
  27. package/dist/mssql/index.d.ts +3 -0
  28. package/dist/mssql/index.js +3 -0
  29. package/dist/mssql/mssqlDialect.d.ts +144 -0
  30. package/dist/mssql/mssqlDialect.js +328 -0
  31. package/dist/mssql/mssqlQuerier.d.ts +23 -0
  32. package/dist/mssql/mssqlQuerier.js +137 -0
  33. package/dist/mssql/mssqlQuerierPool.d.ts +17 -0
  34. package/dist/mssql/mssqlQuerierPool.js +32 -0
  35. package/dist/mssql/mssqlWireTypes.d.ts +23 -0
  36. package/dist/mssql/mssqlWireTypes.js +44 -0
  37. package/dist/pglite/pgliteQuerier.d.ts +4 -2
  38. package/dist/pglite/pgliteQuerier.js +7 -2
  39. package/dist/postgres/pgCursorStream.d.ts +20 -0
  40. package/dist/postgres/pgCursorStream.js +49 -0
  41. package/dist/postgres/pgDialect.d.ts +1 -1
  42. package/dist/postgres/pgDialect.js +1 -1
  43. package/dist/postgres/postgresWireDriverCapabilities.d.ts +12 -10
  44. package/dist/postgres/postgresWireDriverCapabilities.js +12 -10
  45. package/dist/schema/canonicalType.js +96 -113
  46. package/dist/sqlite/sqliteDialect.js +4 -1
  47. package/dist/type/dialect.d.ts +31 -4
  48. package/dist/type/migratorDialect.d.ts +1 -1
  49. package/dist/type/migratorDialect.js +1 -0
  50. package/package.json +13 -3
  51. package/dist/bunSql/bunSqlCockroachDialect.d.ts +0 -12
  52. package/dist/bunSql/bunSqlCockroachDialect.js +0 -15
  53. package/dist/bunSql/bunSqlPostgresDialect.d.ts +0 -11
  54. package/dist/bunSql/bunSqlPostgresDialect.js +0 -14
  55. package/dist/bunSql/bunSqliteDialect.d.ts +0 -6
  56. package/dist/bunSql/bunSqliteDialect.js +0 -6
@@ -0,0 +1,198 @@
1
+ import { AbstractSqlSchemaIntrospector } from './abstractSqlSchemaIntrospector.js';
2
+ /**
3
+ * SQL Server schema introspector.
4
+ *
5
+ * `INFORMATION_SCHEMA` answers columns and foreign keys, but not indexes: it has no view for them at
6
+ * all, and its `CONSTRAINT_COLUMN_USAGE` conflates a unique index with a unique constraint. Those
7
+ * come from `sys.indexes` instead, which is also the only place the filtered-index predicate and the
8
+ * included columns are readable.
9
+ */
10
+ export class MsSqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
11
+ defaultSchemaExpr = 'SCHEMA_NAME()';
12
+ getTableNamesQuery() {
13
+ return /*sql*/ `
14
+ SELECT TABLE_NAME as table_name
15
+ FROM INFORMATION_SCHEMA.TABLES
16
+ WHERE TABLE_SCHEMA = ${this.schemaExpr}
17
+ AND TABLE_TYPE = 'BASE TABLE'
18
+ ORDER BY TABLE_NAME
19
+ `;
20
+ }
21
+ tableExistsQuery() {
22
+ return /*sql*/ `
23
+ SELECT COUNT(*) as count
24
+ FROM INFORMATION_SCHEMA.TABLES
25
+ WHERE TABLE_SCHEMA = ${this.schemaExpr}
26
+ AND TABLE_NAME = @p1
27
+ `;
28
+ }
29
+ parseTableExistsResult(results) {
30
+ return (this.toNumber(results[0]?.count) ?? 0) > 0;
31
+ }
32
+ /**
33
+ * `is_identity` and the default come from `sys`, not `INFORMATION_SCHEMA`: the latter has no
34
+ * identity flag, and reports a default already wrapped in the parentheses the engine reprints it
35
+ * with, which is the same text either way but only nameable as a constraint through `sys`.
36
+ */
37
+ getColumnsQuery(_tableName) {
38
+ return /*sql*/ `
39
+ SELECT
40
+ c.name as column_name,
41
+ t.name as data_type,
42
+ c.max_length as max_length,
43
+ c.precision as numeric_precision,
44
+ c.scale as numeric_scale,
45
+ c.is_nullable as is_nullable,
46
+ c.is_identity as is_identity,
47
+ d.definition as column_default
48
+ FROM sys.columns c
49
+ JOIN sys.objects o ON o.object_id = c.object_id
50
+ JOIN sys.schemas s ON s.schema_id = o.schema_id
51
+ JOIN sys.types t ON t.user_type_id = c.user_type_id
52
+ LEFT JOIN sys.default_constraints d ON d.object_id = c.default_object_id
53
+ WHERE s.name = ${this.schemaExpr} AND o.name = @p1
54
+ ORDER BY c.column_id
55
+ `;
56
+ }
57
+ /** `is_primary_key` is excluded: the key is read separately, the way every other engine reads it. */
58
+ getIndexesQuery(_tableName) {
59
+ return /*sql*/ `
60
+ SELECT
61
+ i.name as index_name,
62
+ i.is_unique as is_unique,
63
+ STRING_AGG(c.name, ',') WITHIN GROUP (ORDER BY ic.key_ordinal) as columns
64
+ FROM sys.indexes i
65
+ JOIN sys.objects o ON o.object_id = i.object_id
66
+ JOIN sys.schemas s ON s.schema_id = o.schema_id
67
+ JOIN sys.index_columns ic ON ic.object_id = i.object_id AND ic.index_id = i.index_id
68
+ JOIN sys.columns c ON c.object_id = ic.object_id AND c.column_id = ic.column_id
69
+ WHERE s.name = ${this.schemaExpr} AND o.name = @p1
70
+ AND i.is_primary_key = 0 AND i.name IS NOT NULL AND ic.is_included_column = 0
71
+ GROUP BY i.name, i.is_unique
72
+ ORDER BY i.name
73
+ `;
74
+ }
75
+ getForeignKeysQuery(_tableName) {
76
+ return /*sql*/ `
77
+ SELECT
78
+ fk.name as constraint_name,
79
+ STRING_AGG(pc.name, ',') WITHIN GROUP (ORDER BY fkc.constraint_column_id) as columns,
80
+ rt.name as referenced_table,
81
+ STRING_AGG(rc.name, ',') WITHIN GROUP (ORDER BY fkc.constraint_column_id) as referenced_columns,
82
+ fk.delete_referential_action_desc as delete_rule,
83
+ fk.update_referential_action_desc as update_rule
84
+ FROM sys.foreign_keys fk
85
+ JOIN sys.objects o ON o.object_id = fk.parent_object_id
86
+ JOIN sys.schemas s ON s.schema_id = o.schema_id
87
+ JOIN sys.foreign_key_columns fkc ON fkc.constraint_object_id = fk.object_id
88
+ JOIN sys.columns pc ON pc.object_id = fkc.parent_object_id AND pc.column_id = fkc.parent_column_id
89
+ JOIN sys.objects rt ON rt.object_id = fk.referenced_object_id
90
+ JOIN sys.columns rc ON rc.object_id = fkc.referenced_object_id AND rc.column_id = fkc.referenced_column_id
91
+ WHERE s.name = ${this.schemaExpr} AND o.name = @p1
92
+ GROUP BY fk.name, rt.name, fk.delete_referential_action_desc, fk.update_referential_action_desc
93
+ ORDER BY fk.name
94
+ `;
95
+ }
96
+ getPrimaryKeyQuery(_tableName) {
97
+ return /*sql*/ `
98
+ SELECT c.name as column_name, i.name as constraint_name
99
+ FROM sys.indexes i
100
+ JOIN sys.objects o ON o.object_id = i.object_id
101
+ JOIN sys.schemas s ON s.schema_id = o.schema_id
102
+ JOIN sys.index_columns ic ON ic.object_id = i.object_id AND ic.index_id = i.index_id
103
+ JOIN sys.columns c ON c.object_id = ic.object_id AND c.column_id = ic.column_id
104
+ WHERE s.name = ${this.schemaExpr} AND o.name = @p1 AND i.is_primary_key = 1
105
+ ORDER BY ic.key_ordinal
106
+ `;
107
+ }
108
+ /**
109
+ * `max_length` is in bytes, so an `NVARCHAR` column reports twice its declared length and `MAX`
110
+ * reports `-1`. Read as characters, every Unicode column would drift to double its width the first
111
+ * time it was diffed against the entity that declared it.
112
+ */
113
+ async mapColumnsResult(read, tableName, results) {
114
+ // One extra statement for both flags, and the reader sends it once: a column is in the key or
115
+ // uniquely indexed on its own, and `sys` is the only place either is readable per column.
116
+ const flags = await read(
117
+ /*sql*/ `
118
+ SELECT c.name as column_name, MAX(CAST(i.is_primary_key AS INT)) as is_primary_key
119
+ FROM sys.indexes i
120
+ JOIN sys.objects o ON o.object_id = i.object_id
121
+ JOIN sys.schemas s ON s.schema_id = o.schema_id
122
+ JOIN sys.index_columns ic ON ic.object_id = i.object_id AND ic.index_id = i.index_id
123
+ JOIN sys.columns c ON c.object_id = ic.object_id AND c.column_id = ic.column_id
124
+ WHERE s.name = ${this.schemaExpr} AND o.name = @p1
125
+ AND i.is_unique = 1 AND ic.is_included_column = 0
126
+ AND (SELECT COUNT(*) FROM sys.index_columns k
127
+ WHERE k.object_id = i.object_id AND k.index_id = i.index_id AND k.is_included_column = 0) = 1
128
+ GROUP BY c.name
129
+ `, [tableName]);
130
+ const unique = new Set(flags.map((row) => row.column_name));
131
+ const keys = new Set(flags.filter((row) => Boolean(row.is_primary_key)).map((row) => row.column_name));
132
+ return results.map((row) => {
133
+ const type = row.data_type.toUpperCase();
134
+ const wide = type.startsWith('N');
135
+ const bytes = this.toNumber(row.max_length);
136
+ const length = bytes === undefined || bytes < 0 || !CHARACTER_TYPES.has(type) ? undefined : wide ? bytes / 2 : bytes;
137
+ return {
138
+ name: row.column_name,
139
+ type: bytes === -1 && CHARACTER_TYPES.has(type) ? `${type}(MAX)` : type,
140
+ nullable: Boolean(row.is_nullable),
141
+ defaultValue: this.parseDefaultValue(row.column_default),
142
+ isAutoIncrement: Boolean(row.is_identity),
143
+ isPrimaryKey: keys.has(row.column_name),
144
+ isUnique: unique.has(row.column_name),
145
+ length,
146
+ precision: NUMERIC_TYPES.has(type) ? this.toNumber(row.numeric_precision) : undefined,
147
+ scale: NUMERIC_TYPES.has(type) ? this.toNumber(row.numeric_scale) : undefined,
148
+ };
149
+ });
150
+ }
151
+ async mapIndexesResult(_read, _tableName, results) {
152
+ return results.map((row) => ({
153
+ name: row.index_name,
154
+ entries: (row.columns ?? '').split(',').map((column) => ({ column })),
155
+ unique: Boolean(row.is_unique),
156
+ }));
157
+ }
158
+ async mapForeignKeysResult(_read, _tableName, results) {
159
+ return results.map((row) => ({
160
+ name: row.constraint_name,
161
+ columns: (row.columns || '').split(','),
162
+ references: { table: row.referenced_table, columns: (row.referenced_columns || '').split(',') },
163
+ // `sys` spells them with an underscore: `SET_NULL`, `NO_ACTION`.
164
+ onDelete: this.normalizeReferentialAction((row.delete_rule || '').replaceAll('_', ' ')),
165
+ onUpdate: this.normalizeReferentialAction((row.update_rule || '').replaceAll('_', ' ')),
166
+ }));
167
+ }
168
+ /**
169
+ * A default is stored wrapped in at least one layer of parentheses - `((0))` for a number,
170
+ * `(N'x')` for a string - because the engine reprints it from its own parse tree.
171
+ */
172
+ parseDefaultValue(defaultValue) {
173
+ if (defaultValue === null || defaultValue === undefined) {
174
+ return undefined;
175
+ }
176
+ let text = defaultValue.trim();
177
+ while (text.startsWith('(') && text.endsWith(')')) {
178
+ text = text.slice(1, -1).trim();
179
+ }
180
+ if (text.toUpperCase() === 'NULL') {
181
+ return null;
182
+ }
183
+ const unicode = text.startsWith("N'") ? text.slice(1) : text;
184
+ if (unicode.startsWith("'") && unicode.endsWith("'")) {
185
+ return unicode.slice(1, -1).replaceAll("''", "'");
186
+ }
187
+ if (/^-?\d+$/.test(text)) {
188
+ return Number.parseInt(text, 10);
189
+ }
190
+ if (/^-?\d+\.\d+$/.test(text)) {
191
+ return Number.parseFloat(text);
192
+ }
193
+ return text;
194
+ }
195
+ }
196
+ /** The types whose `max_length` is a width rather than a fixed storage size. */
197
+ const CHARACTER_TYPES = new Set(['CHAR', 'NCHAR', 'VARCHAR', 'NVARCHAR', 'BINARY', 'VARBINARY']);
198
+ const NUMERIC_TYPES = new Set(['DECIMAL', 'NUMERIC']);
@@ -0,0 +1,3 @@
1
+ import type { QuerierPool, SchemaIntrospector } from '../../type/index.js';
2
+ /** The introspector for `dialectName`, or `undefined` where the migrator has none for it. */
3
+ export declare function introspectorFor(dialectName: string, pool: QuerierPool, schema?: string): SchemaIntrospector | undefined;
@@ -0,0 +1,28 @@
1
+ import { MongoSchemaIntrospector } from './mongoIntrospector.js';
2
+ import { MsSqlSchemaIntrospector } from './mssqlIntrospector.js';
3
+ import { MariadbSchemaIntrospector, MysqlSchemaIntrospector } from './mysqlIntrospector.js';
4
+ import { CockroachSchemaIntrospector, PostgresSchemaIntrospector } from './postgresIntrospector.js';
5
+ import { SqliteSchemaIntrospector } from './sqliteIntrospector.js';
6
+ /**
7
+ * Which introspector each engine gets. A table rather than a `switch` so `Migrator` names no
8
+ * constructor and adding an engine touches one line, not a control flow.
9
+ *
10
+ * Every entry is statically imported, so `uql-orm/migrate` still carries all of them; making the
11
+ * table's values dynamic imports would shrink that entry, at the cost of an async
12
+ * `createIntrospector` the constructor cannot await.
13
+ */
14
+ const INTROSPECTORS = {
15
+ postgres: (pool, schema) => new PostgresSchemaIntrospector(pool, schema),
16
+ cockroachdb: (pool, schema) => new CockroachSchemaIntrospector(pool, schema),
17
+ mysql: (pool, schema) => new MysqlSchemaIntrospector(pool, schema),
18
+ mariadb: (pool, schema) => new MariadbSchemaIntrospector(pool, schema),
19
+ mssql: (pool, schema) => new MsSqlSchemaIntrospector(pool, schema),
20
+ // Neither has schemas to read: SQLite attaches database files and MongoDB takes its database from
21
+ // the connection, so both ignore the argument rather than filtering on it.
22
+ sqlite: (pool) => new SqliteSchemaIntrospector(pool),
23
+ mongodb: (pool) => new MongoSchemaIntrospector(pool),
24
+ };
25
+ /** The introspector for `dialectName`, or `undefined` where the migrator has none for it. */
26
+ export function introspectorFor(dialectName, pool, schema) {
27
+ return INTROSPECTORS[dialectName]?.(pool, schema);
28
+ }
@@ -8,7 +8,7 @@ import { LoggerWrapper } from '../util/index.js';
8
8
  import { withQuerierForMigrations, withSqlQuerierForMigrations } from './acquireQuerierForMigrations.js';
9
9
  import { buildSqlQuerierMigrationModule, EMPTY_MANUAL_MIGRATION_DOWN_INNER, EMPTY_MANUAL_MIGRATION_UP_INNER, emitSqlRunCalls, } from './codegen/migrationFile.js';
10
10
  import { runMongoCommand } from './generator/mongoCommand.js';
11
- import { CockroachSchemaIntrospector, MariadbSchemaIntrospector, MongoSchemaIntrospector, MysqlSchemaIntrospector, PostgresSchemaIntrospector, SqliteSchemaIntrospector, } from './introspection/index.js';
11
+ import { introspectorFor } from './introspection/registry.js';
12
12
  import { createSchemaGenerator } from './schemaGenerator.js';
13
13
  import { createSchemaGeneratorAsync } from './schemaGeneratorAsync.js';
14
14
  import { DatabaseMigrationStorage } from './storage/databaseStorage.js';
@@ -73,26 +73,7 @@ export class Migrator {
73
73
  }
74
74
  /** `schema` reads one namespace instead of the connection's own; see {@link BaseSqlIntrospector.schema}. */
75
75
  createIntrospector(schema) {
76
- const d = this.dialectName;
77
- if (!isKnownMigratorDialect(d)) {
78
- return undefined;
79
- }
80
- switch (d) {
81
- case 'postgres':
82
- return new PostgresSchemaIntrospector(this.pool, schema);
83
- case 'cockroachdb':
84
- return new CockroachSchemaIntrospector(this.pool, schema);
85
- case 'mysql':
86
- return new MysqlSchemaIntrospector(this.pool, schema);
87
- case 'mariadb':
88
- return new MariadbSchemaIntrospector(this.pool, schema);
89
- case 'sqlite':
90
- return new SqliteSchemaIntrospector(this.pool);
91
- case 'mongodb':
92
- return new MongoSchemaIntrospector(this.pool);
93
- default:
94
- return undefined;
95
- }
76
+ return introspectorFor(this.dialectName, this.pool, schema);
96
77
  }
97
78
  createGenerator() {
98
79
  if (!isKnownMigratorDialect(this.dialectName)) {
@@ -22,7 +22,10 @@ export const mongoDialectFeatures = {
22
22
  vectorIndexRequiresNotNull: false,
23
23
  vectorSupportsLength: false,
24
24
  supportsTimestamptz: false,
25
- defaultStringAsText: false,
25
+ stringSizing: 'bounded-text',
26
+ supportsUnsigned: false,
27
+ multipleCascadePaths: true,
28
+ serverSideCursors: false,
26
29
  };
27
30
  /** What `toWireId` converts: the hex spelling of an `ObjectId`, and nothing looser. */
28
31
  const HEX_24 = /^[0-9a-f]{24}$/i;
@@ -0,0 +1,3 @@
1
+ export * from './mssqlDialect.js';
2
+ export * from './mssqlQuerier.js';
3
+ export * from './mssqlQuerierPool.js';
@@ -0,0 +1,3 @@
1
+ export * from './mssqlDialect.js';
2
+ export * from './mssqlQuerier.js';
3
+ export * from './mssqlQuerierPool.js';
@@ -0,0 +1,144 @@
1
+ import { MergeSqlDialect } from '../dialect/mergeSqlDialect.js';
2
+ import type { DialectFeatures, EntityMeta, FieldOptions, InsertIdSource, Query, QueryContext, QueryOptions, QueryPager, QuerySizeComparisonOps, Type } from '../type/index.js';
3
+ /**
4
+ * Microsoft SQL Server 2017 and up - the floor `STRING_AGG` sets, every other construct here being
5
+ * 2016 or older.
6
+ *
7
+ * Identifiers are `"`-quoted rather than bracketed: `escapeIdChar` is one character that doubles to
8
+ * escape itself, `"` is the ANSI spelling, and `tedious` enables `QUOTED_IDENTIFIER` by default.
9
+ * Brackets would buy nothing and cost the shared dialect spec, which reads that one character.
10
+ */
11
+ export declare class MsSqlDialect extends MergeSqlDialect {
12
+ #private;
13
+ protected readonly featureDefaults: DialectFeatures;
14
+ readonly dialectName = "mssql";
15
+ readonly autoIncrementSuffix = "IDENTITY(1,1)";
16
+ readonly tableOptions = "";
17
+ readonly beginTransactionCommand = "BEGIN TRANSACTION";
18
+ readonly commitTransactionCommand = "COMMIT TRANSACTION";
19
+ readonly rollbackTransactionCommand = "ROLLBACK TRANSACTION";
20
+ /**
21
+ * The level rides the `BEGIN` rather than preceding it as its own statement: a `SET TRANSACTION
22
+ * ISOLATION LEVEL` sent on its own would go to whichever pooled connection served it, not the one
23
+ * the transaction opens on, and would then stick to that connection for unrelated later queries.
24
+ * `MsSqlQuerier` reads the level back off this command and hands it to the driver.
25
+ */
26
+ readonly isolationLevelStrategy = "inline";
27
+ readonly dropIndexSyntax = "on-table";
28
+ readonly booleanLiteral = "integer";
29
+ /** [The hard server limit](https://github.com/yiisoft/yii2/issues/10371), not a driver preference. */
30
+ readonly maxBindValues = 2100;
31
+ /** `OUTPUT` has no trailing form: it sits between the column list and `VALUES`. */
32
+ readonly returningPosition = "after-target";
33
+ readonly insertIdSource: InsertIdSource;
34
+ /** Holds the update key lock across the insert; without it two concurrent upserts of one key race. */
35
+ protected readonly mergeTargetHint = " WITH (HOLDLOCK)";
36
+ protected readonly statementTerminator = ";";
37
+ /**
38
+ * `TOP (0)`, the only way this engine says "no rows": `FETCH NEXT 0 ROWS ONLY` is rejected
39
+ * outright ("the number of rows provided for a FETCH clause must be greater then zero").
40
+ */
41
+ protected selectModifier<E>(q: Query<E>): string;
42
+ /**
43
+ * `TOP (0)` is the whole page when nothing is wanted: the engine refuses `TOP` alongside an
44
+ * `OFFSET`, and a skip into an empty result changes nothing, so no clause follows it.
45
+ */
46
+ pager(ctx: QueryContext, opts: QueryPager & {
47
+ $distinct?: boolean;
48
+ }, sorted?: boolean): void;
49
+ /**
50
+ * `SET IDENTITY_INSERT` around the insert, where the payload states a key the engine would
51
+ * otherwise generate: writing one is refused outright ("cannot insert explicit value for identity
52
+ * column ... when IDENTITY_INSERT is set to OFF") rather than ignored.
53
+ *
54
+ * Emitted only for that case, because the setting is per-session and only one table may hold it at
55
+ * a time, so it is turned back off in the same batch it was turned on.
56
+ */
57
+ insert<E>(ctx: QueryContext, entity: Type<E>, payload: E | E[], opts?: QueryOptions): void;
58
+ /** The table to toggle, or nothing when no record writes a key the engine would have generated. */
59
+ private identityInsertTarget;
60
+ /**
61
+ * A `DECIMAL` read back as the exact text it was written as, where the entity declared the field a
62
+ * `String`. `tedious` decodes the type to a JS number before anything here can see it, so the
63
+ * digits past 2^53 are gone at the wire unless the column is converted before it crosses - the same
64
+ * reason MariaDB reads a vector column through `VEC_ToText`. 41 characters covers `DECIMAL(38, s)`
65
+ * with room for the sign and the point.
66
+ */
67
+ protected selectFieldExpr(escapedColumn: string, field: FieldOptions): string;
68
+ /** Named parameters, which `tedious` binds by name rather than by position. */
69
+ placeholder(index: number): string;
70
+ /** `OUTPUT` reads the written row out of the `INSERTED` pseudo-table rather than `RETURNING` it. */
71
+ returningId<E>(meta: EntityMeta<E>): string;
72
+ protected returningIdExpression<E>(meta: EntityMeta<E>): string;
73
+ protected mergeReturning(expression: string): string;
74
+ /**
75
+ * A row lock is a hint on the table here, not a clause at the end of the statement, so
76
+ * {@link lockHint} emits it and this only keeps the guard - see the base declaration.
77
+ */
78
+ protected appendLock<E>(_ctx: QueryContext, entity: Type<E>, q: Query<E>): void;
79
+ protected lockHint<E>(q: Query<E>): string;
80
+ /**
81
+ * `N'...'`, always. A bare literal is `VARCHAR`, whose codepage silently destroys anything outside
82
+ * it, and every string column this dialect creates is `NVARCHAR`. Bound parameters need nothing -
83
+ * `tedious` binds a JS string as `NVarChar` already - so this reaches only inlined literals.
84
+ */
85
+ escape(value: unknown): string;
86
+ /**
87
+ * `REGEXP_LIKE` is SQL Server 2025 at database compatibility level 170; a server below that rejects
88
+ * it itself, neither the version nor the compatibility level being knowable here - the same terms
89
+ * `uuidv7()` is emitted on. It is a predicate rather than a value, so it stands alone.
90
+ */
91
+ protected regexCondition(operand: string, placeholder: string): string;
92
+ /** There is no `CREATE SCHEMA IF NOT EXISTS`, and `CREATE SCHEMA` has to be alone in its batch. */
93
+ createSchemaSql(schema: string): string;
94
+ /** The estimate the engine already keeps per partition, live without a stats refresh. */
95
+ estimatedCount<E>(ctx: QueryContext, entity: Type<E>): void;
96
+ protected numericCast(expr: string): string;
97
+ /**
98
+ * `JSON_VALUE` returns `NVARCHAR(4000)` and, in the lax mode that is the default, answers NULL
99
+ * rather than erroring for anything longer - so a long string read through it disappears without a
100
+ * word. `OPENJSON` has no such bound, so the path is split and its last segment matched as a key.
101
+ */
102
+ protected getJsonPathScalarExpr(escapedColumn: string, jsonPathStr: string): string;
103
+ /**
104
+ * The same read as the scalar one. `JSON_QUERY` answers NULL for anything that is not an object or
105
+ * an array, so it cannot serve the JSON access mode a boolean or a number operand asks for -
106
+ * `OPENJSON` returns both as text, and {@link jsonScalarParam} binds the operand as the matching
107
+ * text. An array or object comes back as its own JSON text, which is what `OPENJSON` takes next.
108
+ */
109
+ protected getJsonPathJsonbExpr(escapedColumn: string, jsonPathStr: string): string;
110
+ /**
111
+ * A value being *compared* against a JSON path, which reads back as the text `JSON_VALUE` yields:
112
+ * `'true'` for a boolean, `'12'` for a number. So only a boolean needs re-spelling; a number or a
113
+ * string already binds as the text it will be compared with.
114
+ *
115
+ * There is no "parse this text as JSON" cast to bind through the way `CAST(? AS JSON)` and
116
+ * `json(?)` serve the other families - `JSON_QUERY` marks text as JSON but answers NULL for a
117
+ * scalar - which is why reading and writing need the two different binders here.
118
+ */
119
+ protected jsonScalarParam(ctx: QueryContext, value: unknown): string;
120
+ /**
121
+ * A value being *written* to a JSON path, which takes the type the driver sent: a `BIT` becomes a
122
+ * JSON boolean, a number a JSON number, a string a JSON string. `normalizeValue` has already
123
+ * flattened a boolean to 1/0 for this engine's columns, so the cast is what restores it.
124
+ */
125
+ protected jsonWriteParam(ctx: QueryContext, value: unknown): string;
126
+ /** An exploded element compares as text here, so `$elemMatch` always expands per field. */
127
+ protected readonly jsonContainmentIsPartial = false;
128
+ protected jsonElemFrom(jsonField: string, _fields: readonly string[], alias: string): string;
129
+ /** `JSON_VALUE`'s 4000-character bound applies to an element's field, unlike a whole column. */
130
+ protected jsonElemRef(alias: string, field?: string): string;
131
+ protected jsonAll(ctx: QueryContext, jsonField: string, value: unknown): string;
132
+ protected jsonSize(ctx: QueryContext, jsonField: string, value: number | QuerySizeComparisonOps): string;
133
+ /** `JSON_MODIFY` takes one path per call, so several keys chain into one expression. */
134
+ protected jsonSet(ctx: QueryContext, expr: string, set: Record<string, unknown>, _field?: FieldOptions): string;
135
+ /** `'append '` prefixing the path extends the array there, creating it where there is none. */
136
+ protected jsonPush(ctx: QueryContext, expr: string, push: Record<string, unknown>): string;
137
+ /** Assigning NULL to a path deletes it in lax mode, which is the default. */
138
+ protected jsonUnset(_ctx: QueryContext, expr: string, unset: readonly string[]): string;
139
+ /**
140
+ * The surviving elements are re-aggregated into an array and written back whole - there is no
141
+ * remove-by-value. `JSON_QUERY` is what marks the rebuilt text as JSON rather than a string.
142
+ */
143
+ protected jsonPullKey(ctx: QueryContext, expr: string, escapedCol: string, key: string, value: unknown): string;
144
+ }