@mikro-orm/sql 7.2.6-dev.9 → 7.2.6
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/AbstractSqlConnection.js +7 -0
- package/AbstractSqlDriver.js +12 -9
- package/AbstractSqlPlatform.js +1 -1
- package/dialects/mysql/BaseMySqlPlatform.d.ts +2 -0
- package/dialects/mysql/BaseMySqlPlatform.js +4 -0
- package/dialects/mysql/MySqlSchemaHelper.d.ts +2 -0
- package/dialects/mysql/MySqlSchemaHelper.js +15 -2
- package/dialects/postgresql/PostgreSqlSchemaHelper.js +3 -4
- package/package.json +4 -4
- package/query/NativeQueryBuilder.d.ts +2 -0
- package/query/NativeQueryBuilder.js +11 -2
- package/query/ObjectCriteriaNode.js +2 -0
- package/query/QueryBuilder.js +16 -6
- package/schema/DatabaseTable.js +13 -2
- package/schema/SchemaComparator.js +4 -10
- package/schema/SchemaHelper.d.ts +2 -0
- package/schema/SchemaHelper.js +7 -1
package/AbstractSqlConnection.js
CHANGED
|
@@ -130,6 +130,10 @@ export class AbstractSqlConnection extends Connection {
|
|
|
130
130
|
}
|
|
131
131
|
catch (rollbackError) {
|
|
132
132
|
this.logger.warn('query', `Failed to roll back transaction: ${rollbackError.message}`);
|
|
133
|
+
// the inner writes are still part of the outer transaction, so it must not commit them
|
|
134
|
+
if (options.ctx) {
|
|
135
|
+
options.ctx.failedSavepointRollback ??= new Error(`Cannot commit the transaction, rolling back to savepoint "${trx.savepointName}" failed: ${rollbackError.message}`, { cause: rollbackError });
|
|
136
|
+
}
|
|
133
137
|
}
|
|
134
138
|
throw error;
|
|
135
139
|
}
|
|
@@ -203,6 +207,9 @@ export class AbstractSqlConnection extends Connection {
|
|
|
203
207
|
if (ctx.isRolledBack) {
|
|
204
208
|
return;
|
|
205
209
|
}
|
|
210
|
+
if ('failedSavepointRollback' in ctx) {
|
|
211
|
+
throw ctx.failedSavepointRollback;
|
|
212
|
+
}
|
|
206
213
|
await eventBroadcaster?.dispatchEvent(EventType.beforeTransactionCommit, ctx);
|
|
207
214
|
if ('savepointName' in ctx) {
|
|
208
215
|
await ctx.releaseSavepoint(ctx.savepointName).execute();
|
package/AbstractSqlDriver.js
CHANGED
|
@@ -467,9 +467,14 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
467
467
|
return;
|
|
468
468
|
}
|
|
469
469
|
const mapToPk = !hint.dataOnly && !!(ref || prop.mapToPk);
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
470
|
+
// all rows of a result share their columns, so resolve the mapped props once per join path
|
|
471
|
+
const joinedTargets = (map.joinedTargets ??= {});
|
|
472
|
+
const { targetProps, virtualProps } = (joinedTargets[path] ??= {
|
|
473
|
+
targetProps: (mapToPk
|
|
474
|
+
? meta2.getPrimaryProps()
|
|
475
|
+
: meta2.props.filter(prop => this.platform.shouldHaveColumn(prop, hint.children || []))).filter(prop => prop.fieldNames.some(name => typeof root[`${relationAlias}__${name}`] !== 'undefined')),
|
|
476
|
+
virtualProps: meta2.props.filter(prop => !ref && prop.persist === false && prop.fieldNames),
|
|
477
|
+
});
|
|
473
478
|
// If the primary key value for the relation is null, we know we haven't joined to anything
|
|
474
479
|
// and therefore we don't return any record (since all values would be null)
|
|
475
480
|
const hasPK = meta2.getPrimaryProps().every(pk => pk.fieldNames.every(name => {
|
|
@@ -490,9 +495,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
490
495
|
return;
|
|
491
496
|
}
|
|
492
497
|
let relationPojo = {};
|
|
493
|
-
|
|
494
|
-
.filter(prop => !ref && prop.persist === false && prop.fieldNames)
|
|
495
|
-
.forEach(prop => {
|
|
498
|
+
virtualProps.forEach(prop => {
|
|
496
499
|
/* v8 ignore next */
|
|
497
500
|
if (prop.fieldNames.length > 1) {
|
|
498
501
|
// composite keys
|
|
@@ -2115,7 +2118,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
2115
2118
|
if (prop.formula) {
|
|
2116
2119
|
const quotedAlias = this.platform.quoteIdentifier(tableAlias).toString();
|
|
2117
2120
|
const table = this.createFormulaTable(quotedAlias, meta, schema);
|
|
2118
|
-
const columns = meta.createColumnMappingObject(p => qb.helper.getTPTAliasForProperty(p.name, tableAlias),
|
|
2121
|
+
const columns = meta.createColumnMappingObject(p => qb.helper.getTPTAliasForProperty(p.name, tableAlias), quotedAlias);
|
|
2119
2122
|
return [raw(`${this.evaluateFormula(prop.formula, columns, table)} as ${aliased}`)];
|
|
2120
2123
|
}
|
|
2121
2124
|
if (prop.customTypes?.some(type => !!type?.convertToJSValueSQL)) {
|
|
@@ -2539,9 +2542,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
2539
2542
|
ret.push('*');
|
|
2540
2543
|
}
|
|
2541
2544
|
if (ret.length > 0 && !hasExplicitFields && addFormulas) {
|
|
2542
|
-
//
|
|
2545
|
+
// `toString()` must yield the quoted alias like `QueryBuilder.finalize()`, which dedupes formulas by rendered SQL
|
|
2543
2546
|
const quotedAlias = this.platform.quoteIdentifier(alias);
|
|
2544
|
-
const columns = meta.createColumnMappingObject(alias);
|
|
2547
|
+
const columns = meta.createColumnMappingObject(alias, quotedAlias.toString());
|
|
2545
2548
|
const effectiveSchema = schema ?? (meta.schema !== '*' ? meta.schema : undefined);
|
|
2546
2549
|
for (const prop of meta.props) {
|
|
2547
2550
|
if (lazyProps.includes(prop)) {
|
package/AbstractSqlPlatform.js
CHANGED
|
@@ -96,7 +96,7 @@ export class AbstractSqlPlatform extends Platform {
|
|
|
96
96
|
return this.formatQuery(value.sql, value.params);
|
|
97
97
|
}
|
|
98
98
|
if (Utils.isPlainObject(value) || value?.[JsonProperty]) {
|
|
99
|
-
return this.escape(
|
|
99
|
+
return this.escape(this.convertJsonToDatabaseValue(value));
|
|
100
100
|
}
|
|
101
101
|
return this.escape(value);
|
|
102
102
|
}
|
|
@@ -16,6 +16,8 @@ export declare class BaseMySqlPlatform extends AbstractSqlPlatform {
|
|
|
16
16
|
};
|
|
17
17
|
/** mysql and mariadb treat null as the lowest value when no placement is requested. */
|
|
18
18
|
sortsNullsLowest(): boolean;
|
|
19
|
+
/** mysql reads backslashes in string literals as escapes, so defaults need the same escaping as values (minus the needless `\"`). */
|
|
20
|
+
quoteDefaultValue(value: string): string;
|
|
19
21
|
supportsMultiColumnCountDistinct(): boolean;
|
|
20
22
|
/** @internal */
|
|
21
23
|
createNativeQueryBuilder(): MySqlNativeQueryBuilder;
|
|
@@ -22,6 +22,10 @@ export class BaseMySqlPlatform extends AbstractSqlPlatform {
|
|
|
22
22
|
sortsNullsLowest() {
|
|
23
23
|
return true;
|
|
24
24
|
}
|
|
25
|
+
/** mysql reads backslashes in string literals as escapes, so defaults need the same escaping as values (minus the needless `\"`). */
|
|
26
|
+
quoteDefaultValue(value) {
|
|
27
|
+
return this.quoteValue(value).replaceAll('\\"', '"');
|
|
28
|
+
}
|
|
25
29
|
supportsMultiColumnCountDistinct() {
|
|
26
30
|
return true;
|
|
27
31
|
}
|
|
@@ -62,4 +62,6 @@ export declare class MySqlSchemaHelper extends SchemaHelper {
|
|
|
62
62
|
protected getChecksSQL(tables: Table[]): string;
|
|
63
63
|
normalizeDefaultValue(defaultValue: string, length: number): string | number;
|
|
64
64
|
protected wrap(val: string | null | undefined, type: Type<unknown>): string | null | undefined;
|
|
65
|
+
/** Decodes the backslash escapes of an introspected string literal. */
|
|
66
|
+
unescapeDefault(val: string): string;
|
|
65
67
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EnumType, StringType, TextType } from '@mikro-orm/core';
|
|
1
|
+
import { EnumType, JsonType, StringType, TextType, } from '@mikro-orm/core';
|
|
2
2
|
import { SchemaHelper } from '../../schema/SchemaHelper.js';
|
|
3
3
|
export class MySqlSchemaHelper extends SchemaHelper {
|
|
4
4
|
#cache = {};
|
|
@@ -601,7 +601,20 @@ export class MySqlSchemaHelper extends SchemaHelper {
|
|
|
601
601
|
return super.normalizeDefaultValue(defaultValue, length, MySqlSchemaHelper.DEFAULT_VALUES);
|
|
602
602
|
}
|
|
603
603
|
wrap(val, type) {
|
|
604
|
+
if (typeof val !== 'string' || val.length === 0) {
|
|
605
|
+
return val;
|
|
606
|
+
}
|
|
607
|
+
// text/blob literal defaults come back escaped twice with a charset introducer, e.g. `_utf8mb4\'it\\\'s\'` (JSON is parsed by the comparator)
|
|
608
|
+
const expression = type instanceof JsonType ? null : /^_\w+\\'(.*)\\'$/s.exec(val);
|
|
609
|
+
if (expression) {
|
|
610
|
+
return this.platform.quoteDefaultValue(this.unescapeDefault(expression[1].replace(/\\(.)/gs, '$1')));
|
|
611
|
+
}
|
|
604
612
|
const stringType = type instanceof StringType || type instanceof TextType || type instanceof EnumType;
|
|
605
|
-
return
|
|
613
|
+
return stringType ? this.platform.quoteDefaultValue(val) : val;
|
|
614
|
+
}
|
|
615
|
+
/** Decodes the backslash escapes of an introspected string literal. */
|
|
616
|
+
unescapeDefault(val) {
|
|
617
|
+
const sqlEscapes = { 0: '\0', b: '\b', n: '\n', r: '\r', t: '\t', Z: '\x1a' };
|
|
618
|
+
return val.replace(/\\(.)/gs, (_, char) => sqlEscapes[char] ?? char);
|
|
606
619
|
}
|
|
607
620
|
}
|
|
@@ -330,10 +330,9 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
|
|
|
330
330
|
return columnDefs.map(colDef => {
|
|
331
331
|
const name = unquote(colDef);
|
|
332
332
|
const result = { name };
|
|
333
|
-
//
|
|
334
|
-
// Create a pattern that matches the column name (quoted or unquoted) followed by modifiers
|
|
333
|
+
// Match the column (quoted or unquoted) as a whole list item, so `id` never matches inside `author_id`
|
|
335
334
|
const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
336
|
-
const colPattern = new RegExp(`"?${escapedName}"
|
|
335
|
+
const colPattern = new RegExp(`(?:^|,)\\s*"?${escapedName}"?(?=\\s|,|$)\\s*([^,)]*?)(?:,|$)`, 'i');
|
|
337
336
|
const colMatch = columnsStr.match(colPattern);
|
|
338
337
|
if (colMatch) {
|
|
339
338
|
const modifiers = colMatch[1];
|
|
@@ -1227,7 +1226,7 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
|
|
|
1227
1226
|
if (!defaultValue || typeof defaultValue !== 'string') {
|
|
1228
1227
|
return super.normalizeDefaultValue(defaultValue, length, PostgreSqlSchemaHelper.DEFAULT_VALUES);
|
|
1229
1228
|
}
|
|
1230
|
-
const match = /^'(.*)'::(.*)
|
|
1229
|
+
const match = /^'(.*)'::(.*)$/s.exec(defaultValue);
|
|
1231
1230
|
if (match) {
|
|
1232
1231
|
if (match[2] === 'integer') {
|
|
1233
1232
|
return +match[1];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/sql",
|
|
3
|
-
"version": "7.2.6
|
|
3
|
+
"version": "7.2.6",
|
|
4
4
|
"description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"data-mapper",
|
|
@@ -47,13 +47,13 @@
|
|
|
47
47
|
"copy": "node ../../scripts/copy.mjs"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"kysely": "0.29.
|
|
50
|
+
"kysely": "0.29.7"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@mikro-orm/core": "^7.2.
|
|
53
|
+
"@mikro-orm/core": "^7.2.6"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@mikro-orm/core": "7.2.6
|
|
56
|
+
"@mikro-orm/core": "7.2.6"
|
|
57
57
|
},
|
|
58
58
|
"engines": {
|
|
59
59
|
"node": ">= 22.17.0"
|
|
@@ -121,6 +121,8 @@ export declare class NativeQueryBuilder implements Subquery {
|
|
|
121
121
|
lockMode(lockMode: LockMode, lockTables?: string[]): this;
|
|
122
122
|
comment(comment: string | string[]): this;
|
|
123
123
|
hintComment(comment: string | string[]): this;
|
|
124
|
+
/** Comments are interpolated verbatim, so a terminator or a `?` placeholder inside one would corrupt the query. */
|
|
125
|
+
private validateComments;
|
|
124
126
|
setFlags(flags: Set<QueryFlag>): this;
|
|
125
127
|
clear(clause: keyof Options): this;
|
|
126
128
|
wrap(prefix: string, suffix: string): this;
|
|
@@ -258,14 +258,23 @@ export class NativeQueryBuilder {
|
|
|
258
258
|
}
|
|
259
259
|
comment(comment) {
|
|
260
260
|
this.options.comment ??= [];
|
|
261
|
-
this.options.comment.push(...
|
|
261
|
+
this.options.comment.push(...this.validateComments(comment));
|
|
262
262
|
return this;
|
|
263
263
|
}
|
|
264
264
|
hintComment(comment) {
|
|
265
265
|
this.options.hintComment ??= [];
|
|
266
|
-
this.options.hintComment.push(...
|
|
266
|
+
this.options.hintComment.push(...this.validateComments(comment));
|
|
267
267
|
return this;
|
|
268
268
|
}
|
|
269
|
+
/** Comments are interpolated verbatim, so a terminator or a `?` placeholder inside one would corrupt the query. */
|
|
270
|
+
validateComments(comment) {
|
|
271
|
+
const comments = Utils.asArray(comment);
|
|
272
|
+
const invalid = comments.find(c => c.includes('/*') || c.includes('*/') || c.includes('?'));
|
|
273
|
+
if (invalid != null) {
|
|
274
|
+
throw new Error(`Query comments cannot contain '/*', '*/' or '?', got: '${invalid}'`);
|
|
275
|
+
}
|
|
276
|
+
return comments;
|
|
277
|
+
}
|
|
269
278
|
setFlags(flags) {
|
|
270
279
|
this.options.flags = flags;
|
|
271
280
|
return this;
|
|
@@ -57,6 +57,8 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
57
57
|
// schema-less entities the schema is resolved dynamically and needs to be carried over
|
|
58
58
|
const fixedSchema = parentMeta.schema && parentMeta.schema !== '*';
|
|
59
59
|
const qb2 = qb.clone(true, fixedSchema ? undefined : ['schema']);
|
|
60
|
+
// the payload was already converted when processing the parent query
|
|
61
|
+
qb2.unsetFlag(QueryFlag.CONVERT_CUSTOM_TYPES);
|
|
60
62
|
const joinAlias = qb2.getNextAlias(this.prop.targetMeta.class);
|
|
61
63
|
const sub = qb2
|
|
62
64
|
.from(parentMeta.class)
|
package/query/QueryBuilder.js
CHANGED
|
@@ -1157,8 +1157,9 @@ export class QueryBuilder {
|
|
|
1157
1157
|
const hash = (data) => {
|
|
1158
1158
|
return Utils.getPrimaryKeyHash(meta.primaryKeys.map(pk => data[pk]));
|
|
1159
1159
|
};
|
|
1160
|
+
const map = {};
|
|
1160
1161
|
for await (const row of res) {
|
|
1161
|
-
const mapped = this.driver.mapResult(row, meta, this.#state.populate, this);
|
|
1162
|
+
const mapped = this.driver.mapResult(row, meta, this.#state.populate, this, map);
|
|
1162
1163
|
if (!options.mergeResults || joinedProps.length === 0) {
|
|
1163
1164
|
yield this.mapResult(mapped, options.mapResults);
|
|
1164
1165
|
continue;
|
|
@@ -1568,6 +1569,19 @@ export class QueryBuilder {
|
|
|
1568
1569
|
if (prop && [ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
|
|
1569
1570
|
return;
|
|
1570
1571
|
}
|
|
1572
|
+
// the joined strategy selects by column name, which can collide with the name of another property
|
|
1573
|
+
const shadowed = customAlias &&
|
|
1574
|
+
prop &&
|
|
1575
|
+
(prop.kind === ReferenceKind.EMBEDDED ||
|
|
1576
|
+
(customAlias.endsWith(`__${f}`) && (prop.persist === false || !prop.fieldNames.includes(f)))) &&
|
|
1577
|
+
(this.#state.aliases[a]?.meta ?? this.mainAlias.meta).props.some(p => p !== prop &&
|
|
1578
|
+
p.persist !== false &&
|
|
1579
|
+
(p.kind !== ReferenceKind.EMBEDDED || p.object) &&
|
|
1580
|
+
p.fieldNames?.includes(f));
|
|
1581
|
+
if (shadowed) {
|
|
1582
|
+
ret.push(raw('?? as ??', [`${a}.${f}`, customAlias]));
|
|
1583
|
+
return;
|
|
1584
|
+
}
|
|
1571
1585
|
if (prop?.persist === false && !prop.embedded && !prop.formula && type === 'where') {
|
|
1572
1586
|
return;
|
|
1573
1587
|
}
|
|
@@ -1577,11 +1591,7 @@ export class QueryBuilder {
|
|
|
1577
1591
|
ret.push(getFieldName(aliased, customAlias));
|
|
1578
1592
|
return;
|
|
1579
1593
|
}
|
|
1580
|
-
|
|
1581
|
-
const shadowed = customAlias &&
|
|
1582
|
-
prop?.kind === ReferenceKind.EMBEDDED &&
|
|
1583
|
-
(this.#state.aliases[a]?.meta ?? this.mainAlias.meta).props.some(p => (p.kind !== ReferenceKind.EMBEDDED || p.object) && p.fieldNames?.includes(f));
|
|
1584
|
-
if (prop?.kind === ReferenceKind.EMBEDDED && !shadowed) {
|
|
1594
|
+
if (prop?.kind === ReferenceKind.EMBEDDED) {
|
|
1585
1595
|
if (customAlias) {
|
|
1586
1596
|
throw new Error(`Cannot use 'as ${customAlias}' alias on embedded property '${field}' because it expands to multiple columns. Alias individual fields instead (e.g. '${field}.propertyName as ${customAlias}').`);
|
|
1587
1597
|
}
|
package/schema/DatabaseTable.js
CHANGED
|
@@ -876,9 +876,9 @@ export class DatabaseTable {
|
|
|
876
876
|
return +defaultValue;
|
|
877
877
|
}
|
|
878
878
|
// unquote string defaults if `raw = false`
|
|
879
|
-
const match = /^'(.*)'
|
|
879
|
+
const match = /^'(.*)'$/s.exec('' + val);
|
|
880
880
|
if (!raw && match) {
|
|
881
|
-
return match[1];
|
|
881
|
+
return schemaHelper.unescapeDefault(match[1]);
|
|
882
882
|
}
|
|
883
883
|
return '' + val;
|
|
884
884
|
}
|
|
@@ -924,6 +924,17 @@ export class DatabaseTable {
|
|
|
924
924
|
};
|
|
925
925
|
return [expand(rootProp, 1)];
|
|
926
926
|
}
|
|
927
|
+
// object embedded property index, the JSON keys are the field names of the embedded properties
|
|
928
|
+
if (rootProp?.embeddable && rootProp.object && rootProp.embeddedProps[parts[1]]) {
|
|
929
|
+
let p = rootProp.embeddedProps[parts[1]];
|
|
930
|
+
let i = 2;
|
|
931
|
+
while (i < parts.length && p.embeddedProps?.[parts[i]]) {
|
|
932
|
+
p = p.embeddedProps[parts[i]];
|
|
933
|
+
i++;
|
|
934
|
+
}
|
|
935
|
+
// a path below the last embedded property targets plain JSON keys, kept as they are
|
|
936
|
+
return [[...p.embeddedPath, ...parts.slice(i)].join('.')];
|
|
937
|
+
}
|
|
927
938
|
// json index, we need to rename the column only
|
|
928
939
|
if (rootProp) {
|
|
929
940
|
return [prop.replace(root, rootProp.fieldNames[0])];
|
|
@@ -866,16 +866,10 @@ export class SchemaComparator {
|
|
|
866
866
|
if (cols1.length === 0 && cols2.length === 0) {
|
|
867
867
|
return true;
|
|
868
868
|
}
|
|
869
|
-
//
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
// Compare each column's options
|
|
874
|
-
// Note: We don't check c1.name !== c2.name because the indexes already have matching columnNames
|
|
875
|
-
// and the columns array is derived from those same column names
|
|
876
|
-
for (let i = 0; i < cols1.length; i++) {
|
|
877
|
-
const c1 = cols1[i];
|
|
878
|
-
const c2 = cols2[i];
|
|
869
|
+
// introspection may omit entries for columns with default options, so match them by name
|
|
870
|
+
for (const name of index1.columnNames) {
|
|
871
|
+
const c1 = cols1.find(c => c.name === name) ?? { name };
|
|
872
|
+
const c2 = cols2.find(c => c.name === name) ?? { name };
|
|
879
873
|
const sort1 = c1.sort?.toUpperCase() ?? 'ASC';
|
|
880
874
|
const sort2 = c2.sort?.toUpperCase() ?? 'ASC';
|
|
881
875
|
if (sort1 !== sort2) {
|
package/schema/SchemaHelper.d.ts
CHANGED
|
@@ -163,6 +163,8 @@ export declare abstract class SchemaHelper {
|
|
|
163
163
|
getNamespaces(connection: AbstractSqlConnection, ctx?: Transaction): Promise<string[]>;
|
|
164
164
|
protected mapIndexes(indexes: IndexDef[]): Promise<IndexDef[]>;
|
|
165
165
|
mapForeignKeys(fks: any[], tableName: string, schemaName?: string): Dictionary;
|
|
166
|
+
/** Decodes the content of an introspected string literal, the inverse of `Platform.quoteDefaultValue()`. */
|
|
167
|
+
unescapeDefault(val: string): string;
|
|
166
168
|
normalizeDefaultValue(defaultValue: string | RawQueryFragment, length?: number, defaultValues?: Dictionary<string[]>): string | number;
|
|
167
169
|
getCreateDatabaseSQL(name: string): string;
|
|
168
170
|
getDropDatabaseSQL(name: string): string;
|
package/schema/SchemaHelper.js
CHANGED
|
@@ -614,6 +614,8 @@ export class SchemaHelper {
|
|
|
614
614
|
}
|
|
615
615
|
if (useDefault) {
|
|
616
616
|
// https://dev.mysql.com/doc/refman/9.0/en/data-type-defaults.html
|
|
617
|
+
// `tinytext`, `mediumtext`, `longblob` etc. follow the same rule as `text` and `blob`
|
|
618
|
+
const baseType = column.type.toLowerCase().replace(/^(tiny|medium|long)/, '');
|
|
617
619
|
const needsExpression = [
|
|
618
620
|
'blob',
|
|
619
621
|
'text',
|
|
@@ -625,7 +627,7 @@ export class SchemaHelper {
|
|
|
625
627
|
'multilinestring',
|
|
626
628
|
'multipolygon',
|
|
627
629
|
'geometrycollection',
|
|
628
|
-
].some(type =>
|
|
630
|
+
].some(type => baseType.startsWith(type));
|
|
629
631
|
const defaultSql = needsExpression && !column.default.startsWith('(') ? `(${column.default})` : column.default;
|
|
630
632
|
col.push(`default ${defaultSql}`);
|
|
631
633
|
}
|
|
@@ -716,6 +718,10 @@ export class SchemaHelper {
|
|
|
716
718
|
return ret;
|
|
717
719
|
}, {});
|
|
718
720
|
}
|
|
721
|
+
/** Decodes the content of an introspected string literal, the inverse of `Platform.quoteDefaultValue()`. */
|
|
722
|
+
unescapeDefault(val) {
|
|
723
|
+
return val.replaceAll("''", "'");
|
|
724
|
+
}
|
|
719
725
|
normalizeDefaultValue(defaultValue, length, defaultValues = {}) {
|
|
720
726
|
if (defaultValue == null) {
|
|
721
727
|
return defaultValue;
|