UNPKG

autosql

Version:

An auto-parser of JSON into SQL.

228 lines (227 loc) 10.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PostgresDatabase = void 0; const database_1 = require("./database"); const pgsql_1 = require("./permanentErrors/pgsql"); const types_1 = require("../config/types"); const pgsqlConfig_1 = require("./config/pgsqlConfig"); const validateQuery_1 = require("./utils/validateQuery"); const metadata_1 = require("../helpers/metadata"); const tableBuilder_1 = require("./queryBuilders/pgsql/tableBuilder"); const indexBuilder_1 = require("./queryBuilders/pgsql/indexBuilder"); const insertBuilder_1 = require("./queryBuilders/pgsql/insertBuilder"); const autosql_1 = require("./autosql"); const utilities_1 = require("../helpers/utilities"); const dialectConfig = pgsqlConfig_1.pgsqlConfig; class PostgresDatabase extends database_1.Database { constructor(config) { super(config); this.autoSQLHandler = new autosql_1.AutoSQLHandler(this); } async establishDatabaseConnection() { let Pg; try { Pg = require("pg"); } catch (err) { throw new Error("Missing required dependency 'pg'. Please install it to use PgDatabase."); } this.connection = new Pg.Pool({ host: this.config.host, user: this.config.user, password: this.config.password, database: this.config.database, port: this.config.port || 5432, max: 5, stream: this.config.sshStream ? () => this.config.sshStream : undefined }); } getMaxConnections() { return this.connection?.options?.max ?? 5; } getDialectConfig() { return dialectConfig; } async getPermanentErrors() { return pgsql_1.pgsqlPermanentErrors; } 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.connect(); if (query.trim().toLowerCase().startsWith("select") || query.trim().toLowerCase().startsWith("insert") || query.trim().toLowerCase().startsWith("update") || query.trim().toLowerCase().startsWith("delete")) { // Use PREPARE for DML queries await client.query(`PREPARE validate_stmt AS ${query}`); await client.query(`DEALLOCATE validate_stmt`); // Cleanup } else { // Use a transaction with ROLLBACK for DDL queries (CREATE TABLE, ALTER TABLE) await client.query("BEGIN;"); await client.query(query); await client.query("ROLLBACK;"); // Prevent execution } return { success: true }; } catch (error) { if (client) await client.query("ROLLBACK;"); console.error("PostgreSQL testQuery failed:", error); throw error; } finally { if (client) client.release(); } } async executeQuery(queryOrParams) { if (!this.connection) { await this.establishConnection(); } const query = typeof queryOrParams === "string" ? queryOrParams : queryOrParams.query; const params = typeof queryOrParams === "string" ? [] : queryOrParams.params || []; let client = null; try { client = await this.connection.connect(); const result = await client.query(query, params); const rows = result.rows || []; const affectedRows = result.rowCount ?? rows.length ?? 0; 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.PostgresTableQueryBuilder.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)); queries.push({ query: "COMMIT;", params: [] }); queries.push({ query: "BEGIN;", params: [] }); } // ✅ Only fetch unique indexes if there are columns to remove uniqueness from let indexesToDrop = []; if (alterTableChanges.noLongerUnique.length > 0) { // Extract the results from the QueryResult response 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 IF EXISTS "${index_name}"`); if (indexesToDrop.length > 0) { queries.push({ query: indexesToDrop.join("; ") + ";", params: [] }); } } // Get actual ALTER TABLE queries const alterQueries = tableBuilder_1.PostgresTableQueryBuilder.getAlterTableQuery(table, alterTableChanges, this.config.schema, this.getConfig()); queries.push(...alterQueries); // Add New Primary Key (if changed and allowed) if (alterTableChanges.primaryKeyChanges.length > 0 && alterPrimaryKey) { queries.push({ query: "COMMIT;", params: [] }); queries.push({ query: "BEGIN;", params: [] }); queries.push(this.getAddPrimaryKeyQuery(table, alterTableChanges.primaryKeyChanges)); } return queries; } getDropTableQuery(table) { return tableBuilder_1.PostgresTableQueryBuilder.getDropTableQuery(table, this.config.schema); } getTableExistsQuery(schema, table) { return tableBuilder_1.PostgresTableQueryBuilder.getTableExistsQuery(schema, table); } getTableMetaDataQuery(schema, table) { return tableBuilder_1.PostgresTableQueryBuilder.getTableMetaDataQuery(schema, table); } getPrimaryKeysQuery(table) { return indexBuilder_1.PostgresIndexQueryBuilder.getPrimaryKeysQuery(table, this.config.schema); } getForeignKeyConstraintsQuery(table) { return indexBuilder_1.PostgresIndexQueryBuilder.getForeignKeyConstraintsQuery(table, this.config.schema); } getViewDependenciesQuery(table) { return indexBuilder_1.PostgresIndexQueryBuilder.getViewDependenciesQuery(table, this.config.schema); } getDropPrimaryKeyQuery(table) { return indexBuilder_1.PostgresIndexQueryBuilder.getDropPrimaryKeyQuery(table, this.config.schema); } getDropUniqueConstraintQuery(table, indexName) { return indexBuilder_1.PostgresIndexQueryBuilder.getDropUniqueConstraintQuery(table, indexName, this.config.schema); } getAddPrimaryKeyQuery(table, primaryKeys) { return indexBuilder_1.PostgresIndexQueryBuilder.getAddPrimaryKeyQuery(table, primaryKeys, this.config.schema); } getUniqueIndexesQuery(table, column_name) { return indexBuilder_1.PostgresIndexQueryBuilder.getUniqueIndexesQuery(table, column_name, this.config.schema); } getSplitTablesQuery(table) { return tableBuilder_1.PostgresTableQueryBuilder.getSplitTablesQuery(table, this.config.schema); } getInsertStatementQuery(tableOrInput, data, metaData, insertInput) { return insertBuilder_1.PostgresInsertQueryBuilder.getInsertStatementQuery(tableOrInput, data, metaData, this.getConfig(), insertInput); } getInsertFromStagingQuery(tableOrInput, metaData, insertInput) { return insertBuilder_1.PostgresInsertQueryBuilder.getInsertFromStagingQuery(tableOrInput, metaData, this.getConfig(), insertInput); } getInsertChangedRowsToHistoryQuery(tableOrInput, metaData) { return insertBuilder_1.PostgresInsertQueryBuilder.getInsertChangedRowsToHistoryQuery(tableOrInput, metaData, this.getConfig()); } getCreateTempTableQuery(table) { return tableBuilder_1.PostgresTableQueryBuilder.getCreateTempTableQuery(table, this.config.schema); } getConstraintConflictQuery(table, structure) { return indexBuilder_1.PostgresIndexQueryBuilder.generateConstraintConflictBreakdownQuery(table, structure, this.config.schema); } } exports.PostgresDatabase = PostgresDatabase;