"use strict"; const _ = require("lodash"); const AbstractConnectionManager = require("../abstract/connection-manager"); const { logger } = require("../../utils/logger"); const debug = logger.debugContext("connection:pg"); const sequelizeErrors = require("../../errors"); const semver = require("semver"); const dataTypes = require("../../data-types"); const momentTz = require("moment-timezone"); const { promisify } = require("util"); class ConnectionManager extends AbstractConnectionManager { constructor(dialect, sequelize) { sequelize.config.port = sequelize.config.port || 5432; super(dialect, sequelize); const pgLib = this._loadDialectModule("pg"); this.lib = this.sequelize.config.native ? pgLib.native : pgLib; this._clearDynamicOIDs(); this._clearTypeParser(); this.refreshTypeParser(dataTypes.postgres); } _refreshTypeParser(dataType) { const arrayParserBuilder = (parser2) => { return (value) => this.lib.types.arrayParser.create(value, parser2).parse(); }; const rangeParserBuilder = (parser2) => { return (value) => dataType.parse(value, { parser: parser2 }); }; if (dataType.key.toLowerCase() === "range") { for (const name in this.nameOidMap) { const entry = this.nameOidMap[name]; if (!entry.rangeOid) continue; const rangeParser = rangeParserBuilder(this.getTypeParser(entry.oid)); const arrayRangeParser = arrayParserBuilder(rangeParser); this.oidParserMap.set(entry.rangeOid, rangeParser); if (!entry.arrayRangeOid) continue; this.oidParserMap.set(entry.arrayRangeOid, arrayRangeParser); } return; } const parser = (value) => dataType.parse(value); const arrayParser = arrayParserBuilder(parser); if (dataType.key.toLowerCase() === "enum") { this.enumOids.oids.forEach((oid) => { this.oidParserMap.set(oid, parser); }); this.enumOids.arrayOids.forEach((arrayOid) => { this.oidParserMap.set(arrayOid, arrayParser); }); return; } dataType.types.postgres.forEach((name) => { if (!this.nameOidMap[name]) return; this.oidParserMap.set(this.nameOidMap[name].oid, parser); if (!this.nameOidMap[name].arrayOid) return; this.oidParserMap.set(this.nameOidMap[name].arrayOid, arrayParser); }); } _clearTypeParser() { this.oidParserMap = /* @__PURE__ */ new Map(); } getTypeParser(oid, ...args) { if (this.oidParserMap.get(oid)) return this.oidParserMap.get(oid); return this.lib.types.getTypeParser(oid, ...args); } async connect(config) { config.user = config.username; const connectionConfig = _.pick(config, [ "user", "password", "host", "database", "port" ]); connectionConfig.types = { getTypeParser: ConnectionManager.prototype.getTypeParser.bind(this) }; if (config.dialectOptions) { _.merge(connectionConfig, _.pick(config.dialectOptions, [ "application_name", "ssl", "client_encoding", "binary", "keepAlive", "statement_timeout", "query_timeout", "connectionTimeoutMillis", "idle_in_transaction_session_timeout", "lock_timeout", "options", "stream" ])); } const connection = await new Promise((resolve, reject) => { let responded = false; const connection2 = new this.lib.Client(connectionConfig); const parameterHandler = (message) => { switch (message.parameterName) { case "server_version": if (this.sequelize.options.databaseVersion === 0) { const version = semver.coerce(message.parameterValue).version; this.sequelize.options.databaseVersion = semver.valid(version) ? version : this.dialect.defaultVersion; } break; case "standard_conforming_strings": connection2["standard_conforming_strings"] = message.parameterValue; break; } }; const endHandler = () => { debug("connection timeout"); if (!responded) { reject(new sequelizeErrors.ConnectionTimedOutError(new Error("Connection timed out"))); } }; connection2.once("end", endHandler); if (!this.sequelize.config.native) { connection2.connection.on("parameterStatus", parameterHandler); } connection2.connect((err) => { responded = true; if (!this.sequelize.config.native) { connection2.connection.removeListener("parameterStatus", parameterHandler); } if (err) { if (err.code) { switch (err.code) { case "ECONNREFUSED": reject(new sequelizeErrors.ConnectionRefusedError(err)); break; case "ENOTFOUND": reject(new sequelizeErrors.HostNotFoundError(err)); break; case "EHOSTUNREACH": reject(new sequelizeErrors.HostNotReachableError(err)); break; case "EINVAL": reject(new sequelizeErrors.InvalidConnectionError(err)); break; default: reject(new sequelizeErrors.ConnectionError(err)); break; } } else { reject(new sequelizeErrors.ConnectionError(err)); } } else { debug("connection acquired"); connection2.removeListener("end", endHandler); resolve(connection2); } }); }); connection.on("error", (error) => { connection._invalid = true; debug(`connection error ${error.code || error.message}`); this.pool.destroy(connection); }); let query = ""; if (this.sequelize.options.standardConformingStrings !== false && connection["standard_conforming_strings"] !== "on") { query += "SET standard_conforming_strings=on;"; } if (this.sequelize.options.clientMinMessages !== void 0) { console.warn('Usage of "options.clientMinMessages" is deprecated and will be removed in v7.'); console.warn('Please use the sequelize option "dialectOptions.clientMinMessages" instead.'); } if (!(config.dialectOptions && config.dialectOptions.clientMinMessages && config.dialectOptions.clientMinMessages.toLowerCase() === "ignore" || this.sequelize.options.clientMinMessages === false)) { const clientMinMessages = config.dialectOptions && config.dialectOptions.clientMinMessages || this.sequelize.options.clientMinMessages || "warning"; query += `SET client_min_messages TO ${clientMinMessages};`; } if (!this.sequelize.config.keepDefaultTimezone) { const isZone = !!momentTz.tz.zone(this.sequelize.options.timezone); if (isZone) { query += `SET TIME ZONE '${this.sequelize.options.timezone}';`; } else { query += `SET TIME ZONE INTERVAL '${this.sequelize.options.timezone}' HOUR TO MINUTE;`; } } if (query) { await connection.query(query); } if (Object.keys(this.nameOidMap).length === 0 && this.enumOids.oids.length === 0 && this.enumOids.arrayOids.length === 0) { await this._refreshDynamicOIDs(connection); } return connection; } async disconnect(connection) { if (connection._ending) { debug("connection tried to disconnect but was already at ENDING state"); return; } return await promisify((callback) => connection.end(callback))(); } validate(connection) { return !connection._invalid && !connection._ending; } async _refreshDynamicOIDs(connection) { const databaseVersion = this.sequelize.options.databaseVersion; const supportedVersion = "8.3.0"; if ((databaseVersion && semver.gte(databaseVersion, supportedVersion)) === false) { return; } const results = await (connection || this.sequelize).query("WITH ranges AS ( SELECT pg_range.rngtypid, pg_type.typname AS rngtypname, pg_type.typarray AS rngtyparray, pg_range.rngsubtype FROM pg_range LEFT OUTER JOIN pg_type ON pg_type.oid = pg_range.rngtypid)SELECT pg_type.typname, pg_type.typtype, pg_type.oid, pg_type.typarray, ranges.rngtypname, ranges.rngtypid, ranges.rngtyparray FROM pg_type LEFT OUTER JOIN ranges ON pg_type.oid = ranges.rngsubtype WHERE (pg_type.typtype IN('b', 'e'));"); let result = Array.isArray(results) ? results.pop() : results; if (Array.isArray(result)) { if (result[0].command === "SET") { result = result.pop(); } } const newNameOidMap = {}; const newEnumOids = { oids: [], arrayOids: [] }; for (const row of result.rows) { if (row.typtype === "e") { newEnumOids.oids.push(row.oid); if (row.typarray) newEnumOids.arrayOids.push(row.typarray); continue; } newNameOidMap[row.typname] = { oid: row.oid }; if (row.typarray) newNameOidMap[row.typname].arrayOid = row.typarray; if (row.rngtypid) { newNameOidMap[row.typname].rangeOid = row.rngtypid; if (row.rngtyparray) newNameOidMap[row.typname].arrayRangeOid = row.rngtyparray; } } this.nameOidMap = newNameOidMap; this.enumOids = newEnumOids; this.refreshTypeParser(dataTypes.postgres); } _clearDynamicOIDs() { this.nameOidMap = {}; this.enumOids = { oids: [], arrayOids: [] }; } } module.exports = ConnectionManager; module.exports.ConnectionManager = ConnectionManager; module.exports.default = ConnectionManager; //# sourceMappingURL=connection-manager.js.map