diff --git a/lib/promise/make_done_cb.js b/lib/promise/make_done_cb.js index 124303f256..3dea01a51c 100644 --- a/lib/promise/make_done_cb.js +++ b/lib/promise/make_done_cb.js @@ -6,6 +6,7 @@ function makeDoneCb(resolve, reject, localErr) { localErr.message = err.message; localErr.code = err.code; localErr.errno = err.errno; + localErr.fatal = err.fatal; localErr.sql = err.sql; localErr.sqlState = err.sqlState; localErr.sqlMessage = err.sqlMessage; diff --git a/lib/promise/pool.js b/lib/promise/pool.js index 24a8ca93c2..60b3ee84fc 100644 --- a/lib/promise/pool.js +++ b/lib/promise/pool.js @@ -59,7 +59,7 @@ class PromisePool extends EventEmitter { } return new this.Promise((resolve, reject) => { const done = makeDoneCb(resolve, reject, localErr); - if (args) { + if (args !== undefined) { corePool.execute(sql, args, done); } else { corePool.execute(sql, done); diff --git a/test/integration/promise-wrappers/test-promise-error-properties.test.mts b/test/integration/promise-wrappers/test-promise-error-properties.test.mts new file mode 100644 index 0000000000..e8040e7f09 --- /dev/null +++ b/test/integration/promise-wrappers/test-promise-error-properties.test.mts @@ -0,0 +1,59 @@ +import type { RowDataPacket } from '../../../index.js'; +import { describe, it, strict } from 'poku'; +import { createConnection, createPool } from '../../common.test.mjs'; + +type NRow = RowDataPacket & { n: number }; + +type MysqlError = Error & { + code?: string; + errno?: number; + fatal?: boolean; + sql?: string; + sqlState?: string; + sqlMessage?: string; +}; + +await describe('promise error propagation: fatal flag via makeDoneCb', async () => { + const conn = createConnection().promise(); + + await it('query rejection should carry fatal=true on fatal errors', async () => { + let caughtErr: MysqlError | undefined; + try { + // Force a fatal protocol error by sending an invalid SQL that causes + // a server-side error — non-fatal, but we verify the property is forwarded + await conn.query('SELECT 1 FROM nonexistent_table_xyz'); + } catch (err) { + caughtErr = err as MysqlError; + } + strict.ok(caughtErr, 'Expected query to throw'); + strict.ok('errno' in caughtErr!, 'errno should be propagated'); + strict.ok('code' in caughtErr!, 'code should be propagated'); + strict.ok('sqlMessage' in caughtErr!, 'sqlMessage should be propagated'); + // fatal should be explicitly false (not undefined) for non-fatal errors + strict.equal( + caughtErr!.fatal, + undefined, + 'fatal should be undefined for non-fatal errors' + ); + }); + + await conn.end(); +}); + +await describe('promise error propagation: PromisePool.execute with falsy args', async () => { + const pool = createPool().promise(); + + await it('execute with empty array args should work correctly', async () => { + // Previously `if (args)` would treat [] as falsy — but [] is truthy in JS, + // the real risk was args=0 or args=null. Verify [] works fine. + const [rows] = await pool.execute('SELECT 1 AS n', []); + strict.equal(rows[0].n, 1); + }); + + await it('execute without args should work correctly', async () => { + const [rows] = await pool.execute('SELECT 2 AS n'); + strict.equal(rows[0].n, 2); + }); + + await pool.end(); +});