Skip to content

Commit 5a3994a

Browse files
committed
Add support for describing prepared statements
1 parent 70cf4dc commit 5a3994a

File tree

6 files changed

+126
-0
lines changed

6 files changed

+126
-0
lines changed

packages/pg/lib/client.js

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

335+
// delegate paramDescription to active query
336+
con.on('paramDescription', function (msg) {
337+
self.activeQuery.handleParamDescription(msg, con)
338+
})
339+
335340
// delegate portalSuspended to active query
336341
// eslint-disable-next-line no-unused-vars
337342
con.on('portalSuspended', function (msg) {

packages/pg/lib/connection.js

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

406+
case 0x74: // t
407+
return this.parset(buffer, length)
408+
406409
case 0x47: // G
407410
return this.parseG(buffer, length)
408411

@@ -540,6 +543,17 @@ Connection.prototype.parseField = function (buffer) {
540543
return field
541544
}
542545

546+
Connection.prototype.parset = function (buffer, length) {
547+
var msg = new Message('paramDescription', length)
548+
msg.paramCount = this.parseInt16(buffer)
549+
var params = []
550+
for (var i = 0; i < msg.paramCount; i++) {
551+
params.push(this.parseInt32(buffer))
552+
}
553+
msg.params = params
554+
return msg
555+
}
556+
543557
var DATA_ROW = 'dataRow'
544558
var DataRowMessage = function (length, fieldCount) {
545559
this.name = DATA_ROW

packages/pg/lib/query.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class Query extends EventEmitter {
2424
this.types = config.types
2525
this.name = config.name
2626
this.binary = config.binary
27+
this.describe = config.describe
2728
// use unique portal name each time
2829
this.portal = config.portal || ''
2930
this.callback = config.callback
@@ -45,6 +46,10 @@ class Query extends EventEmitter {
4546
if (this.name) {
4647
return true
4748
}
49+
// always prepare if describing a query
50+
if (this.describe) {
51+
return true
52+
}
4853
// always prepare if there are max number of rows expected per
4954
// portal execution
5055
if (this.rows) {
@@ -103,6 +108,11 @@ class Query extends EventEmitter {
103108
}
104109
}
105110

111+
handleParamDescription(msg, con) {
112+
this._result.addParams(msg.params)
113+
con.sync()
114+
}
115+
106116
handleCommandComplete(msg, con) {
107117
this._checkForMultirow()
108118
this._result.addCommandComplete(msg)
@@ -202,6 +212,16 @@ class Query extends EventEmitter {
202212
)
203213
}
204214

215+
if (this.describe) {
216+
// if describe is set, the query is not executed
217+
connection.describe({
218+
type: 'S',
219+
name: this.name,
220+
})
221+
connection.flush()
222+
return
223+
}
224+
205225
if (this.values) {
206226
try {
207227
this.values = this.values.map(utils.prepareValue)

packages/pg/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 = undefined
2223
this._types = types
2324
this.RowCtor = null
@@ -102,4 +103,14 @@ Result.prototype.addFields = function (fieldDescriptions) {
102103
}
103104
}
104105

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

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

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

177177
suite.test('cleanup', () => client.end())
178178
})()
179+
180+
;(function () {
181+
var client = helper.client()
182+
client.on('drain', client.end.bind(client))
183+
184+
suite.test('describe', function (done) {
185+
var query = client.query(new Query({
186+
text: 'SELECT id, name, age FROM person WHERE age > $1',
187+
describe: true
188+
}, (err, res) => {
189+
assert.deepEqual(res.params, [23])
190+
assert.deepEqual(
191+
res.fields.map(field => ({ name: field.name, type: field.dataTypeID })),
192+
[
193+
{ name: 'id', type: 23 },
194+
{ name: 'name', type: 1043 },
195+
{ name: 'age', type: 23 }
196+
]
197+
)
198+
done()
199+
}))
200+
})
201+
202+
suite.test('cleanup', () => client.end())
203+
})()

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

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

0 commit comments

Comments
 (0)