From b37034cb4f2bff93b88b4b9e1f17748de7547870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Eiras?= Date: Sat, 1 Jan 2022 22:06:07 +0100 Subject: [PATCH] feat(query): added options.rawErrors to query() method This option causes all errors from the underlying connection/database library to be propagated unformatted and unmodified. Use this for lesser common errors sequelize can't parse or in case you perfer to see the full raw errors. --- lib/dialects/abstract/query.js | 23 +++++++++++++++++++++++ lib/sequelize.js | 1 + test/integration/sequelize/query.test.js | 11 +++++++++++ 3 files changed, 35 insertions(+) diff --git a/lib/dialects/abstract/query.js b/lib/dialects/abstract/query.js index 6aa63ba55629..1f3c0bf4ec18 100644 --- a/lib/dialects/abstract/query.js +++ b/lib/dialects/abstract/query.js @@ -23,6 +23,14 @@ class AbstractQuery { ...options, }; this.checkLoggingOption(); + + if (options.rawErrors) { + // The default implementation in AbstractQuery just returns the same + // error object. By overidding this.formatError, this saves every dialect + // having to check for options.rawErrors in their own formatError + // implementations. + this.formatError = AbstractQuery.prototype.formatError; + } } /** @@ -109,6 +117,21 @@ class AbstractQuery { return [sql, []]; } + /** + * Formats a raw database error from the database library into a common Sequelize exception. + * + * @param {Error} error The exception object. + * @param {object} errStack The stack trace that started the database query. + * @returns {BaseError} the new formatted error object. + */ + formatError(error, errStack) { + // Default implementation, no formatting. + // Each dialect overrides this method to parse errors from their respective the database engines. + error.stack = errStack; + + return error; + } + /** * Execute the passed sql query. * diff --git a/lib/sequelize.js b/lib/sequelize.js index c8ad42189220..8a2608ba9b0d 100644 --- a/lib/sequelize.js +++ b/lib/sequelize.js @@ -518,6 +518,7 @@ class Sequelize { * @param {boolean} [options.supportsSearchPath] If false do not prepend the query with the search_path (Postgres only) * @param {boolean} [options.mapToModel=false] Map returned fields to model's fields if `options.model` or `options.instance` is present. Mapping will occur before building the model instance. * @param {object} [options.fieldMap] Map returned fields to arbitrary names for `SELECT` query type. + * @param {boolean} [options.rawErrors=false] Set to `true` to cause errors coming from the underlying connection/database library to be propagated unmodified and unformatted. Else, the default behavior (=true) is to reinterpret errors as sequelize.errors.BaseError objects. * * @returns {Promise} * diff --git a/test/integration/sequelize/query.test.js b/test/integration/sequelize/query.test.js index 79ab8ffe6562..9cb6199d1b75 100644 --- a/test/integration/sequelize/query.test.js +++ b/test/integration/sequelize/query.test.js @@ -365,6 +365,17 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { await this.UserVisit.sync({ force: true }); }); + it('emits raw errors if requested', async function () { + const sql = 'SELECT 1 FROM NotFoundTable'; + + await expect(this.sequelize.query(sql, { rawErrors: false })) + .to.eventually.be.rejectedWith(DatabaseError); + + await expect(this.sequelize.query(sql, { rawErrors: true })) + .to.eventually.be.rejected + .and.not.be.an.instanceOf(DatabaseError); + }); + it('emits full stacktraces for generic database error', async function () { let error = null; try {