diff --git a/packages/pg-native/lib/build-result.js b/packages/pg-native/lib/build-result.js index 9117a11ef..2a07dd279 100644 --- a/packages/pg-native/lib/build-result.js +++ b/packages/pg-native/lib/build-result.js @@ -39,28 +39,34 @@ class Result { consumeRows(pq) { const tupleCount = pq.ntuples() this.rows = new Array(tupleCount) + // all the values in one native call when libpq has it, instead of one or two calls per value + const values = typeof pq.getvalues === 'function' ? pq.getvalues() : undefined for (let i = 0; i < tupleCount; i++) { - this.rows[i] = this._arrayMode ? this.consumeRowAsArray(pq, i) : this.consumeRowAsObject(pq, i) + this.rows[i] = this._arrayMode ? this.consumeRowAsArray(pq, i, values) : this.consumeRowAsObject(pq, i, values) } } - consumeRowAsObject(pq, rowIndex) { + consumeRowAsObject(pq, rowIndex, values) { const row = { ...this._prebuiltEmptyResultObject } for (let j = 0; j < this.fields.length; j++) { - row[this.fields[j].name] = this.readValue(pq, rowIndex, j) + row[this.fields[j].name] = this.readValue(pq, rowIndex, j, values) } return row } - consumeRowAsArray(pq, rowIndex) { + consumeRowAsArray(pq, rowIndex, values) { const row = new Array(this.fields.length) for (let j = 0; j < this.fields.length; j++) { - row[j] = this.readValue(pq, rowIndex, j) + row[j] = this.readValue(pq, rowIndex, j, values) } return row } - readValue(pq, rowIndex, colIndex) { + readValue(pq, rowIndex, colIndex, values) { + if (values !== undefined) { + const value = values[rowIndex * this.fields.length + colIndex] + return value === null ? null : this._parsers[colIndex](value) + } const rawValue = pq.getvalue(rowIndex, colIndex) if (rawValue === '' && pq.getisnull(rowIndex, colIndex)) { return null diff --git a/packages/pg-native/test/build-result.js b/packages/pg-native/test/build-result.js new file mode 100644 index 000000000..af33aa893 --- /dev/null +++ b/packages/pg-native/test/build-result.js @@ -0,0 +1,52 @@ +const assert = require('assert') +const types = require('pg-types') +const buildResult = require('../lib/build-result') + +// the rows ('brian', 30) and ('', NULL) as libpq returns them +function fakeResult() { + const values = [ + ['brian', '30'], + ['', null], + ] + return { + cmdStatus: () => 'SELECT 2', + cmdTuples: () => '2', + ntuples: () => values.length, + nfields: () => 2, + fname: (col) => ['name', 'age'][col], + ftype: (col) => [25, 23][col], + getvalue: (row, col) => values[row][col] ?? '', + getisnull: (row, col) => values[row][col] === null, + getvalues: () => values.flat(), + } +} + +describe('build result', function () { + it('reads all the values in one call when libpq has getvalues', function () { + const pq = fakeResult() + pq.getvalue = pq.getisnull = () => { + throw new Error('one native call per value') + } + assert.deepStrictEqual(buildResult(pq, types, false).rows, [ + { name: 'brian', age: 30 }, + { name: '', age: null }, + ]) + assert.deepStrictEqual(buildResult(pq, types, true).rows, [ + ['brian', 30], + ['', null], + ]) + }) + + it('reads one value at a time with an older libpq', function () { + const pq = fakeResult() + delete pq.getvalues + assert.deepStrictEqual(buildResult(pq, types, false).rows, [ + { name: 'brian', age: 30 }, + { name: '', age: null }, + ]) + assert.deepStrictEqual(buildResult(pq, types, true).rows, [ + ['brian', 30], + ['', null], + ]) + }) +})