Files
evento/node_modules/@mikro-orm/sql/AbstractSqlPlatform.js
2026-03-18 14:55:56 -03:00

169 lines
5.2 KiB
JavaScript

import { isRaw, JsonProperty, Platform, raw, Utils } from '@mikro-orm/core';
import { SqlEntityRepository } from './SqlEntityRepository.js';
import { SqlSchemaGenerator } from './schema/SqlSchemaGenerator.js';
import { NativeQueryBuilder } from './query/NativeQueryBuilder.js';
/** Base class for SQL database platforms, providing SQL generation and quoting utilities. */
export class AbstractSqlPlatform extends Platform {
static #JSON_PROPERTY_NAME_RE = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
schemaHelper;
usesPivotTable() {
return true;
}
indexForeignKeys() {
return true;
}
getRepositoryClass() {
return SqlEntityRepository;
}
getSchemaHelper() {
return this.schemaHelper;
}
/** @inheritDoc */
lookupExtensions(orm) {
SqlSchemaGenerator.register(orm);
}
/* v8 ignore next: kept for type inference only */
getSchemaGenerator(driver, em) {
return new SqlSchemaGenerator(em ?? driver);
}
/** @internal */
/* v8 ignore next */
createNativeQueryBuilder() {
return new NativeQueryBuilder(this);
}
getBeginTransactionSQL(options) {
if (options?.isolationLevel) {
return [`set transaction isolation level ${options.isolationLevel}`, 'begin'];
}
return ['begin'];
}
getCommitTransactionSQL() {
return 'commit';
}
getRollbackTransactionSQL() {
return 'rollback';
}
getSavepointSQL(savepointName) {
return `savepoint ${this.quoteIdentifier(savepointName)}`;
}
getRollbackToSavepointSQL(savepointName) {
return `rollback to savepoint ${this.quoteIdentifier(savepointName)}`;
}
getReleaseSavepointSQL(savepointName) {
return `release savepoint ${this.quoteIdentifier(savepointName)}`;
}
quoteValue(value) {
if (isRaw(value)) {
return this.formatQuery(value.sql, value.params);
}
if (Utils.isPlainObject(value) || value?.[JsonProperty]) {
return this.escape(JSON.stringify(value));
}
return this.escape(value);
}
getSearchJsonPropertySQL(path, type, aliased) {
return this.getSearchJsonPropertyKey(path.split('->'), type, aliased);
}
getSearchJsonPropertyKey(path, type, aliased, value) {
const [a, ...b] = path;
if (aliased) {
return raw(
alias => `json_extract(${this.quoteIdentifier(`${alias}.${a}`)}, '$.${b.map(this.quoteJsonKey).join('.')}')`,
);
}
return raw(`json_extract(${this.quoteIdentifier(a)}, '$.${b.map(this.quoteJsonKey).join('.')}')`);
}
/**
* Quotes a key for use inside a JSON path expression (e.g. `$.key`).
* Simple alphanumeric keys are left unquoted; others are wrapped in double quotes.
* @internal
*/
quoteJsonKey(key) {
return /^[a-z]\w*$/i.exec(key) ? key : `"${key}"`;
}
getJsonIndexDefinition(index) {
return index.columnNames.map(column => {
if (!column.includes('.')) {
return column;
}
const [root, ...path] = column.split('.');
return `(json_extract(${root}, '$.${path.join('.')}'))`;
});
}
supportsUnionWhere() {
return true;
}
supportsSchemas() {
return false;
}
/** @inheritDoc */
generateCustomOrder(escapedColumn, values) {
let ret = '(case ';
values.forEach((v, i) => {
ret += `when ${escapedColumn} = ${this.quoteValue(v)} then ${i} `;
});
return ret + 'else null end)';
}
/**
* @internal
*/
getOrderByExpression(column, direction, collation) {
if (collation) {
return [`${column} collate ${this.quoteCollation(collation)} ${direction.toLowerCase()}`];
}
return [`${column} ${direction.toLowerCase()}`];
}
/**
* Quotes a collation name for use in COLLATE clauses.
* @internal
*/
quoteCollation(collation) {
this.validateCollationName(collation);
return this.quoteIdentifier(collation);
}
/** @internal */
validateCollationName(collation) {
if (!/^[\w]+$/.test(collation)) {
throw new Error(`Invalid collation name: '${collation}'. Collation names must contain only word characters.`);
}
}
/** @internal */
validateJsonPropertyName(name) {
if (!AbstractSqlPlatform.#JSON_PROPERTY_NAME_RE.test(name)) {
throw new Error(
`Invalid JSON property name: '${name}'. JSON property names must contain only alphanumeric characters and underscores.`,
);
}
}
/**
* Returns FROM clause for JSON array iteration.
* @internal
*/
getJsonArrayFromSQL(column, alias, _properties) {
return `json_each(${column}) as ${this.quoteIdentifier(alias)}`;
}
/**
* Returns SQL expression to access an element's property within a JSON array iteration.
* @internal
*/
getJsonArrayElementPropertySQL(alias, property, _type) {
return `${this.quoteIdentifier(alias)}.${this.quoteIdentifier(property)}`;
}
/**
* Wraps JSON array FROM clause and WHERE condition into a full EXISTS condition.
* MySQL overrides this because `json_table` doesn't support correlated subqueries.
* @internal
*/
getJsonArrayExistsSQL(from, where) {
return `exists (select 1 from ${from} where ${where})`;
}
/**
* Maps a runtime type name (e.g. 'string', 'number') to a driver-specific bind type constant.
* Used by NativeQueryBuilder for output bindings.
* @internal
*/
mapToBindType(type) {
return type;
}
}