pg-native: unhandled libpq result status drops the result and the server's error
Maintainer thường phản hồi trong vòng 1 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 58/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Khá rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Công nghệ
- javascript, node.js, postgresql
Hướng nghiên cứu
Bắt đầu bằng cách tái hiện trường hợp COPY … TO STDOUT được cung cấp với các client native và pure-JS. Đọc packages/pg-native/index.js quanh _emitResult, _read và _onReadyForQuery, sau đó đọc packages/pg/lib/native/query.js quanh after(). Hoàn thành khi một trạng thái libpq chưa được xử lý không còn tạo ra kết quả thành công undefined và lỗi máy chủ đến được callback của query.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Disclaimer
This is a real issue we saw in our production environment via sequelize v6, we used claude + 2 agents to generate this report, I saw no AI/slop policy, feel free to do whatever you feel like based on this.
Summary
client.query(sql, cb) on the native client can call cb(null, undefined) — no error, no result — so anything that reads res.rows throws. The pure-JS client returns a Result for the same query.
Versions: pg 8.23.0, pg-native 3.9.0, libpq 1.11.0, Node 26.7.0. Reproduced on PostgreSQL 17.11 and CockroachDB 26.2.5 with identical output. Line numbers below are from master; the published 3.9.0 tarball is offset by ~17 lines around _onReadyForQuery.
Repro
COPY … TO STDOUT through client.query() hits it every time, no patching needed. This runs the same query through both drivers:
const CONN = process.argv[2] || 'postgres://postgres@localhost:5432/postgres';
const SQL = 'COPY (SELECT 1) TO STDOUT';
const native = require('pg').native;
const { Client: JsClient } = require('pg');
function run(Client, label) {
return new Promise((resolve) => {
const client = new Client(CONN);
client.connect((connErr) => {
if (connErr) return resolve(console.log(`[${label}] connect failed: ${connErr.message}`));
client.query(SQL, (err, res) => {
console.log(`[${label}] err = ${err && err.message}`);
console.log(`[${label}] res = ${res && res.constructor.name}`);
try {
console.log(`[${label}] res.rows = ${JSON.stringify(res.rows)}`);
} catch (e) {
console.log(`[${label}] res.rows THREW ${e.constructor.name}: ${e.message}`);
}
client.end(() => resolve());
});
});
});
}
(async () => {
await run(JsClient, 'js ');
await run(native.Client, 'native');
})();
[js ] err = null
[js ] res = Result
[js ] res.rows = []
[native] err = null
[native] res = undefined
[native] res.rows THREW TypeError: Cannot read properties of undefined (reading 'rows')
What's happening
_emitResult (packages/pg-native/index.js:181) emits a result event for three statuses only — PGRES_TUPLES_OK, PGRES_COMMAND_OK, PGRES_EMPTY_QUERY. COPY_OUT/COPY_BOTH break out without one, and the default: branch sends the error to the client through _readError rather than to the query. Either way _read reaches emit('readyForQuery') (:248) with _resultCount === 0, and _onReadyForQuery (:490) passes that straight through:
const results = this._results // undefined: no 'result' event fired
cb(err, rows || [], results)
pg's NativeQuery then reads the falsy err as success and calls back with undefined (packages/pg/lib/native/query.js:121).
The case that actually bit us
COPY is just the convenient reproduction. The default: branch is the damaging one, because the server's error goes to the client's error event and never reaches the query — so you lose the result and the explanation. We spent a while on a Sequelize crash that looked like this:
TypeError: Cannot read properties of undefined (reading 'rows')
at Query.run (sequelize/lib/dialects/postgres/query.js:62)
The transaction was left aborted, every later statement failed with current transaction is aborted, commands ignored until end of transaction block, and the error explaining why was gone. On the JS driver the same server condition arrives as an ordinary error.
Possible fixes
- In
_onReadyForQuery, when there's no result and no_queryError, call back with an error instead ofundefined. - In the
default:branch, set_queryErrorso the server's message reaches the caller. - Or defensively in
pg'safter()(packages/pg/lib/native/query.js:121): treat a missingresultsas an error.
Happy to open a PR if one of those looks like the right shape.
Possibly related, and I can file it separately: at packages/pg-native/index.js:214, if pq.consumeInput() returns false, _read calls _readError() and returns before emit('readyForQuery') — the in-flight query's callback never fires and the promise hangs. I saw this by cancelling a running statement server-side (on CockroachDB; haven't rechecked on PostgreSQL).
- Ngôn ngữ chính
- JavaScript
- Star
- 13.2k
- Fork
- 1.4k
- Merge trung bình
- 3 ngày 20 giờ
- Pull request đã merge (30 ngày)
- 6
Chuẩn bị môi trường
Khởi chạy dev container của dự án ngay trên trình duyệt, bằng tài khoản GitHub của bạn.
- Không có Dockerfile hay tệp Docker Compose
- Không có mẫu pull request
- Không có hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của brianc/node-postgres
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
brianc/node-postgres#3770 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
brianc/node-postgres#3716 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
# remove bring your own promiseĐang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
brianc/node-postgres#3631 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 1/5 1-3 giờ Mức phù hợp với người mới 62/100
brianc/node-postgres#2857 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
can't view docs for old versionsĐang mở
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 68/100
brianc/node-postgres#2433 ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của brianc/node-postgres
Issue tương tự
-
bug CI breakage triage needed
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
oppia/oppia#27517 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
draftomen enhancement size: S
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
andreagrandi/draftomen#761 ·
-
Remove the stale .eslintignoreĐang mở
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 92/100
HarperFast/harper#2866 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 84/100
HarperFast/harper-pro#927 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 88/100
anthropics/skills#1897 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày