From 106ca8a1786dd9a3b6c790b6643f235be69f9106 Mon Sep 17 00:00:00 2001 From: Sehrope Sarkuni Date: Thu, 14 Sep 2023 15:43:14 -0400 Subject: [PATCH 001/149] Fix get value of last column with same name in result rows (#3063) * Add failing test for result rows with the same column names * Fix handling of duplicate column names in results to ensure last value is populated Fixes handling of result rows that have the same column name duplicated in the results to ensure that the last value is the one returned to the user. This was the old behavior but unintentionally broken when the pre-built object optimization was added. --- packages/pg/lib/result.js | 2 ++ .../test/integration/gh-issues/3062-tests.js | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 packages/pg/test/integration/gh-issues/3062-tests.js diff --git a/packages/pg/lib/result.js b/packages/pg/lib/result.js index 187c0d016..f9053c7a9 100644 --- a/packages/pg/lib/result.js +++ b/packages/pg/lib/result.js @@ -67,6 +67,8 @@ class Result { var field = this.fields[i].name if (rawValue !== null) { row[field] = this._parsers[i](rawValue) + } else { + row[field] = null } } return row diff --git a/packages/pg/test/integration/gh-issues/3062-tests.js b/packages/pg/test/integration/gh-issues/3062-tests.js new file mode 100644 index 000000000..7666751ed --- /dev/null +++ b/packages/pg/test/integration/gh-issues/3062-tests.js @@ -0,0 +1,21 @@ +'use strict' +const helper = require('../test-helper') +var assert = require('assert') +const suite = new helper.Suite() + +// https://github.com/brianc/node-postgres/issues/3062 +suite.testAsync('result fields with the same name should pick the last value', async () => { + const client = new helper.pg.Client() + await client.connect() + + const { rows: [shouldBeNullRow] } = await client.query('SELECT NULL AS test, 10 AS test, NULL AS test') + assert.equal(shouldBeNullRow.test, null) + + const { rows: [shouldBeTwelveRow] } = await client.query('SELECT NULL AS test, 10 AS test, 12 AS test') + assert.equal(shouldBeTwelveRow.test, 12) + + const { rows: [shouldBeAbcRow] } = await client.query(`SELECT NULL AS test, 10 AS test, 12 AS test, 'ABC' AS test`) + assert.equal(shouldBeAbcRow.test, 'ABC') + + await client.end() +}) From da0f5c5eb251853c5f99939433bbed33451c5989 Mon Sep 17 00:00:00 2001 From: Brian C Date: Fri, 15 Sep 2023 16:21:45 -0500 Subject: [PATCH 002/149] Remove 1 loop on rowDescription event (#3056) * Remove 1 loop on rowDescription event * Update packages/pg/lib/result.js Co-authored-by: Charmander <~@charmander.me> --------- Co-authored-by: Charmander <~@charmander.me> --- packages/pg/lib/result.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/pg/lib/result.js b/packages/pg/lib/result.js index f9053c7a9..98018a7d8 100644 --- a/packages/pg/lib/result.js +++ b/packages/pg/lib/result.js @@ -61,7 +61,7 @@ class Result { } parseRow(rowData) { - var row = { ... this._prebuiltEmptyResultObject } + var row = { ...this._prebuiltEmptyResultObject } for (var i = 0, len = rowData.length; i < len; i++) { var rawValue = rowData[i] var field = this.fields[i].name @@ -87,22 +87,20 @@ class Result { if (this.fields.length) { this._parsers = new Array(fieldDescriptions.length) } + + var row = {} + for (var i = 0; i < fieldDescriptions.length; i++) { var desc = fieldDescriptions[i] + row[desc.name] = null + if (this._types) { this._parsers[i] = this._types.getTypeParser(desc.dataTypeID, desc.format || 'text') } else { this._parsers[i] = types.getTypeParser(desc.dataTypeID, desc.format || 'text') } } - this._createPrebuiltEmptyResultObject() - } - _createPrebuiltEmptyResultObject() { - var row = {} - for (var i = 0; i < this.fields.length; i++) { - row[this.fields[i].name] = null - } - this._prebuiltEmptyResultObject = { ... row } + this._prebuiltEmptyResultObject = { ...row } } } From d21cc09556899b8038ec23613a801c19228637ca Mon Sep 17 00:00:00 2001 From: Sebastien Stettler Date: Fri, 15 Sep 2023 14:22:18 -0700 Subject: [PATCH 003/149] fix: conflict between browser URL object and Node URL object (#3061) I am running this package using electron, what i noticed was that due to the fact that the lines between node and browser environments become a bit blurred, the URL class that was being used was the one defined by the browser and not node. By making an explicit require it ensures the correct Class is used. While creating a test for this would be difficuilt i think adding an eslint rule to stop using globally defined objects and require imports instead would resolve issues like this in the future --- packages/pg-connection-string/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pg-connection-string/index.js b/packages/pg-connection-string/index.js index c7fc72a36..a005ffa20 100644 --- a/packages/pg-connection-string/index.js +++ b/packages/pg-connection-string/index.js @@ -1,9 +1,9 @@ 'use strict' - //Parse method copied from https://github.com/brianc/node-postgres //Copyright (c) 2010-2014 Brian Carlson (brian.m.carlson@gmail.com) //MIT License +const { URL } = require('url') //parses a connection string function parse(str) { //unix socket From b1a8947738ce0af004cb926f79829bb2abc64aa6 Mon Sep 17 00:00:00 2001 From: Romain Gilliotte Date: Fri, 15 Sep 2023 23:23:05 +0200 Subject: [PATCH 004/149] Fail gracefully when connecting to other database (#3026) * Fail gracefully when connecting to other SGDB vendor * Make test more flexible. Adjust error wording to match native better. --------- Co-authored-by: Brian Carlson --- packages/pg-protocol/src/parser.ts | 2 +- .../test/integration/gh-issues/2627-tests.js | 63 +++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 packages/pg/test/integration/gh-issues/2627-tests.js diff --git a/packages/pg-protocol/src/parser.ts b/packages/pg-protocol/src/parser.ts index f900193d7..1ad4e3abd 100644 --- a/packages/pg-protocol/src/parser.ts +++ b/packages/pg-protocol/src/parser.ts @@ -199,7 +199,7 @@ export class Parser { case MessageCodes.CopyData: return this.parseCopyData(offset, length, bytes) default: - assert.fail(`unknown message code: ${code.toString(16)}`) + return new DatabaseError('received invalid response: ' + code.toString(16), length, 'error') } } diff --git a/packages/pg/test/integration/gh-issues/2627-tests.js b/packages/pg/test/integration/gh-issues/2627-tests.js new file mode 100644 index 000000000..83e09b910 --- /dev/null +++ b/packages/pg/test/integration/gh-issues/2627-tests.js @@ -0,0 +1,63 @@ +'use strict' +const net = require('net') +const helper = require('./../test-helper') + +const suite = new helper.Suite() + +const options = { + host: 'localhost', + port: Math.floor(Math.random() * 2000) + 2000, + connectionTimeoutMillis: 2000, + user: 'not', + database: 'existing', +} + +// This is the content of the packets sent by a MySQL server during the handshake. +// Those were captured with the `mysql:8.0.33` docker image. +const MySqlHandshake = Buffer.from( + 'SgAAAAo4LjAuMjgAHwAAAB4dKyUJZ2p6AP///wIA/98VAAAAAAAAAAAA' + + 'AAo1YiNJajgKKGkpfgBjYWNoaW5nX3NoYTJfcGFzc3dvcmQAIQAAAf+EBC' + + 'MwOFMwMUdvdCBwYWNrZXRzIG91dCBvZiBvcmRlcg==', + 'base64' +) + +const serverWithInvalidResponse = (port, callback) => { + const sockets = new Set() + + const server = net.createServer((socket) => { + socket.write(MySqlHandshake) + + // This server sends an invalid response which should throw in pg-protocol + sockets.add(socket) + }) + + let closing = false + const closeServer = (done) => { + if (closing) return + closing = true + + server.close(done) + for (const socket of sockets) { + socket.destroy() + } + } + + server.listen(port, options.host, () => callback(closeServer)) +} + +suite.test('client should fail to connect', (done) => { + serverWithInvalidResponse(options.port, (closeServer) => { + const client = new helper.Client(options) + + client + .connect() + .then(() => { + done(new Error('Expected client.connect() to fail')) + }) + .catch((err) => { + assert(err) + assert(err.message.includes('invalid response')) + closeServer(done) + }) + }) +}) From b9a528cb3b62aa7de86a35467f8d3bbfb5457a56 Mon Sep 17 00:00:00 2001 From: Alex Anderson <191496+alxndrsn@users.noreply.github.com> Date: Thu, 19 Oct 2023 18:27:11 +0300 Subject: [PATCH 005/149] eslintrc: update prettier base config (#3077) Co-authored-by: alxndrsn --- .eslintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintrc b/.eslintrc index 4766b9889..bb77004ec 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,7 +1,7 @@ { "plugins": ["prettier"], "parser": "@typescript-eslint/parser", - "extends": ["plugin:prettier/recommended", "prettier/@typescript-eslint"], + "extends": ["plugin:prettier/recommended", "prettier"], "ignorePatterns": ["node_modules", "coverage", "packages/pg-protocol/dist/**/*", "packages/pg-query-stream/dist/**/*"], "parserOptions": { "ecmaVersion": 2017, From 16322c2d508d66c5a012b6e0ba2bf0e92c481966 Mon Sep 17 00:00:00 2001 From: Alex Anderson <191496+alxndrsn@users.noreply.github.com> Date: Thu, 19 Oct 2023 18:27:25 +0300 Subject: [PATCH 006/149] Fix eslint violations (#3078) Co-authored-by: alxndrsn --- docs/theme.config.js | 5 ++- packages/pg-cursor/test/error-handling.js | 21 ++++++++++-- packages/pg-pool/index.js | 6 ++-- packages/pg-pool/test/idle-timeout-exit.js | 6 +++- packages/pg/bench.js | 3 +- packages/pg/lib/client.js | 6 ++-- packages/pg/lib/crypto/utils.js | 2 +- packages/pg/lib/native/client.js | 6 ++-- packages/pg/lib/query.js | 3 +- .../client/async-stack-trace-tests.js | 32 +++++++++---------- .../integration/client/sasl-scram-tests.js | 2 +- .../test/integration/gh-issues/3062-tests.js | 12 +++++-- .../test/integration/gh-issues/787-tests.js | 3 +- packages/pg/test/unit/client/escape-tests.js | 6 ++-- packages/pg/test/unit/utils-tests.js | 30 ++++++++++++++--- 15 files changed, 93 insertions(+), 50 deletions(-) diff --git a/docs/theme.config.js b/docs/theme.config.js index 00410f791..02e5327c3 100644 --- a/docs/theme.config.js +++ b/docs/theme.config.js @@ -30,7 +30,10 @@ export default { head: ( <> - +