Initial commit - Event Planner application
This commit is contained in:
71
node_modules/@mikro-orm/sql/dialects/mysql/BaseMySqlPlatform.d.ts
generated
vendored
Normal file
71
node_modules/@mikro-orm/sql/dialects/mysql/BaseMySqlPlatform.d.ts
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
import {
|
||||
type SimpleColumnMeta,
|
||||
type Type,
|
||||
type TransformContext,
|
||||
type MikroORM,
|
||||
type IsolationLevel,
|
||||
} from '@mikro-orm/core';
|
||||
import { MySqlSchemaHelper } from './MySqlSchemaHelper.js';
|
||||
import { MySqlExceptionConverter } from './MySqlExceptionConverter.js';
|
||||
import { AbstractSqlPlatform } from '../../AbstractSqlPlatform.js';
|
||||
import type { IndexDef } from '../../typings.js';
|
||||
import { MySqlNativeQueryBuilder } from './MySqlNativeQueryBuilder.js';
|
||||
export declare class BaseMySqlPlatform extends AbstractSqlPlatform {
|
||||
#private;
|
||||
protected readonly schemaHelper: MySqlSchemaHelper;
|
||||
protected readonly exceptionConverter: MySqlExceptionConverter;
|
||||
protected readonly ORDER_BY_NULLS_TRANSLATE: {
|
||||
readonly 'asc nulls first': 'is not null';
|
||||
readonly 'asc nulls last': 'is null';
|
||||
readonly 'desc nulls first': 'is not null';
|
||||
readonly 'desc nulls last': 'is null';
|
||||
};
|
||||
/** @internal */
|
||||
createNativeQueryBuilder(): MySqlNativeQueryBuilder;
|
||||
getDefaultCharset(): string;
|
||||
init(orm: MikroORM): void;
|
||||
getBeginTransactionSQL(options?: { isolationLevel?: IsolationLevel; readOnly?: boolean }): string[];
|
||||
convertJsonToDatabaseValue(value: unknown, context?: TransformContext): unknown;
|
||||
getJsonIndexDefinition(index: IndexDef): string[];
|
||||
getBooleanTypeDeclarationSQL(): string;
|
||||
normalizeColumnType(
|
||||
type: string,
|
||||
options: {
|
||||
length?: number;
|
||||
precision?: number;
|
||||
scale?: number;
|
||||
},
|
||||
): string;
|
||||
getDefaultMappedType(type: string): Type<unknown>;
|
||||
isNumericColumn(mappedType: Type<unknown>): boolean;
|
||||
supportsUnsigned(): boolean;
|
||||
/**
|
||||
* Returns the default name of index for the given columns
|
||||
* cannot go past 64 character length for identifiers in MySQL
|
||||
*/
|
||||
getIndexName(
|
||||
tableName: string,
|
||||
columns: string[],
|
||||
type: 'index' | 'unique' | 'foreign' | 'primary' | 'sequence',
|
||||
): string;
|
||||
getDefaultPrimaryName(tableName: string, columns: string[]): string;
|
||||
supportsCreatingFullTextIndex(): boolean;
|
||||
getFullTextWhereClause(): string;
|
||||
getFullTextIndexExpression(
|
||||
indexName: string,
|
||||
schemaName: string | undefined,
|
||||
tableName: string,
|
||||
columns: SimpleColumnMeta[],
|
||||
): string;
|
||||
getOrderByExpression(column: string, direction: string, collation?: string): string[];
|
||||
getJsonArrayFromSQL(
|
||||
column: string,
|
||||
alias: string,
|
||||
properties: {
|
||||
name: string;
|
||||
type: string;
|
||||
}[],
|
||||
): string;
|
||||
getJsonArrayExistsSQL(from: string, where: string): string;
|
||||
getDefaultClientUrl(): string;
|
||||
}
|
||||
140
node_modules/@mikro-orm/sql/dialects/mysql/BaseMySqlPlatform.js
generated
vendored
Normal file
140
node_modules/@mikro-orm/sql/dialects/mysql/BaseMySqlPlatform.js
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
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';
|
||||
}
|
||||
}
|
||||
9
node_modules/@mikro-orm/sql/dialects/mysql/MySqlExceptionConverter.d.ts
generated
vendored
Normal file
9
node_modules/@mikro-orm/sql/dialects/mysql/MySqlExceptionConverter.d.ts
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import { ExceptionConverter, type Dictionary, type DriverException } from '@mikro-orm/core';
|
||||
export declare class MySqlExceptionConverter extends ExceptionConverter {
|
||||
/**
|
||||
* @see http://dev.mysql.com/doc/refman/5.7/en/error-messages-client.html
|
||||
* @see http://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
|
||||
* @see https://github.com/doctrine/dbal/blob/master/src/Driver/AbstractMySQLDriver.php
|
||||
*/
|
||||
convertException(exception: Error & Dictionary): DriverException;
|
||||
}
|
||||
94
node_modules/@mikro-orm/sql/dialects/mysql/MySqlExceptionConverter.js
generated
vendored
Normal file
94
node_modules/@mikro-orm/sql/dialects/mysql/MySqlExceptionConverter.js
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
import {
|
||||
DeadlockException,
|
||||
LockWaitTimeoutException,
|
||||
TableExistsException,
|
||||
TableNotFoundException,
|
||||
ForeignKeyConstraintViolationException,
|
||||
UniqueConstraintViolationException,
|
||||
InvalidFieldNameException,
|
||||
NonUniqueFieldNameException,
|
||||
SyntaxErrorException,
|
||||
ConnectionException,
|
||||
NotNullConstraintViolationException,
|
||||
ExceptionConverter,
|
||||
CheckConstraintViolationException,
|
||||
} from '@mikro-orm/core';
|
||||
export class MySqlExceptionConverter extends ExceptionConverter {
|
||||
/**
|
||||
* @see http://dev.mysql.com/doc/refman/5.7/en/error-messages-client.html
|
||||
* @see http://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
|
||||
* @see https://github.com/doctrine/dbal/blob/master/src/Driver/AbstractMySQLDriver.php
|
||||
*/
|
||||
convertException(exception) {
|
||||
/* v8 ignore next */
|
||||
switch (exception.errno) {
|
||||
case 1213:
|
||||
return new DeadlockException(exception);
|
||||
case 1205:
|
||||
return new LockWaitTimeoutException(exception);
|
||||
case 1050:
|
||||
return new TableExistsException(exception);
|
||||
case 1051:
|
||||
case 1146:
|
||||
return new TableNotFoundException(exception);
|
||||
case 1216:
|
||||
case 1217:
|
||||
case 1451:
|
||||
case 1452:
|
||||
case 1701:
|
||||
return new ForeignKeyConstraintViolationException(exception);
|
||||
case 3819:
|
||||
case 4025:
|
||||
return new CheckConstraintViolationException(exception);
|
||||
case 1062:
|
||||
case 1557:
|
||||
case 1569:
|
||||
case 1586:
|
||||
return new UniqueConstraintViolationException(exception);
|
||||
case 1054:
|
||||
case 1166:
|
||||
case 1611:
|
||||
return new InvalidFieldNameException(exception);
|
||||
case 1052:
|
||||
case 1060:
|
||||
case 1110:
|
||||
return new NonUniqueFieldNameException(exception);
|
||||
case 1064:
|
||||
case 1149:
|
||||
case 1287:
|
||||
case 1341:
|
||||
case 1342:
|
||||
case 1343:
|
||||
case 1344:
|
||||
case 1382:
|
||||
case 1479:
|
||||
case 1541:
|
||||
case 1554:
|
||||
case 1626:
|
||||
return new SyntaxErrorException(exception);
|
||||
case 1044:
|
||||
case 1045:
|
||||
case 1046:
|
||||
case 1049:
|
||||
case 1095:
|
||||
case 1142:
|
||||
case 1143:
|
||||
case 1227:
|
||||
case 1370:
|
||||
case 1429:
|
||||
case 2002:
|
||||
case 2005:
|
||||
return new ConnectionException(exception);
|
||||
case 1048:
|
||||
case 1121:
|
||||
case 1138:
|
||||
case 1171:
|
||||
case 1252:
|
||||
case 1263:
|
||||
case 1364:
|
||||
case 1566:
|
||||
return new NotNullConstraintViolationException(exception);
|
||||
}
|
||||
return super.convertException(exception);
|
||||
}
|
||||
}
|
||||
7
node_modules/@mikro-orm/sql/dialects/mysql/MySqlNativeQueryBuilder.d.ts
generated
vendored
Normal file
7
node_modules/@mikro-orm/sql/dialects/mysql/MySqlNativeQueryBuilder.d.ts
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import { NativeQueryBuilder } from '../../query/NativeQueryBuilder.js';
|
||||
/** @internal */
|
||||
export declare class MySqlNativeQueryBuilder extends NativeQueryBuilder {
|
||||
protected compileInsert(): void;
|
||||
protected addLockClause(): void;
|
||||
protected addOnConflictClause(): void;
|
||||
}
|
||||
74
node_modules/@mikro-orm/sql/dialects/mysql/MySqlNativeQueryBuilder.js
generated
vendored
Normal file
74
node_modules/@mikro-orm/sql/dialects/mysql/MySqlNativeQueryBuilder.js
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
import { LockMode, RawQueryFragment, Utils } from '@mikro-orm/core';
|
||||
import { NativeQueryBuilder } from '../../query/NativeQueryBuilder.js';
|
||||
/** @internal */
|
||||
export class MySqlNativeQueryBuilder extends NativeQueryBuilder {
|
||||
compileInsert() {
|
||||
if (!this.options.data) {
|
||||
throw new Error('No data provided');
|
||||
}
|
||||
this.parts.push('insert');
|
||||
if (this.options.onConflict?.ignore) {
|
||||
this.parts.push('ignore');
|
||||
}
|
||||
this.addHintComment();
|
||||
this.parts.push(`into ${this.getTableName()}`);
|
||||
if (Object.keys(this.options.data).length === 0) {
|
||||
this.parts.push('default values');
|
||||
return;
|
||||
}
|
||||
const parts = this.processInsertData();
|
||||
this.parts.push(parts.join(', '));
|
||||
}
|
||||
addLockClause() {
|
||||
if (!this.options.lockMode) {
|
||||
return;
|
||||
}
|
||||
const map = {
|
||||
[LockMode.PESSIMISTIC_READ]: 'lock in share mode',
|
||||
[LockMode.PESSIMISTIC_WRITE]: 'for update',
|
||||
[LockMode.PESSIMISTIC_PARTIAL_WRITE]: 'for update skip locked',
|
||||
[LockMode.PESSIMISTIC_WRITE_OR_FAIL]: 'for update nowait',
|
||||
[LockMode.PESSIMISTIC_PARTIAL_READ]: 'lock in share mode skip locked',
|
||||
[LockMode.PESSIMISTIC_READ_OR_FAIL]: 'lock in share mode nowait',
|
||||
};
|
||||
if (this.options.lockMode !== LockMode.OPTIMISTIC) {
|
||||
this.parts.push(map[this.options.lockMode]);
|
||||
}
|
||||
}
|
||||
addOnConflictClause() {
|
||||
const clause = this.options.onConflict;
|
||||
if (!clause || clause.ignore) {
|
||||
return;
|
||||
}
|
||||
if (clause.merge) {
|
||||
this.parts.push('on duplicate key update');
|
||||
if (Utils.isObject(clause.merge)) {
|
||||
const fields = Object.keys(clause.merge).map(field => {
|
||||
this.params.push(clause.merge[field]);
|
||||
return `${this.quote(field)} = ?`;
|
||||
});
|
||||
this.parts.push(fields.join(', '));
|
||||
} else if (clause.merge.length === 0) {
|
||||
const dataAsArray = Utils.asArray(this.options.data);
|
||||
const keys = Object.keys(dataAsArray[0]);
|
||||
this.parts.push(keys.map(key => `${this.quote(key)} = values(${this.quote(key)})`).join(', '));
|
||||
} else {
|
||||
const fields = clause.merge.map(key => `${this.quote(key)} = values(${this.quote(key)})`);
|
||||
this.parts.push(fields.join(', '));
|
||||
}
|
||||
if (clause.where) {
|
||||
this.parts.push(`where ${clause.where.sql}`);
|
||||
this.params.push(...clause.where.params);
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.parts.push('on conflict');
|
||||
if (clause.fields instanceof RawQueryFragment) {
|
||||
this.parts.push(clause.fields.sql);
|
||||
this.params.push(...clause.fields.params);
|
||||
} else if (clause.fields.length > 0) {
|
||||
const fields = clause.fields.map(field => this.quote(field));
|
||||
this.parts.push(`(${fields.join(', ')})`);
|
||||
}
|
||||
}
|
||||
}
|
||||
47
node_modules/@mikro-orm/sql/dialects/mysql/MySqlSchemaHelper.d.ts
generated
vendored
Normal file
47
node_modules/@mikro-orm/sql/dialects/mysql/MySqlSchemaHelper.d.ts
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
import { type Dictionary, type Type } from '@mikro-orm/core';
|
||||
import type { CheckDef, Column, ForeignKey, IndexDef, Table, TableDifference } from '../../typings.js';
|
||||
import type { AbstractSqlConnection } from '../../AbstractSqlConnection.js';
|
||||
import { SchemaHelper } from '../../schema/SchemaHelper.js';
|
||||
import type { DatabaseSchema } from '../../schema/DatabaseSchema.js';
|
||||
import type { DatabaseTable } from '../../schema/DatabaseTable.js';
|
||||
export declare class MySqlSchemaHelper extends SchemaHelper {
|
||||
#private;
|
||||
static readonly DEFAULT_VALUES: {
|
||||
'now()': string[];
|
||||
'current_timestamp(?)': string[];
|
||||
'0': string[];
|
||||
};
|
||||
getSchemaBeginning(charset: string, disableForeignKeys?: boolean): string;
|
||||
disableForeignKeysSQL(): string;
|
||||
enableForeignKeysSQL(): string;
|
||||
finalizeTable(table: DatabaseTable, charset: string, collate?: string): string;
|
||||
getListTablesSQL(): string;
|
||||
getListViewsSQL(): string;
|
||||
loadViews(schema: DatabaseSchema, connection: AbstractSqlConnection, schemaName?: string): Promise<void>;
|
||||
loadInformationSchema(schema: DatabaseSchema, connection: AbstractSqlConnection, tables: Table[]): Promise<void>;
|
||||
getAllIndexes(connection: AbstractSqlConnection, tables: Table[]): Promise<Dictionary<IndexDef[]>>;
|
||||
getCreateIndexSQL(tableName: string, index: IndexDef, partialExpression?: boolean): string;
|
||||
/**
|
||||
* Build the column list for a MySQL index, with MySQL-specific handling for collation.
|
||||
* MySQL requires collation to be specified as an expression: (column_name COLLATE collation_name)
|
||||
*/
|
||||
protected getIndexColumns(index: IndexDef): string;
|
||||
/**
|
||||
* Append MySQL-specific index suffixes like INVISIBLE.
|
||||
*/
|
||||
protected appendMySqlIndexSuffix(sql: string, index: IndexDef): string;
|
||||
getAllColumns(connection: AbstractSqlConnection, tables: Table[]): Promise<Dictionary<Column[]>>;
|
||||
getAllChecks(connection: AbstractSqlConnection, tables: Table[]): Promise<Dictionary<CheckDef[]>>;
|
||||
getAllForeignKeys(connection: AbstractSqlConnection, tables: Table[]): Promise<Dictionary<Dictionary<ForeignKey>>>;
|
||||
getPreAlterTable(tableDiff: TableDifference, safe: boolean): string[];
|
||||
getRenameColumnSQL(tableName: string, oldColumnName: string, to: Column): string;
|
||||
getRenameIndexSQL(tableName: string, index: IndexDef, oldIndexName: string): string[];
|
||||
getChangeColumnCommentSQL(tableName: string, to: Column, schemaName?: string): string;
|
||||
alterTableColumn(column: Column, table: DatabaseTable, changedProperties: Set<string>): string[];
|
||||
private getColumnDeclarationSQL;
|
||||
getAllEnumDefinitions(connection: AbstractSqlConnection, tables: Table[]): Promise<Dictionary<Dictionary<string[]>>>;
|
||||
private supportsCheckConstraints;
|
||||
protected getChecksSQL(tables: Table[]): string;
|
||||
normalizeDefaultValue(defaultValue: string, length: number): string | number;
|
||||
protected wrap(val: string | null | undefined, type: Type<unknown>): string | null | undefined;
|
||||
}
|
||||
379
node_modules/@mikro-orm/sql/dialects/mysql/MySqlSchemaHelper.js
generated
vendored
Normal file
379
node_modules/@mikro-orm/sql/dialects/mysql/MySqlSchemaHelper.js
generated
vendored
Normal file
@@ -0,0 +1,379 @@
|
||||
import { EnumType, StringType, TextType } from '@mikro-orm/core';
|
||||
import { SchemaHelper } from '../../schema/SchemaHelper.js';
|
||||
export class MySqlSchemaHelper extends SchemaHelper {
|
||||
#cache = {};
|
||||
static DEFAULT_VALUES = {
|
||||
'now()': ['now()', 'current_timestamp'],
|
||||
'current_timestamp(?)': ['current_timestamp(?)'],
|
||||
0: ['0', 'false'],
|
||||
};
|
||||
getSchemaBeginning(charset, disableForeignKeys) {
|
||||
if (disableForeignKeys) {
|
||||
return `set names ${charset};\n${this.disableForeignKeysSQL()}\n\n`;
|
||||
}
|
||||
return `set names ${charset};\n\n`;
|
||||
}
|
||||
disableForeignKeysSQL() {
|
||||
return 'set foreign_key_checks = 0;';
|
||||
}
|
||||
enableForeignKeysSQL() {
|
||||
return 'set foreign_key_checks = 1;';
|
||||
}
|
||||
finalizeTable(table, charset, collate) {
|
||||
let sql = ` default character set ${charset}`;
|
||||
if (collate) {
|
||||
sql += ` collate ${collate}`;
|
||||
}
|
||||
sql += ' engine = InnoDB';
|
||||
if (table.comment) {
|
||||
sql += ` comment = ${this.platform.quoteValue(table.comment)}`;
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
getListTablesSQL() {
|
||||
return `select table_name as table_name, nullif(table_schema, schema()) as schema_name, table_comment as table_comment from information_schema.tables where table_type = 'BASE TABLE' and table_schema = schema()`;
|
||||
}
|
||||
getListViewsSQL() {
|
||||
return `select table_name as view_name, nullif(table_schema, schema()) as schema_name, view_definition from information_schema.views where table_schema = schema()`;
|
||||
}
|
||||
async loadViews(schema, connection, schemaName) {
|
||||
const views = await connection.execute(this.getListViewsSQL());
|
||||
for (const view of views) {
|
||||
// MySQL information_schema.views.view_definition requires SHOW VIEW privilege
|
||||
// and may return NULL. Use SHOW CREATE VIEW as fallback.
|
||||
let definition = view.view_definition?.trim();
|
||||
if (!definition) {
|
||||
const createView = await connection.execute(`show create view \`${view.view_name}\``);
|
||||
if (createView[0]?.['Create View']) {
|
||||
// Extract SELECT statement from CREATE VIEW ... AS SELECT ...
|
||||
const match = /\bAS\s+(.+)$/is.exec(createView[0]['Create View']);
|
||||
definition = match?.[1]?.trim();
|
||||
}
|
||||
}
|
||||
if (definition) {
|
||||
schema.addView(view.view_name, view.schema_name ?? undefined, definition);
|
||||
}
|
||||
}
|
||||
}
|
||||
async loadInformationSchema(schema, connection, tables) {
|
||||
if (tables.length === 0) {
|
||||
return;
|
||||
}
|
||||
const columns = await this.getAllColumns(connection, tables);
|
||||
const indexes = await this.getAllIndexes(connection, tables);
|
||||
const checks = await this.getAllChecks(connection, tables);
|
||||
const fks = await this.getAllForeignKeys(connection, tables);
|
||||
const enums = await this.getAllEnumDefinitions(connection, tables);
|
||||
for (const t of tables) {
|
||||
const key = this.getTableKey(t);
|
||||
const table = schema.addTable(t.table_name, t.schema_name, t.table_comment);
|
||||
const pks = await this.getPrimaryKeys(connection, indexes[key], table.name, table.schema);
|
||||
table.init(columns[key], indexes[key], checks[key], pks, fks[key], enums[key]);
|
||||
}
|
||||
}
|
||||
async getAllIndexes(connection, tables) {
|
||||
const sql = `select table_name as table_name, nullif(table_schema, schema()) as schema_name, index_name as index_name, non_unique as non_unique, column_name as column_name, index_type as index_type, sub_part as sub_part, collation as sort_order /*!80013 , expression as expression, is_visible as is_visible */
|
||||
from information_schema.statistics where table_schema = database()
|
||||
and table_name in (${tables.map(t => this.platform.quoteValue(t.table_name)).join(', ')})
|
||||
order by schema_name, table_name, index_name, seq_in_index`;
|
||||
const allIndexes = await connection.execute(sql);
|
||||
const ret = {};
|
||||
for (const index of allIndexes) {
|
||||
const key = this.getTableKey(index);
|
||||
const indexDef = {
|
||||
columnNames: [index.column_name],
|
||||
keyName: index.index_name,
|
||||
unique: !index.non_unique,
|
||||
primary: index.index_name === 'PRIMARY',
|
||||
constraint: !index.non_unique,
|
||||
};
|
||||
// Capture column options (prefix length, sort order)
|
||||
if (index.sub_part != null || index.sort_order === 'D') {
|
||||
indexDef.columns = [
|
||||
{
|
||||
name: index.column_name,
|
||||
...(index.sub_part != null && { length: index.sub_part }),
|
||||
...(index.sort_order === 'D' && { sort: 'DESC' }),
|
||||
},
|
||||
];
|
||||
}
|
||||
// Capture index type for fulltext and spatial indexes
|
||||
if (index.index_type === 'FULLTEXT') {
|
||||
indexDef.type = 'fulltext';
|
||||
} else if (index.index_type === 'SPATIAL') {
|
||||
/* v8 ignore next */
|
||||
indexDef.type = 'spatial';
|
||||
}
|
||||
// Capture invisible flag (MySQL 8.0.13+)
|
||||
if (index.is_visible === 'NO') {
|
||||
indexDef.invisible = true;
|
||||
}
|
||||
if (!index.column_name || index.expression?.match(/ where /i)) {
|
||||
indexDef.expression = index.expression; // required for the `getCreateIndexSQL()` call
|
||||
indexDef.expression = this.getCreateIndexSQL(index.table_name, indexDef, !!index.expression);
|
||||
}
|
||||
ret[key] ??= [];
|
||||
ret[key].push(indexDef);
|
||||
}
|
||||
for (const key of Object.keys(ret)) {
|
||||
ret[key] = await this.mapIndexes(ret[key]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
getCreateIndexSQL(tableName, index, partialExpression = false) {
|
||||
/* v8 ignore next */
|
||||
if (index.expression && !partialExpression) {
|
||||
return index.expression;
|
||||
}
|
||||
tableName = this.quote(tableName);
|
||||
const keyName = this.quote(index.keyName);
|
||||
let sql = `alter table ${tableName} add ${index.unique ? 'unique' : 'index'} ${keyName} `;
|
||||
if (index.expression && partialExpression) {
|
||||
sql += `(${index.expression})`;
|
||||
return this.appendMySqlIndexSuffix(sql, index);
|
||||
}
|
||||
// JSON columns can have unique index but not unique constraint, and we need to distinguish those, so we can properly drop them
|
||||
if (index.columnNames.some(column => column.includes('.'))) {
|
||||
const columns = this.platform.getJsonIndexDefinition(index);
|
||||
sql = `alter table ${tableName} add ${index.unique ? 'unique ' : ''}index ${keyName} `;
|
||||
sql += `(${columns.join(', ')})`;
|
||||
return this.appendMySqlIndexSuffix(sql, index);
|
||||
}
|
||||
// Build column list with advanced options
|
||||
const columns = this.getIndexColumns(index);
|
||||
sql += `(${columns})`;
|
||||
return this.appendMySqlIndexSuffix(sql, index);
|
||||
}
|
||||
/**
|
||||
* Build the column list for a MySQL index, with MySQL-specific handling for collation.
|
||||
* MySQL requires collation to be specified as an expression: (column_name COLLATE collation_name)
|
||||
*/
|
||||
getIndexColumns(index) {
|
||||
if (index.columns?.length) {
|
||||
return index.columns
|
||||
.map(col => {
|
||||
const quotedName = this.quote(col.name);
|
||||
// MySQL supports collation via expression: (column_name COLLATE collation_name)
|
||||
// When collation is specified, wrap in parentheses as an expression
|
||||
if (col.collation) {
|
||||
let expr = col.length ? `${quotedName}(${col.length})` : quotedName;
|
||||
expr = `(${expr} collate ${col.collation})`;
|
||||
// Sort order comes after the expression
|
||||
if (col.sort) {
|
||||
expr += ` ${col.sort}`;
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
// Standard column definition without collation
|
||||
let colDef = quotedName;
|
||||
// MySQL supports prefix length
|
||||
if (col.length) {
|
||||
colDef += `(${col.length})`;
|
||||
}
|
||||
// MySQL supports sort order
|
||||
if (col.sort) {
|
||||
colDef += ` ${col.sort}`;
|
||||
}
|
||||
return colDef;
|
||||
})
|
||||
.join(', ');
|
||||
}
|
||||
return index.columnNames.map(c => this.quote(c)).join(', ');
|
||||
}
|
||||
/**
|
||||
* Append MySQL-specific index suffixes like INVISIBLE.
|
||||
*/
|
||||
appendMySqlIndexSuffix(sql, index) {
|
||||
// MySQL 8.0+ supports INVISIBLE indexes
|
||||
if (index.invisible) {
|
||||
sql += ' invisible';
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
async getAllColumns(connection, tables) {
|
||||
const sql = `select table_name as table_name,
|
||||
nullif(table_schema, schema()) as schema_name,
|
||||
column_name as column_name,
|
||||
column_default as column_default,
|
||||
nullif(column_comment, '') as column_comment,
|
||||
is_nullable as is_nullable,
|
||||
data_type as data_type,
|
||||
column_type as column_type,
|
||||
column_key as column_key,
|
||||
extra as extra,
|
||||
generation_expression as generation_expression,
|
||||
numeric_precision as numeric_precision,
|
||||
numeric_scale as numeric_scale,
|
||||
ifnull(datetime_precision, character_maximum_length) length
|
||||
from information_schema.columns where table_schema = database() and table_name in (${tables.map(t => this.platform.quoteValue(t.table_name))})
|
||||
order by ordinal_position`;
|
||||
const allColumns = await connection.execute(sql);
|
||||
const str = val => (val != null ? '' + val : val);
|
||||
const extra = val =>
|
||||
val.replace(/auto_increment|default_generated|(stored|virtual) generated/i, '').trim() || undefined;
|
||||
const ret = {};
|
||||
for (const col of allColumns) {
|
||||
const mappedType = this.platform.getMappedType(col.column_type);
|
||||
const defaultValue = str(
|
||||
this.normalizeDefaultValue(
|
||||
mappedType.compareAsType() === 'boolean' && ['0', '1'].includes(col.column_default)
|
||||
? ['false', 'true'][+col.column_default]
|
||||
: col.column_default,
|
||||
col.length,
|
||||
),
|
||||
);
|
||||
const key = this.getTableKey(col);
|
||||
const generated = col.generation_expression
|
||||
? `(${col.generation_expression.replaceAll(`\\'`, `'`)}) ${col.extra.match(/stored generated/i) ? 'stored' : 'virtual'}`
|
||||
: undefined;
|
||||
ret[key] ??= [];
|
||||
ret[key].push({
|
||||
name: col.column_name,
|
||||
type: this.platform.isNumericColumn(mappedType)
|
||||
? col.column_type.replace(/ unsigned$/, '').replace(/\(\d+\)$/, '')
|
||||
: col.column_type,
|
||||
mappedType,
|
||||
unsigned: col.column_type.endsWith(' unsigned'),
|
||||
length: col.length,
|
||||
default: this.wrap(defaultValue, mappedType),
|
||||
nullable: col.is_nullable === 'YES',
|
||||
primary: col.column_key === 'PRI',
|
||||
unique: col.column_key === 'UNI',
|
||||
autoincrement: col.extra === 'auto_increment',
|
||||
precision: col.numeric_precision,
|
||||
scale: col.numeric_scale,
|
||||
comment: col.column_comment,
|
||||
extra: extra(col.extra),
|
||||
generated,
|
||||
});
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
async getAllChecks(connection, tables) {
|
||||
/* v8 ignore next */
|
||||
if (!(await this.supportsCheckConstraints(connection))) {
|
||||
return {};
|
||||
}
|
||||
const sql = this.getChecksSQL(tables);
|
||||
const allChecks = await connection.execute(sql);
|
||||
const ret = {};
|
||||
for (const check of allChecks) {
|
||||
const key = this.getTableKey(check);
|
||||
ret[key] ??= [];
|
||||
ret[key].push({
|
||||
name: check.name,
|
||||
columnName: check.column_name,
|
||||
definition: `check ${check.expression}`,
|
||||
expression: check.expression.replace(/^\((.*)\)$/, '$1'),
|
||||
});
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
async getAllForeignKeys(connection, tables) {
|
||||
const sql = `select k.constraint_name as constraint_name, nullif(k.table_schema, schema()) as schema_name, k.table_name as table_name, k.column_name as column_name, k.referenced_table_name as referenced_table_name, k.referenced_column_name as referenced_column_name, c.update_rule as update_rule, c.delete_rule as delete_rule
|
||||
from information_schema.key_column_usage k
|
||||
inner join information_schema.referential_constraints c on c.constraint_name = k.constraint_name and c.table_name = k.table_name
|
||||
where k.table_name in (${tables.map(t => this.platform.quoteValue(t.table_name)).join(', ')})
|
||||
and k.table_schema = database() and c.constraint_schema = database() and k.referenced_column_name is not null
|
||||
order by constraint_name, k.ordinal_position`;
|
||||
const allFks = await connection.execute(sql);
|
||||
const ret = {};
|
||||
for (const fk of allFks) {
|
||||
const key = this.getTableKey(fk);
|
||||
ret[key] ??= [];
|
||||
ret[key].push(fk);
|
||||
}
|
||||
Object.keys(ret).forEach(key => {
|
||||
const parts = key.split('.');
|
||||
/* v8 ignore next */
|
||||
const schemaName = parts.length > 1 ? parts[0] : undefined;
|
||||
ret[key] = this.mapForeignKeys(ret[key], key, schemaName);
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
getPreAlterTable(tableDiff, safe) {
|
||||
// Dropping primary keys requires to unset autoincrement attribute on the particular column first.
|
||||
const pk = Object.values(tableDiff.removedIndexes).find(idx => idx.primary);
|
||||
if (!pk || safe) {
|
||||
return [];
|
||||
}
|
||||
return pk.columnNames
|
||||
.filter(col => tableDiff.fromTable.hasColumn(col))
|
||||
.map(col => tableDiff.fromTable.getColumn(col))
|
||||
.filter(col => col.autoincrement)
|
||||
.map(
|
||||
col =>
|
||||
`alter table \`${tableDiff.name}\` modify \`${col.name}\` ${this.getColumnDeclarationSQL({ ...col, autoincrement: false })}`,
|
||||
);
|
||||
}
|
||||
getRenameColumnSQL(tableName, oldColumnName, to) {
|
||||
tableName = this.quote(tableName);
|
||||
oldColumnName = this.quote(oldColumnName);
|
||||
const columnName = this.quote(to.name);
|
||||
return `alter table ${tableName} change ${oldColumnName} ${columnName} ${this.getColumnDeclarationSQL(to)}`;
|
||||
}
|
||||
getRenameIndexSQL(tableName, index, oldIndexName) {
|
||||
tableName = this.quote(tableName);
|
||||
oldIndexName = this.quote(oldIndexName);
|
||||
const keyName = this.quote(index.keyName);
|
||||
return [`alter table ${tableName} rename index ${oldIndexName} to ${keyName}`];
|
||||
}
|
||||
getChangeColumnCommentSQL(tableName, to, schemaName) {
|
||||
tableName = this.quote(tableName);
|
||||
const columnName = this.quote(to.name);
|
||||
return `alter table ${tableName} modify ${columnName} ${this.getColumnDeclarationSQL(to)}`;
|
||||
}
|
||||
alterTableColumn(column, table, changedProperties) {
|
||||
const col = this.createTableColumn(column, table, changedProperties);
|
||||
return [`alter table ${table.getQuotedName()} modify ${col}`];
|
||||
}
|
||||
getColumnDeclarationSQL(col) {
|
||||
let ret = col.type;
|
||||
ret += col.unsigned ? ' unsigned' : '';
|
||||
ret += col.autoincrement ? ' auto_increment' : '';
|
||||
ret += ' ';
|
||||
ret += col.nullable ? 'null' : 'not null';
|
||||
ret += col.default ? ' default ' + col.default : '';
|
||||
ret += col.comment ? ` comment ${this.platform.quoteValue(col.comment)}` : '';
|
||||
return ret;
|
||||
}
|
||||
async getAllEnumDefinitions(connection, tables) {
|
||||
const sql = `select column_name as column_name, column_type as column_type, table_name as table_name
|
||||
from information_schema.columns
|
||||
where data_type = 'enum' and table_name in (${tables.map(t => `'${t.table_name}'`).join(', ')}) and table_schema = database()`;
|
||||
const enums = await connection.execute(sql);
|
||||
return enums.reduce((o, item) => {
|
||||
o[item.table_name] ??= {};
|
||||
o[item.table_name][item.column_name] = item.column_type
|
||||
.match(/enum\((.*)\)/)[1]
|
||||
.split(',')
|
||||
.map(item => /'(.*)'/.exec(item)[1]);
|
||||
return o;
|
||||
}, {});
|
||||
}
|
||||
async supportsCheckConstraints(connection) {
|
||||
if (this.#cache.supportsCheckConstraints != null) {
|
||||
return this.#cache.supportsCheckConstraints;
|
||||
}
|
||||
const sql = `select 1 from information_schema.tables where table_name = 'CHECK_CONSTRAINTS' and table_schema = 'information_schema'`;
|
||||
const res = await connection.execute(sql);
|
||||
return (this.#cache.supportsCheckConstraints = res.length > 0);
|
||||
}
|
||||
getChecksSQL(tables) {
|
||||
return `select cc.constraint_schema as table_schema, tc.table_name as table_name, cc.constraint_name as name, cc.check_clause as expression
|
||||
from information_schema.check_constraints cc
|
||||
join information_schema.table_constraints tc
|
||||
on tc.constraint_schema = cc.constraint_schema
|
||||
and tc.constraint_name = cc.constraint_name
|
||||
and constraint_type = 'CHECK'
|
||||
where tc.table_name in (${tables.map(t => this.platform.quoteValue(t.table_name))}) and tc.constraint_schema = database()
|
||||
order by tc.constraint_name`;
|
||||
}
|
||||
normalizeDefaultValue(defaultValue, length) {
|
||||
return super.normalizeDefaultValue(defaultValue, length, MySqlSchemaHelper.DEFAULT_VALUES);
|
||||
}
|
||||
wrap(val, type) {
|
||||
const stringType = type instanceof StringType || type instanceof TextType || type instanceof EnumType;
|
||||
return typeof val === 'string' && val.length > 0 && stringType ? this.platform.quoteValue(val) : val;
|
||||
}
|
||||
}
|
||||
3
node_modules/@mikro-orm/sql/dialects/mysql/index.d.ts
generated
vendored
Normal file
3
node_modules/@mikro-orm/sql/dialects/mysql/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './MySqlSchemaHelper.js';
|
||||
export * from './BaseMySqlPlatform.js';
|
||||
export * from './MySqlNativeQueryBuilder.js';
|
||||
3
node_modules/@mikro-orm/sql/dialects/mysql/index.js
generated
vendored
Normal file
3
node_modules/@mikro-orm/sql/dialects/mysql/index.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './MySqlSchemaHelper.js';
|
||||
export * from './BaseMySqlPlatform.js';
|
||||
export * from './MySqlNativeQueryBuilder.js';
|
||||
Reference in New Issue
Block a user