UNPKG

sequelize

Version:

Sequelize is a promise-based Node.js ORM tool for Postgres, MySQL, MariaDB, SQLite, Microsoft SQL Server, Amazon Redshift and Snowflake’s Data Cloud. It features solid transaction support, relations, eager and lazy loading, read replication and more.

245 lines (244 loc) 9.68 kB
"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