Skip to content

Commit ad03785

Browse files
committed
Add support for describing prepared statements
1 parent 06fbe19 commit ad03785

File tree

6 files changed

+124
-0
lines changed

6 files changed

+124
-0
lines changed

lib/client.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,11 @@ Client.prototype._attachListeners = function (con) {
308308
self.activeQuery.handleDataRow(msg)
309309
})
310310

311+
// delegate paramDescription to active query
312+
con.on('paramDescription', function (msg) {
313+
self.activeQuery.handleParamDescription(msg, con)
314+
})
315+
311316
// delegate portalSuspended to active query
312317
// eslint-disable-next-line no-unused-vars
313318
con.on('portalSuspended', function (msg) {

lib/connection.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,9 @@ Connection.prototype.parseMessage = function (buffer) {
426426
case 0x73: // s
427427
return new Message('portalSuspended', length)
428428

429+
case 0x74: // t
430+
return this.parset(buffer, length)
431+
429432
case 0x47: // G
430433
return this.parseG(buffer, length)
431434

@@ -563,6 +566,17 @@ Connection.prototype.parseField = function (buffer) {
563566
return field
564567
}
565568

569+
Connection.prototype.parset = function (buffer, length) {
570+
var msg = new Message('paramDescription', length)
571+
msg.paramCount = this.parseInt16(buffer)
572+
var params = []
573+
for (var i = 0; i < msg.paramCount; i++) {
574+
params.push(this.parseInt32(buffer))
575+
}
576+
msg.params = params
577+
return msg
578+
}
579+
566580
var DATA_ROW = 'dataRow'
567581
var DataRowMessage = function (length, fieldCount) {
568582
this.name = DATA_ROW

lib/query.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ var Query = function (config, values, callback) {
2525
this.types = config.types
2626
this.name = config.name
2727
this.binary = config.binary
28+
this.describe = config.describe
2829
// use unique portal name each time
2930
this.portal = config.portal || ''
3031
this.callback = config.callback
@@ -47,6 +48,8 @@ util.inherits(Query, EventEmitter)
4748
Query.prototype.requiresPreparation = function () {
4849
// named queries must always be prepared
4950
if (this.name) { return true }
51+
// always prepare if describing a query
52+
if (this.describe) { return true }
5053
// always prepare if there are max number of rows expected per
5154
// portal execution
5255
if (this.rows) { return true }
@@ -99,6 +102,11 @@ Query.prototype.handleDataRow = function (msg) {
99102
}
100103
}
101104

105+
Query.prototype.handleParamDescription = function (msg, con) {
106+
this._result.addParams(msg.params)
107+
con.sync()
108+
}
109+
102110
Query.prototype.handleCommandComplete = function (msg, con) {
103111
this._checkForMultirow()
104112
this._result.addCommandComplete(msg)
@@ -193,6 +201,16 @@ Query.prototype.prepare = function (connection) {
193201
}, true)
194202
}
195203

204+
if (self.describe) {
205+
// if describe is set, the query is not executed
206+
connection.describe({
207+
type: 'S',
208+
name: self.name
209+
})
210+
connection.flush()
211+
return
212+
}
213+
196214
if (self.values) {
197215
try {
198216
self.values = self.values.map(utils.prepareValue)

lib/result.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ var Result = function (rowMode, types) {
1818
this.oid = null
1919
this.rows = []
2020
this.fields = []
21+
this.params = []
2122
this._parsers = []
2223
this._types = types
2324
this.RowCtor = null
@@ -100,4 +101,14 @@ Result.prototype.addFields = function (fieldDescriptions) {
100101
}
101102
}
102103

104+
Result.prototype.addParams = function (paramDescriptions) {
105+
if (this.params.length) {
106+
this.params = []
107+
}
108+
for (var i = 0; i < paramDescriptions.length; i++) {
109+
var desc = paramDescriptions[i]
110+
this.params.push(desc)
111+
}
112+
}
113+
103114
module.exports = Result

test/integration/client/prepared-statement-tests.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,28 @@ var suite = new helper.Suite()
151151

152152
suite.test('cleanup', () => client.end())
153153
})()
154+
155+
;(function () {
156+
var client = helper.client()
157+
client.on('drain', client.end.bind(client))
158+
159+
suite.test('describe', function (done) {
160+
var query = client.query(new Query({
161+
text: 'SELECT id, name, age FROM person WHERE age > $1',
162+
describe: true
163+
}, (err, res) => {
164+
assert.deepEqual(res.params, [23])
165+
assert.deepEqual(
166+
res.fields.map(field => ({ name: field.name, type: field.dataTypeID })),
167+
[
168+
{ name: 'id', type: 23 },
169+
{ name: 'name', type: 1043 },
170+
{ name: 'age', type: 23 }
171+
]
172+
)
173+
done()
174+
}))
175+
})
176+
177+
suite.test('cleanup', () => client.end())
178+
})()

test/unit/client/prepared-statement-tests.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,54 @@ test('prepared statement with explicit portal', function () {
153153
})
154154
})
155155
})
156+
157+
var describeClient = helper.client()
158+
var describeCon = describeClient.connection
159+
describeCon.parse = function (arg) {
160+
process.nextTick(function () {
161+
describeCon.emit('parseComplete')
162+
})
163+
}
164+
var describeDescribeArg = null
165+
describeCon.describe = function (arg) {
166+
describeDescribeArg = arg
167+
}
168+
169+
var describeFlushCalled = false
170+
describeCon.flush = function () {
171+
describeFlushCalled = true
172+
process.nextTick(function () {
173+
describeCon.emit('paramDescription', { params: [] })
174+
describeCon.emit('rowDescription', { fields: [] })
175+
})
176+
}
177+
178+
var describeSyncCalled = false
179+
describeCon.sync = function () {
180+
process.nextTick(function () {
181+
describeSyncCalled = true
182+
describeCon.emit('readyForQuery')
183+
})
184+
}
185+
186+
test('describe prepared statement', function () {
187+
assert.ok(describeClient.connection.emit('readyForQuery'))
188+
189+
var query = describeClient.query(new Query({
190+
text: 'select * from X where name = $1',
191+
describe: true
192+
}))
193+
194+
assert.emits(query, 'end', function () {
195+
test('describe argument', function () {
196+
assert.equal(describeDescribeArg.type, 'S')
197+
assert.equal(describeDescribeArg.name, undefined)
198+
})
199+
test('flush called', function () {
200+
assert.ok(describeFlushCalled)
201+
})
202+
test('sync called', function () {
203+
assert.ok(describeSyncCalled)
204+
})
205+
})
206+
})

0 commit comments

Comments
 (0)