autosql
Version:
An auto-parser of JSON into SQL.
215 lines (214 loc) • 10.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MySQLDatabase = void 0;
const database_1 = require("./database");
const mysql_1 = require("./permanentErrors/mysql");
const types_1 = require("../config/types");
const mysqlConfig_1 = require("./config/mysqlConfig");
const validateQuery_1 = require("./utils/validateQuery");
const metadata_1 = require("../helpers/metadata");
const tableBuilder_1 = require("./queryBuilders/mysql/tableBuilder");
const indexBuilder_1 = require("./queryBuilders/mysql/indexBuilder");
const insertBuilder_1 = require("./queryBuilders/mysql/insertBuilder");
const autosql_1 = require("./autosql");
const utilities_1 = require("../helpers/utilities");
const dialectConfig = mysqlConfig_1.mysqlConfig;
class MySQLDatabase extends database_1.Database {
constructor(config) {
super(config);
this.autoSQLHandler = new autosql_1.AutoSQLHandler(this);
}
async establishDatabaseConnection() {
let mysql;
try {
mysql = require("mysql2/promise");
}
catch (err) {
throw new Error("Missing required dependency 'mysql2'. Please install it to use MySQLDatabase.");
}
this.connection = mysql.createPool({
host: this.config.host,
user: this.config.user,
password: this.config.password,
database: this.config.database || this.config.schema,
port: this.config.port || 3306,
connectionLimit: 5,
...(this.config.sshStream ? { stream: this.config.sshStream } : {})
});
}
getMaxConnections() {
return this.connection?.config?.connectionLimit ?? 5;
}
getDialectConfig() {
return dialectConfig;
}
async getPermanentErrors() {
return mysql_1.mysqlPermanentErrors;
}
async testQuery(queryOrParams) {
const query = typeof queryOrParams === "string" ? queryOrParams : queryOrParams.query;
if (!(0, validateQuery_1.isValidSingleQuery)(query)) {
throw new Error("Each query in the transaction must be a single statement.");
}
if (!this.connection) {
await this.establishConnection();
}
let client = null;
try {
client = await this.connection.getConnection();
// Use PREPARE to validate syntax without executing
await client.query(`PREPARE stmt FROM ?`, [query]);
await client.query(`DEALLOCATE PREPARE stmt`); // Cleanup
return { success: true };
}
catch (error) {
if (client)
await client.query("ROLLBACK;");
console.error("MySQL testQuery failed:", error);
throw error;
}
finally {
if (client)
client.release();
}
}
async executeQuery(queryOrParams) {
if (!this.connection) {
await this.establishConnection();
}
let client = null;
const query = typeof queryOrParams === "string" ? queryOrParams : queryOrParams.query;
const params = typeof queryOrParams === "string" ? [] : queryOrParams.params || [];
try {
client = await this.connection.getConnection();
const [rowsOrResult, maybeHeader] = await client.query(query, params);
const rows = Array.isArray(rowsOrResult) ? rowsOrResult : [];
let affectedRows = 0;
if (maybeHeader && typeof maybeHeader === 'object' && 'affectedRows' in maybeHeader) {
affectedRows = maybeHeader.affectedRows;
}
else if (rows.length > 0) {
affectedRows = rows.length;
}
return { rows, affectedRows };
}
catch (error) {
if (client)
await client.query("ROLLBACK;");
throw error;
}
finally {
if (client)
client.release();
}
}
getCreateSchemaQuery(schemaName) {
return { query: `CREATE SCHEMA IF NOT EXISTS \`${schemaName}\`;` };
}
getCheckSchemaQuery(schemaName) {
if (Array.isArray(schemaName)) {
return { query: `SELECT ${schemaName
.map((db) => `(CASE WHEN EXISTS (SELECT NULL FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '${db}') THEN 1 ELSE 0 END) AS '${db}'`)
.join(", ")};` };
}
return { query: `SELECT (CASE WHEN EXISTS (SELECT NULL FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '${schemaName}') THEN 1 ELSE 0 END) AS '${schemaName}';` };
}
getCreateTableQuery(table, headers) {
return tableBuilder_1.MySQLTableQueryBuilder.getCreateTableQuery(table, headers, this.config);
}
async getAlterTableQuery(table, alterTableChangesOrOldHeaders, newHeaders) {
let alterTableChanges;
let updatedMetaData;
const alterPrimaryKey = this.config.updatePrimaryKey ?? false;
if ((0, types_1.isMetadataHeader)(alterTableChangesOrOldHeaders)) {
// If old headers are provided in MetadataHeader format, compare them with newHeaders
if (!newHeaders) {
throw new Error("Missing new headers for ALTER TABLE query");
}
({ changes: alterTableChanges, updatedMetaData } = (0, metadata_1.compareMetaData)(alterTableChangesOrOldHeaders, newHeaders, this.getDialectConfig()));
this.updateTableMetadata(table, updatedMetaData, "metaData");
}
else {
alterTableChanges = alterTableChangesOrOldHeaders;
}
const queries = [];
const schemaPrefix = this.config.schema ? `\`${this.config.schema}\`.` : "";
if (alterTableChanges.primaryKeyChanges.length > 0 && alterPrimaryKey) {
queries.push(this.getDropPrimaryKeyQuery(table));
}
let indexesToDrop = [];
if (alterTableChanges.noLongerUnique.length > 0) {
const uniqueIndexesResult = await this.runQuery(this.getUniqueIndexesQuery(table));
if (!uniqueIndexesResult.success || !uniqueIndexesResult.results) {
throw new Error(`Failed to fetch unique indexes for table ${table}: ${uniqueIndexesResult.error}`);
}
const uniqueIndexes = (uniqueIndexesResult.results || [])
.map(row => (0, utilities_1.normalizeResultKeys)(row))
.filter(row => row.columns);
const indexesToDrop = uniqueIndexes
.filter(({ columns }) => columns.split(", ").some((col) => alterTableChanges.noLongerUnique.includes(col)))
.map(({ index_name }) => `DROP INDEX \`${index_name}\``);
if (indexesToDrop.length > 0) {
queries.push({
query: `ALTER TABLE ${schemaPrefix}\`${table}\` ${indexesToDrop.join(", ")};`,
params: []
});
}
}
const alterQueries = tableBuilder_1.MySQLTableQueryBuilder.getAlterTableQuery(table, alterTableChanges, this.config.schema, this.getConfig());
queries.push(...alterQueries);
if (alterTableChanges.primaryKeyChanges.length > 0 && alterPrimaryKey) {
queries.push(this.getAddPrimaryKeyQuery(table, alterTableChanges.primaryKeyChanges));
}
return queries;
}
getDropTableQuery(table) {
return tableBuilder_1.MySQLTableQueryBuilder.getDropTableQuery(table, this.config.schema);
}
getTableExistsQuery(schema, table) {
return tableBuilder_1.MySQLTableQueryBuilder.getTableExistsQuery(schema, table);
}
getTableMetaDataQuery(schema, table) {
return tableBuilder_1.MySQLTableQueryBuilder.getTableMetaDataQuery(schema, table);
}
getPrimaryKeysQuery(table) {
return indexBuilder_1.MySQLIndexQueryBuilder.getPrimaryKeysQuery(table, this.config.schema);
}
getForeignKeyConstraintsQuery(table) {
return indexBuilder_1.MySQLIndexQueryBuilder.getForeignKeyConstraintsQuery(table, this.config.schema);
}
getViewDependenciesQuery(table) {
return indexBuilder_1.MySQLIndexQueryBuilder.getViewDependenciesQuery(table, this.config.schema);
}
getDropPrimaryKeyQuery(table) {
return indexBuilder_1.MySQLIndexQueryBuilder.getDropPrimaryKeyQuery(table, this.config.schema);
}
getDropUniqueConstraintQuery(table, indexName) {
return indexBuilder_1.MySQLIndexQueryBuilder.getDropUniqueConstraintQuery(table, indexName, this.config.schema);
}
getAddPrimaryKeyQuery(table, primaryKeys) {
return indexBuilder_1.MySQLIndexQueryBuilder.getAddPrimaryKeyQuery(table, primaryKeys, this.config.schema);
}
getUniqueIndexesQuery(table, column_name) {
return indexBuilder_1.MySQLIndexQueryBuilder.getUniqueIndexesQuery(table, column_name, this.config.schema);
}
getSplitTablesQuery(table) {
return tableBuilder_1.MySQLTableQueryBuilder.getSplitTablesQuery(table, this.config.schema);
}
getInsertStatementQuery(tableOrInput, data, metaData, insertInput) {
return insertBuilder_1.MySQLInsertQueryBuilder.getInsertStatementQuery(tableOrInput, data, metaData, this.getConfig(), insertInput);
}
getInsertFromStagingQuery(tableOrInput, metaData, insertInput) {
return insertBuilder_1.MySQLInsertQueryBuilder.getInsertFromStagingQuery(tableOrInput, metaData, this.getConfig(), insertInput);
}
getInsertChangedRowsToHistoryQuery(tableOrInput, metaData) {
return insertBuilder_1.MySQLInsertQueryBuilder.getInsertChangedRowsToHistoryQuery(tableOrInput, metaData, this.getConfig());
}
getCreateTempTableQuery(table) {
return tableBuilder_1.MySQLTableQueryBuilder.getCreateTempTableQuery(table, this.config.schema);
}
getConstraintConflictQuery(table, structure) {
return indexBuilder_1.MySQLIndexQueryBuilder.generateConstraintConflictBreakdownQuery(table, structure, this.config.schema);
}
}
exports.MySQLDatabase = MySQLDatabase;