autosql
Version:
An auto-parser of JSON into SQL.
228 lines (227 loc) • 10.8 kB
JavaScript
;
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;