Skip to content

Commit 4c8f489

Browse files
committed
Merge pull request #572 from letsface/test-prepared-statement-error
test to reproduce behavior of issue #549
2 parents 1047aeb + fbedaf4 commit 4c8f489

File tree

4 files changed

+103
-18
lines changed

4 files changed

+103
-18
lines changed

Diff for: lib/client.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -176,13 +176,13 @@ Client.prototype.connect = function(callback) {
176176
con.once('end', function() {
177177
if ( callback ) {
178178
// haven't received a connection message yet !
179-
var err = new Error("Stream unexpectedly ended before getting ready for query execution");
179+
var err = new Error('Connection was ended during query');
180180
callback(err);
181181
callback = null;
182182
return;
183183
}
184184
if(self.activeQuery) {
185-
var disconnectError = new Error('Stream unexpectedly ended during query execution');
185+
var disconnectError = new Error('Connection was ended during query');
186186
self.activeQuery.handleError(disconnectError, con);
187187
self.activeQuery = null;
188188
}

Diff for: lib/connection.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -285,14 +285,15 @@ Connection.prototype.sync = function() {
285285
this.writer.flush(0);
286286

287287
this.writer.add(emptyBuffer);
288+
this._ending = true;
288289
this._send(0x53);
289290
};
290291

291292
Connection.prototype.end = function() {
292293
//0x58 = 'X'
293294
this.writer.add(emptyBuffer);
294-
this._send(0x58);
295295
this._ending = true;
296+
this._send(0x58);
296297
};
297298

298299
Connection.prototype.describe = function(msg, more) {

Diff for: test/integration/client/copy-tests.js

+16-15
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ var prepareTable = function (client, callback) {
88
client.query(
99
'CREATE TEMP TABLE copy_test (id SERIAL, name CHARACTER VARYING(10), age INT)',
1010
assert.calls(function (err, result) {
11-
assert.equal(err, null, "create table query should not fail");
11+
assert.equal(err, null,
12+
err && err.message ? "create table query should not fail: " + err.message : null);
1213
callback();
1314
})
1415
);
@@ -19,7 +20,7 @@ test('COPY FROM', function () {
1920
prepareTable(client, function () {
2021
var stream = client.copyFrom("COPY copy_test (name, age) FROM stdin WITH CSV");
2122
stream.on('error', function (error) {
22-
assert.ok(false, "COPY FROM stream should not emit errors" + helper.sys.inspect(error));
23+
assert.ok(false, "COPY FROM stream should not emit errors" + helper.sys.inspect(error));
2324
});
2425
for (var i = 0; i < ROWS_TO_INSERT; i++) {
2526
stream.write( String(Date.now() + Math.random()).slice(0,10) + ',' + i + '\n');
@@ -44,11 +45,11 @@ test('COPY TO', function () {
4445
var stream = client.copyTo("COPY person (id, name, age) TO stdin WITH CSV");
4546
var buf = new Buffer(0);
4647
stream.on('error', function (error) {
47-
assert.ok(false, "COPY TO stream should not emit errors" + helper.sys.inspect(error));
48+
assert.ok(false, "COPY TO stream should not emit errors" + helper.sys.inspect(error));
4849
});
4950
assert.emits(stream, 'data', function (chunk) {
50-
buf = Buffer.concat([buf, chunk]);
51-
}, "COPY IN stream should emit data event for each row");
51+
buf = Buffer.concat([buf, chunk]);
52+
}, "COPY IN stream should emit data event for each row");
5253
assert.emits(stream, 'end', function () {
5354
var lines = buf.toString().split('\n');
5455
assert.equal(lines.length >= 0, true, "copy in should return rows saved by copy from");
@@ -73,19 +74,19 @@ test('COPY TO, queue queries', function () {
7374
});
7475
var stream = client.copyTo("COPY person (id, name, age) TO stdin WITH CSV");
7576
//imitate long query, to make impossible,
76-
//that copy query end callback runs after
77+
//that copy query end callback runs after
7778
//second query callback
7879
client.query("SELECT pg_sleep(1)", function () {
7980
query2Done = true;
8081
assert.ok(copyQueryDone && query2Done, "second query has to be executed after others");
8182
});
8283
var buf = new Buffer(0);
8384
stream.on('error', function (error) {
84-
assert.ok(false, "COPY TO stream should not emit errors" + helper.sys.inspect(error));
85+
assert.ok(false, "COPY TO stream should not emit errors" + helper.sys.inspect(error));
8586
});
8687
assert.emits(stream, 'data', function (chunk) {
87-
buf = Buffer.concat([buf, chunk]);
88-
}, "COPY IN stream should emit data event for each row");
88+
buf = Buffer.concat([buf, chunk]);
89+
}, "COPY IN stream should emit data event for each row");
8990
assert.emits(stream, 'end', function () {
9091
copyQueryDone = true;
9192
assert.ok(query1Done && ! query2Done, "copy query has to be executed before second query and after first");
@@ -100,14 +101,14 @@ test('COPY TO, queue queries', function () {
100101

101102
test("COPY TO incorrect usage with large data", function () {
102103
if(helper.config.native) return false;
103-
//when many data is loaded from database (and it takes a lot of time)
104+
//when many data is loaded from database (and it takes a lot of time)
104105
//there are chance, that query will be canceled before it ends
105-
//but if there are not so much data, cancel message may be
106+
//but if there are not so much data, cancel message may be
106107
//send after copy query ends
107108
//so we need to test both situations
108109
pg.connect(helper.config, assert.calls(function (error, client, done) {
109110
assert.equal(error, null, "Failed to connect: " + helper.sys.inspect(error));
110-
//intentionally incorrect usage of copy.
111+
//intentionally incorrect usage of copy.
111112
//this has to report error in standart way, instead of just throwing exception
112113
client.query(
113114
"COPY (SELECT GENERATE_SERIES(1, 10000000)) TO STDOUT WITH CSV",
@@ -127,7 +128,7 @@ test("COPY TO incorrect usage with small data", function () {
127128
if(helper.config.native) return false;
128129
pg.connect(helper.config, assert.calls(function (error, client, done) {
129130
assert.equal(error, null, "Failed to connect: " + helper.sys.inspect(error));
130-
//intentionally incorrect usage of copy.
131+
//intentionally incorrect usage of copy.
131132
//this has to report error in standart way, instead of just throwing exception
132133
client.query(
133134
"COPY (SELECT GENERATE_SERIES(1, 1)) TO STDOUT WITH CSV",
@@ -147,7 +148,7 @@ test("COPY FROM incorrect usage", function () {
147148
pg.connect(helper.config, function (error, client, done) {
148149
assert.equal(error, null, "Failed to connect: " + helper.sys.inspect(error));
149150
prepareTable(client, function () {
150-
//intentionally incorrect usage of copy.
151+
//intentionally incorrect usage of copy.
151152
//this has to report error in standart way, instead of just throwing exception
152153
client.query(
153154
"COPY copy_test from STDIN WITH CSV",
@@ -157,7 +158,7 @@ test("COPY FROM incorrect usage", function () {
157158
assert.isNull(error, "incorrect copy usage should not break connection: " + error);
158159
assert.ok(result, "incorrect copy usage should not break connection");
159160
done();
160-
pg.end(helper.config);
161+
pg.end(helper.config);
161162
}));
162163
})
163164
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
var helper = require(__dirname + '/test-helper');
2+
var util = require('util');
3+
4+
function killIdleQuery(targetQuery) {
5+
var client2 = new Client(helper.args);
6+
var pidColName = 'procpid'
7+
var queryColName = 'current_query';
8+
client2.connect(assert.success(function() {
9+
helper.versionGTE(client2, '9.2.0', assert.success(function(isGreater) {
10+
if(isGreater) {
11+
pidColName = 'pid';
12+
queryColName = 'query';
13+
}
14+
var killIdleQuery = "SELECT " + pidColName + ", (SELECT pg_terminate_backend(" + pidColName + ")) AS killed FROM pg_stat_activity WHERE " + queryColName + " = $1";
15+
client2.query(killIdleQuery, [targetQuery], assert.calls(function(err, res) {
16+
assert.ifError(err);
17+
assert.equal(res.rows.length, 1);
18+
client2.end();
19+
assert.emits(client2, 'end');
20+
}));
21+
}));
22+
}));
23+
}
24+
25+
test('query killed during query execution of prepared statement', function() {
26+
if(helper.args.native) {
27+
return false;
28+
}
29+
var client = new Client(helper.args);
30+
client.connect(assert.success(function() {
31+
var sleepQuery = 'select pg_sleep($1)';
32+
var query1 = client.query({
33+
name: 'sleep query',
34+
text: sleepQuery,
35+
values: [5] },
36+
assert.calls(function(err, result) {
37+
assert.equal(err.message, 'terminating connection due to administrator command');
38+
}));
39+
40+
query1.on('error', function(err) {
41+
assert.fail('Prepared statement should not emit error');
42+
});
43+
44+
query1.on('row', function(row) {
45+
assert.fail('Prepared statement should not emit row');
46+
});
47+
48+
query1.on('end', function(err) {
49+
assert.fail('Prepared statement when executed should not return before being killed');
50+
});
51+
52+
killIdleQuery(sleepQuery);
53+
}));
54+
});
55+
56+
57+
test('client end during query execution of prepared statement', function() {
58+
var client = new Client(helper.args);
59+
client.connect(assert.success(function() {
60+
var sleepQuery = 'select pg_sleep($1)';
61+
var query1 = client.query({
62+
name: 'sleep query',
63+
text: sleepQuery,
64+
values: [5] },
65+
assert.calls(function(err, result) {
66+
assert.equal(err.message, 'Connection was ended during query');
67+
}));
68+
69+
query1.on('error', function(err) {
70+
assert.fail('Prepared statement should not emit error');
71+
});
72+
73+
query1.on('row', function(row) {
74+
assert.fail('Prepared statement should not emit row');
75+
});
76+
77+
query1.on('end', function(err) {
78+
assert.fail('Prepared statement when executed should not return before being killed');
79+
});
80+
81+
client.end();
82+
}));
83+
});

0 commit comments

Comments
 (0)