Skip to content

Commit eeeba9f

Browse files
committed
Fix many bugs in protocol logic
1 parent 41e3d7f commit eeeba9f

File tree

1 file changed

+66
-61
lines changed

1 file changed

+66
-61
lines changed

postgres.js

Lines changed: 66 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ var DEBUG = 0;
22

33
include('util.js');
44

5-
function add_int(message, num) {
5+
function add_int32(message, num) {
66
var part;
77
part = Math.floor(num / 0xffffff);
88
message.push(part);
@@ -35,70 +35,70 @@ function add_string(message, text) {
3535

3636
function format_message(type, body) {
3737
var message = [type.charCodeAt(0)];
38-
add_int(message, body.length + 4);
38+
add_int32(message, body.length + 4);
3939
add_string(message, body);
4040

4141
return message;
4242
}
4343

4444
// http://www.postgresql.org/docs/8.3/static/protocol-message-formats.html
4545
var formatter = {
46+
AddHeader: function (message, code) {
47+
var stream = [];
48+
if (code) {
49+
stream.push(code.charCodeAt(0));
50+
}
51+
add_int32(stream, message.length + 4);
52+
return stream.concat(message);
53+
},
4654
CopyData: function () {
4755
// TODO: implement
4856
},
4957
CopyDone: function () {
5058
// TODO: implement
5159
},
5260
Describe: function (name, type) {
53-
var message = ['D'.charCodeAt(0)];
54-
add_int(message, name.length + 6);
61+
var message = [];
5562
message.push(type.charCodeAt(0));
5663
add_string(message, name);
57-
return message;
64+
return formatter.AddHeader(message, 'D');
5865
},
5966
Execute: function (name, max_rows) {
60-
var message = ['E'.charCodeAt(0)];
61-
add_int(message, name.length + 9);
67+
var message = [];
6268
add_string(message, name);
63-
add_int(message, max_rows);
64-
return message;
69+
add_int32(message, max_rows);
70+
return formatter.AddHeader(message, 'E');
6571
},
6672
Flush: function () {
67-
var message = ['H'.charCodeAt(0)];
68-
add_int(message, 4);
69-
return message;
73+
return formatter.AddHeader([], 'H');
7074
},
7175
FunctionCall: function () {
7276
// TODO: implement
7377
},
7478
Parse: function (name, query, var_types) {
75-
var message = ['P'.charCodeAt(0)];
76-
add_int(message, 4 + name.length + query.length + 4 + var_types.length * 4);
79+
var message = [];
7780
add_string(message, name);
7881
add_string(message, query);
7982
add_int16(message, var_types.length);
8083
var_types.each(function (var_type) {
81-
add_int(message, var_type);
84+
add_int32(message, var_type);
8285
});
83-
return message;
86+
return formatter.AddHeader(message, 'P');
8487
},
8588
PasswordMessage: function (password) {
86-
var message = ['p'.charCodeAt(0)];
87-
add_int(message, password.length + 5);
89+
var message = [];
8890
add_string(message, password);
89-
return message;
91+
return formatter.AddHeader(message, 'p');
9092
},
9193
Query: function (query) {
92-
var message = ['Q'.charCodeAt(0)];
93-
add_int(message, query.length + 5);
94+
var message = [];
9495
add_string(message, query);
95-
return message;
96+
return formatter.AddHeader(message, 'Q');
9697
},
9798
SSLRequest: function () {
9899
var message = [];
99-
add_int(message, 8); // Message length
100-
add_int(message, 80877103);
101-
return message;
100+
add_int32(message, 80877103);
101+
return formatter.AddHeader(message);
102102
},
103103
StartupMessage: function (options) {
104104
var message = [];
@@ -108,39 +108,34 @@ var formatter = {
108108
text += k + String.fromCharCode(0) + options[k] + String.fromCharCode(0);
109109
}
110110
}
111-
add_int(message, text.length + 9); // Message length
112-
add_int(message, 196608); // Protocol version number
111+
add_int32(message, 196608); // Protocol version number
113112
add_string(message, text); // options
114-
return message;
113+
return formatter.AddHeader(message);
115114
},
116115
Sync: function () {
117-
var message = ['S'.charCodeAt(0)];
118-
add_int(message, 4);
119-
return message;
116+
return formatter.AddHeader([], 'S');
120117
},
121118
Terminate: function () {
122-
var message = ['X'.charCodeAt(0)];
123-
add_int(message, 4);
124-
return message;
119+
return formatter.AddHeader([], 'X');
125120
}
126121
};
127122

123+
// Convert 4 bytes to signed 32 bit integer
128124
function parse_int32(message) {
129-
return message.shift() * 0xffffff + message.shift() * 0xffff + message.shift() * 0xff + message.shift();
125+
var unsigned = message.shift() * 0x1000000 + message.shift() * 0x10000 + message.shift() * 0x100 + message.shift();
126+
return (unsigned & 0x80000000) ? (unsigned - 0x100000000) : unsigned;
130127
}
131128

129+
// Convert 2 bytes to signed 16 bit integer
132130
function parse_int16(message) {
133-
return message.shift() * 0xff + message.shift();
131+
var unsigned = message.shift() * 0x100 + message.shift();
132+
return (unsigned & 0x8000) ? (unsigned - 0x10000) : unsigned;
134133
}
135134

136135
function parse_string(message) {
137136
var text = "";
138-
while (message[0] !== 0) {
139-
var n = message.shift();
140-
if (n < 0) {
141-
n += 256;
142-
}
143-
text += String.fromCharCode(n);
137+
while (message.length > 0 && message[0] !== 0) {
138+
text += String.fromCharCode(message.shift());
144139
}
145140
message.shift();
146141
return text;
@@ -159,18 +154,12 @@ function parse_raw_string(message, len) {
159154
var text = "";
160155
while (len > 0) {
161156
len -= 1;
162-
var n = message.shift();
163-
if (n < 0) {
164-
n += 256;
165-
}
166-
text += String.fromCharCode(n);
157+
text += String.fromCharCode(message.shift());
167158
}
168159
return text;
169160
}
170161

171-
function parse_response(message) {
172-
var code = String.fromCharCode(message.shift());
173-
var len = parse_int32(message);
162+
function parse_response(code, message) {
174163
var type;
175164
var args = [];
176165
switch (code) {
@@ -250,7 +239,11 @@ function parse_response(message) {
250239
var num_cols = parse_int16(message);
251240
for (var i = 0; i < num_cols; i += 1) {
252241
var size = parse_int32(message);
253-
row.push(parse_raw_string(message, size));
242+
if (size === -1) {
243+
row.push(null);
244+
} else {
245+
row.push(parse_raw_string(message, size));
246+
}
254247
}
255248
args = [row];
256249
break;
@@ -282,11 +275,9 @@ exports.Connection = function (database, username, password) {
282275
function sendMessage(type, args) {
283276
var message = formatter[type].apply(this, args);
284277
if (DEBUG > 0) {
285-
print("Sending " + type + ": ");
286-
p(args);
287-
if (DEBUG > 1) {
288-
print("->");
289-
p(message);
278+
node.debug("Sending " + type + ": " + JSON.stringify(args));
279+
if (DEBUG > 2) {
280+
node.debug("->" + JSON.stringify(message));
290281
}
291282
}
292283
connection.send(message, "raw");
@@ -298,16 +289,30 @@ exports.Connection = function (database, username, password) {
298289
sendMessage('StartupMessage', [{user: username, database: database}]);
299290
});
300291
connection.addListener("receive", function (data) {
292+
293+
// Hack to work around bug in node
294+
// TODO: remove once Ry fixes bug
295+
for (var i = 0, l = data.length; i < l; i += 1) {
296+
if (data[i] < 0) {
297+
data[i] += 256;
298+
}
299+
}
300+
301+
if (DEBUG > 2) {
302+
node.debug("<-" + JSON.stringify(data));
303+
}
304+
301305
while (data.length > 0) {
306+
var code = String.fromCharCode(data.shift());
307+
var len = parse_int32(data);
308+
var message = data.splice(0, len - 4);
302309
if (DEBUG > 1) {
303-
print("<-");
304-
p(data);
310+
node.debug("message: " + code + " " + JSON.stringify(message));
305311
}
306-
var command = parse_response(data);
312+
var command = parse_response(code, message);
307313
if (command.type) {
308314
if (DEBUG > 0) {
309-
print("Received " + command.type + ": ");
310-
p(command.args);
315+
node.debug("Received " + command.type + ": " + JSON.stringify(command.args));
311316
}
312317
events.emit(command.type, command.args);
313318
}

0 commit comments

Comments
 (0)