141 lines
4.9 KiB
JavaScript
141 lines
4.9 KiB
JavaScript
import { Utils, QueryOrder, DecimalType, DoubleType } from '@mikro-orm/core';
|
|
import { MySqlSchemaHelper } from './MySqlSchemaHelper.js';
|
|
import { MySqlExceptionConverter } from './MySqlExceptionConverter.js';
|
|
import { AbstractSqlPlatform } from '../../AbstractSqlPlatform.js';
|
|
import { MySqlNativeQueryBuilder } from './MySqlNativeQueryBuilder.js';
|
|
export class BaseMySqlPlatform extends AbstractSqlPlatform {
|
|
schemaHelper = new MySqlSchemaHelper(this);
|
|
exceptionConverter = new MySqlExceptionConverter();
|
|
#jsonTypeCasts = {
|
|
string: 'text',
|
|
number: 'double',
|
|
bigint: 'bigint',
|
|
boolean: 'unsigned',
|
|
};
|
|
ORDER_BY_NULLS_TRANSLATE = {
|
|
[QueryOrder.asc_nulls_first]: 'is not null',
|
|
[QueryOrder.asc_nulls_last]: 'is null',
|
|
[QueryOrder.desc_nulls_first]: 'is not null',
|
|
[QueryOrder.desc_nulls_last]: 'is null',
|
|
};
|
|
/** @internal */
|
|
createNativeQueryBuilder() {
|
|
return new MySqlNativeQueryBuilder(this);
|
|
}
|
|
getDefaultCharset() {
|
|
return 'utf8mb4';
|
|
}
|
|
init(orm) {
|
|
super.init(orm);
|
|
orm.config.get('schemaGenerator').disableForeignKeysForClear ??= true;
|
|
}
|
|
getBeginTransactionSQL(options) {
|
|
if (options?.isolationLevel || options?.readOnly) {
|
|
const parts = [];
|
|
if (options.isolationLevel) {
|
|
parts.push(`isolation level ${options.isolationLevel}`);
|
|
}
|
|
if (options.readOnly) {
|
|
parts.push('read only');
|
|
}
|
|
const sql = `set transaction ${parts.join(', ')}`;
|
|
return [sql, 'begin'];
|
|
}
|
|
return ['begin'];
|
|
}
|
|
convertJsonToDatabaseValue(value, context) {
|
|
if (context?.mode === 'query') {
|
|
return value;
|
|
}
|
|
return JSON.stringify(value);
|
|
}
|
|
getJsonIndexDefinition(index) {
|
|
return index.columnNames.map(column => {
|
|
if (!column.includes('.')) {
|
|
return column;
|
|
}
|
|
const [root, ...path] = column.split('.');
|
|
return `(json_value(${this.quoteIdentifier(root)}, '$.${path.join('.')}' returning ${index.options?.returning ?? 'char(255)'}))`;
|
|
});
|
|
}
|
|
getBooleanTypeDeclarationSQL() {
|
|
return 'tinyint(1)';
|
|
}
|
|
normalizeColumnType(type, options) {
|
|
const simpleType = this.extractSimpleType(type);
|
|
if (['decimal', 'numeric'].includes(simpleType)) {
|
|
return this.getDecimalTypeDeclarationSQL(options);
|
|
}
|
|
return type;
|
|
}
|
|
getDefaultMappedType(type) {
|
|
if (type === 'tinyint(1)') {
|
|
return super.getDefaultMappedType('boolean');
|
|
}
|
|
return super.getDefaultMappedType(type);
|
|
}
|
|
isNumericColumn(mappedType) {
|
|
return super.isNumericColumn(mappedType) || [DecimalType, DoubleType].some(t => mappedType instanceof t);
|
|
}
|
|
supportsUnsigned() {
|
|
return true;
|
|
}
|
|
/**
|
|
* Returns the default name of index for the given columns
|
|
* cannot go past 64 character length for identifiers in MySQL
|
|
*/
|
|
getIndexName(tableName, columns, type) {
|
|
if (type === 'primary') {
|
|
return this.getDefaultPrimaryName(tableName, columns);
|
|
}
|
|
const indexName = super.getIndexName(tableName, columns, type);
|
|
if (indexName.length > 64) {
|
|
return `${indexName.substring(0, 56 - type.length)}_${Utils.hash(indexName, 5)}_${type}`;
|
|
}
|
|
return indexName;
|
|
}
|
|
getDefaultPrimaryName(tableName, columns) {
|
|
return 'PRIMARY'; // https://dev.mysql.com/doc/refman/8.0/en/create-table.html#create-table-indexes-keys
|
|
}
|
|
supportsCreatingFullTextIndex() {
|
|
return true;
|
|
}
|
|
getFullTextWhereClause() {
|
|
return `match(:column:) against (:query in boolean mode)`;
|
|
}
|
|
getFullTextIndexExpression(indexName, schemaName, tableName, columns) {
|
|
/* v8 ignore next */
|
|
const quotedTableName = this.quoteIdentifier(schemaName ? `${schemaName}.${tableName}` : tableName);
|
|
const quotedColumnNames = columns.map(c => this.quoteIdentifier(c.name));
|
|
const quotedIndexName = this.quoteIdentifier(indexName);
|
|
return `alter table ${quotedTableName} add fulltext index ${quotedIndexName}(${quotedColumnNames.join(',')})`;
|
|
}
|
|
getOrderByExpression(column, direction, collation) {
|
|
const ret = [];
|
|
const dir = direction.toLowerCase();
|
|
const col = collation ? `${column} collate ${this.quoteCollation(collation)}` : column;
|
|
if (dir in this.ORDER_BY_NULLS_TRANSLATE) {
|
|
ret.push(`${col} ${this.ORDER_BY_NULLS_TRANSLATE[dir]}`);
|
|
}
|
|
ret.push(`${col} ${dir.replace(/(\s|nulls|first|last)*/gi, '')}`);
|
|
return ret;
|
|
}
|
|
getJsonArrayFromSQL(column, alias, properties) {
|
|
const columns = properties
|
|
.map(
|
|
p =>
|
|
`${this.quoteIdentifier(p.name)} ${this.#jsonTypeCasts[p.type] ?? 'text'} path '$.${this.quoteJsonKey(p.name)}'`,
|
|
)
|
|
.join(', ');
|
|
return `json_table(${column}, '$[*]' columns (${columns})) as ${this.quoteIdentifier(alias)}`;
|
|
}
|
|
// MySQL does not support correlated json_table inside EXISTS subqueries,
|
|
// so we use a semi-join via the comma-join pattern instead.
|
|
getJsonArrayExistsSQL(from, where) {
|
|
return `(select 1 from ${from} where ${where} limit 1) is not null`;
|
|
}
|
|
getDefaultClientUrl() {
|
|
return 'mysql://root@127.0.0.1:3306';
|
|
}
|
|
}
|