Skip to content

Multiple result sets #1368

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions lib/native/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
var EventEmitter = require('events').EventEmitter
var util = require('util')
var utils = require('../utils')
var NativeResult = require('./result')

var NativeQuery = module.exports = function (config, values, callback) {
EventEmitter.call(this)
Expand Down Expand Up @@ -91,7 +90,7 @@ NativeQuery.prototype.submit = function (client) {
this.native = client.native
client.native.arrayMode = this._arrayMode

var after = function (err, rows) {
var after = function (err, rows, results) {
client.native.arrayMode = false
setImmediate(function () {
self.emit('_done')
Expand All @@ -102,22 +101,26 @@ NativeQuery.prototype.submit = function (client) {
return self.handleError(err)
}

var result = new NativeResult()
result.addCommandComplete(self.native.pq)
result.rows = rows

// emit row events for each row in the result
if (self._emitRowEvents) {
rows.forEach(function (row) {
self.emit('row', row, result)
})
if (results.length > 1) {
rows.forEach((rowOfRows, i) => {
rowOfRows.forEach(row => {
self.emit('row', row, results[i])
})
})
} else {
rows.forEach(function (row) {
self.emit('row', row, results)
})
}
}

// handle successful result
self.state = 'end'
self.emit('end', result)
self.emit('end', results)
if (self.callback) {
self.callback(null, result)
self.callback(null, results)
}
}

Expand Down
37 changes: 0 additions & 37 deletions lib/native/result.js

This file was deleted.

25 changes: 22 additions & 3 deletions lib/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,14 @@ var Query = function (config, values, callback) {
// use unique portal name each time
this.portal = config.portal || ''
this.callback = config.callback
this._rowMode = config.rowMode
if (process.domain && config.callback) {
this.callback = process.domain.bind(config.callback)
}
this._result = new Result(config.rowMode, config.types)
this._result = new Result(this._rowMode, this.types)

// potential for multiple results
this._results = this._result
this.isPreparedStatement = false
this._canceledDueToError = false
this._promise = null
Expand All @@ -54,10 +58,24 @@ Query.prototype.requiresPreparation = function () {
return this.values.length > 0
}

Query.prototype._checkForMultirow = function () {
// if we already have a result with a command property
// then we've already executed one query in a multi-statement simple query
// turn our results into an array of results
if (this._result.command) {
if (!Array.isArray(this._results)) {
this._results = [this._result]
}
this._result = new Result(this._rowMode, this.types)
this._results.push(this._result)
}
}

// associates row metadata from the supplied
// message with this query object
// metadata used when parsing row results
Query.prototype.handleRowDescription = function (msg) {
this._checkForMultirow()
this._result.addFields(msg.fields)
this._accumulateRows = this.callback || !this.listeners('row').length
}
Expand All @@ -83,6 +101,7 @@ Query.prototype.handleDataRow = function (msg) {
}

Query.prototype.handleCommandComplete = function (msg, con) {
this._checkForMultirow()
this._result.addCommandComplete(msg)
// need to sync after each command complete of a prepared statement
if (this.isPreparedStatement) {
Expand All @@ -104,9 +123,9 @@ Query.prototype.handleReadyForQuery = function (con) {
return this.handleError(this._canceledDueToError, con)
}
if (this.callback) {
this.callback(null, this._result)
this.callback(null, this._results)
}
this.emit('end', this._result)
this.emit('end', this._results)
}

Query.prototype.handleError = function (err, connection) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"eslint-plugin-standard": "3.0.1",
"pg-copy-streams": "0.3.0"
},
"minNativeVersion": "1.7.0",
"minNativeVersion": "2.0.0",
"scripts": {
"test": "make test-all"
},
Expand Down
69 changes: 69 additions & 0 deletions test/integration/client/multiple-results-tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
'use strict'
const assert = require('assert')
const co = require('co')

const helper = require('./test-helper')

const suite = new helper.Suite('multiple result sets')

suite.test('two select results work', co.wrap(function * () {
const client = new helper.Client()
yield client.connect()

const results = yield client.query(`SELECT 'foo'::text as name; SELECT 'bar'::text as baz`)
assert(Array.isArray(results))

assert.equal(results[0].fields[0].name, 'name')
assert.deepEqual(results[0].rows, [{ name: 'foo' }])

assert.equal(results[1].fields[0].name, 'baz')
assert.deepEqual(results[1].rows, [{ baz: 'bar' }])

return client.end()
}))

suite.test('multiple selects work', co.wrap(function * () {
const client = new helper.Client()
yield client.connect()

const text = `
SELECT * FROM generate_series(2, 4) as foo;
SELECT * FROM generate_series(8, 10) as bar;
SELECT * FROM generate_series(20, 22) as baz;
`

const results = yield client.query(text)
assert(Array.isArray(results))

assert.equal(results[0].fields[0].name, 'foo')
assert.deepEqual(results[0].rows, [{ foo: 2 }, { foo: 3 }, { foo: 4 }])

assert.equal(results[1].fields[0].name, 'bar')
assert.deepEqual(results[1].rows, [{ bar: 8 }, { bar: 9 }, { bar: 10 }])

assert.equal(results[2].fields[0].name, 'baz')
assert.deepEqual(results[2].rows, [{ baz: 20 }, { baz: 21 }, { baz: 22 }])

assert.equal(results.length, 3)

return client.end()
}))

suite.test('mixed queries and statements', co.wrap(function * () {
const client = new helper.Client()
yield client.connect()

const text = `
CREATE TEMP TABLE weather(type text);
INSERT INTO weather(type) VALUES ('rain');
SELECT * FROM weather;
`

const results = yield client.query(text)
assert(Array.isArray(results))
assert.equal(results[0].command, 'CREATE')
assert.equal(results[1].command, 'INSERT')
assert.equal(results[2].command, 'SELECT')

return client.end()
}))
11 changes: 6 additions & 5 deletions test/integration/client/simple-query-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,17 @@ test('prepared statements do not mutate params', function () {

client.on('drain', client.end.bind(client))

const rows = []
query.on('row', function (row, result) {
assert.ok(result)
result.addRow(row)
rows.push(row)
})

query.on('end', function (result) {
assert.lengthIs(result.rows, 26, 'result returned wrong number of rows')
assert.lengthIs(result.rows, result.rowCount)
assert.equal(result.rows[0].name, 'Aaron')
assert.equal(result.rows[25].name, 'Zanzabar')
assert.lengthIs(rows, 26, 'result returned wrong number of rows')
assert.lengthIs(rows, result.rowCount)
assert.equal(rows[0].name, 'Aaron')
assert.equal(rows[25].name, 'Zanzabar')
})
})

Expand Down