UNPKG

autosql

Version:

An auto-parser of JSON into SQL.

215 lines (214 loc) 10.1 kB
"use strict"; 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;