You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
193 lines
6.4 KiB
193 lines
6.4 KiB
4 years ago
|
"use strict";
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
const buffer_writer_1 = require("./buffer-writer");
|
||
|
const writer = new buffer_writer_1.Writer();
|
||
|
const startup = (opts) => {
|
||
|
// protocol version
|
||
|
writer.addInt16(3).addInt16(0);
|
||
|
for (const key of Object.keys(opts)) {
|
||
|
writer.addCString(key).addCString(opts[key]);
|
||
|
}
|
||
|
writer.addCString('client_encoding').addCString('UTF8');
|
||
|
var bodyBuffer = writer.addCString('').flush();
|
||
|
// this message is sent without a code
|
||
|
var length = bodyBuffer.length + 4;
|
||
|
return new buffer_writer_1.Writer().addInt32(length).add(bodyBuffer).flush();
|
||
|
};
|
||
|
const requestSsl = () => {
|
||
|
const response = Buffer.allocUnsafe(8);
|
||
|
response.writeInt32BE(8, 0);
|
||
|
response.writeInt32BE(80877103, 4);
|
||
|
return response;
|
||
|
};
|
||
|
const password = (password) => {
|
||
|
return writer.addCString(password).flush(112 /* startup */);
|
||
|
};
|
||
|
const sendSASLInitialResponseMessage = function (mechanism, initialResponse) {
|
||
|
// 0x70 = 'p'
|
||
|
writer.addCString(mechanism).addInt32(Buffer.byteLength(initialResponse)).addString(initialResponse);
|
||
|
return writer.flush(112 /* startup */);
|
||
|
};
|
||
|
const sendSCRAMClientFinalMessage = function (additionalData) {
|
||
|
return writer.addString(additionalData).flush(112 /* startup */);
|
||
|
};
|
||
|
const query = (text) => {
|
||
|
return writer.addCString(text).flush(81 /* query */);
|
||
|
};
|
||
|
const emptyArray = [];
|
||
|
const parse = (query) => {
|
||
|
// expect something like this:
|
||
|
// { name: 'queryName',
|
||
|
// text: 'select * from blah',
|
||
|
// types: ['int8', 'bool'] }
|
||
|
// normalize missing query names to allow for null
|
||
|
const name = query.name || '';
|
||
|
if (name.length > 63) {
|
||
|
/* eslint-disable no-console */
|
||
|
console.error('Warning! Postgres only supports 63 characters for query names.');
|
||
|
console.error('You supplied %s (%s)', name, name.length);
|
||
|
console.error('This can cause conflicts and silent errors executing queries');
|
||
|
/* eslint-enable no-console */
|
||
|
}
|
||
|
const types = query.types || emptyArray;
|
||
|
var len = types.length;
|
||
|
var buffer = writer
|
||
|
.addCString(name) // name of query
|
||
|
.addCString(query.text) // actual query text
|
||
|
.addInt16(len);
|
||
|
for (var i = 0; i < len; i++) {
|
||
|
buffer.addInt32(types[i]);
|
||
|
}
|
||
|
return writer.flush(80 /* parse */);
|
||
|
};
|
||
|
const bind = (config = {}) => {
|
||
|
// normalize config
|
||
|
const portal = config.portal || '';
|
||
|
const statement = config.statement || '';
|
||
|
const binary = config.binary || false;
|
||
|
var values = config.values || emptyArray;
|
||
|
var len = values.length;
|
||
|
var useBinary = false;
|
||
|
// TODO(bmc): all the loops in here aren't nice, we can do better
|
||
|
for (var j = 0; j < len; j++) {
|
||
|
useBinary = useBinary || values[j] instanceof Buffer;
|
||
|
}
|
||
|
var buffer = writer.addCString(portal).addCString(statement);
|
||
|
if (!useBinary) {
|
||
|
buffer.addInt16(0);
|
||
|
}
|
||
|
else {
|
||
|
buffer.addInt16(len);
|
||
|
for (j = 0; j < len; j++) {
|
||
|
buffer.addInt16(values[j] instanceof Buffer ? 1 : 0);
|
||
|
}
|
||
|
}
|
||
|
buffer.addInt16(len);
|
||
|
for (var i = 0; i < len; i++) {
|
||
|
var val = values[i];
|
||
|
if (val === null || typeof val === 'undefined') {
|
||
|
buffer.addInt32(-1);
|
||
|
}
|
||
|
else if (val instanceof Buffer) {
|
||
|
buffer.addInt32(val.length);
|
||
|
buffer.add(val);
|
||
|
}
|
||
|
else {
|
||
|
buffer.addInt32(Buffer.byteLength(val));
|
||
|
buffer.addString(val);
|
||
|
}
|
||
|
}
|
||
|
if (binary) {
|
||
|
buffer.addInt16(1); // format codes to use binary
|
||
|
buffer.addInt16(1);
|
||
|
}
|
||
|
else {
|
||
|
buffer.addInt16(0); // format codes to use text
|
||
|
}
|
||
|
return writer.flush(66 /* bind */);
|
||
|
};
|
||
|
const emptyExecute = Buffer.from([69 /* execute */, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00]);
|
||
|
const execute = (config) => {
|
||
|
// this is the happy path for most queries
|
||
|
if (!config || (!config.portal && !config.rows)) {
|
||
|
return emptyExecute;
|
||
|
}
|
||
|
const portal = config.portal || '';
|
||
|
const rows = config.rows || 0;
|
||
|
const portalLength = Buffer.byteLength(portal);
|
||
|
const len = 4 + portalLength + 1 + 4;
|
||
|
// one extra bit for code
|
||
|
const buff = Buffer.allocUnsafe(1 + len);
|
||
|
buff[0] = 69 /* execute */;
|
||
|
buff.writeInt32BE(len, 1);
|
||
|
buff.write(portal, 5, 'utf-8');
|
||
|
buff[portalLength + 5] = 0; // null terminate portal cString
|
||
|
buff.writeUInt32BE(rows, buff.length - 4);
|
||
|
return buff;
|
||
|
};
|
||
|
const cancel = (processID, secretKey) => {
|
||
|
const buffer = Buffer.allocUnsafe(16);
|
||
|
buffer.writeInt32BE(16, 0);
|
||
|
buffer.writeInt16BE(1234, 4);
|
||
|
buffer.writeInt16BE(5678, 6);
|
||
|
buffer.writeInt32BE(processID, 8);
|
||
|
buffer.writeInt32BE(secretKey, 12);
|
||
|
return buffer;
|
||
|
};
|
||
|
const cstringMessage = (code, string) => {
|
||
|
const stringLen = Buffer.byteLength(string);
|
||
|
const len = 4 + stringLen + 1;
|
||
|
// one extra bit for code
|
||
|
const buffer = Buffer.allocUnsafe(1 + len);
|
||
|
buffer[0] = code;
|
||
|
buffer.writeInt32BE(len, 1);
|
||
|
buffer.write(string, 5, 'utf-8');
|
||
|
buffer[len] = 0; // null terminate cString
|
||
|
return buffer;
|
||
|
};
|
||
|
const emptyDescribePortal = writer.addCString('P').flush(68 /* describe */);
|
||
|
const emptyDescribeStatement = writer.addCString('S').flush(68 /* describe */);
|
||
|
const describe = (msg) => {
|
||
|
return msg.name
|
||
|
? cstringMessage(68 /* describe */, `${msg.type}${msg.name || ''}`)
|
||
|
: msg.type === 'P'
|
||
|
? emptyDescribePortal
|
||
|
: emptyDescribeStatement;
|
||
|
};
|
||
|
const close = (msg) => {
|
||
|
const text = `${msg.type}${msg.name || ''}`;
|
||
|
return cstringMessage(67 /* close */, text);
|
||
|
};
|
||
|
const copyData = (chunk) => {
|
||
|
return writer.add(chunk).flush(100 /* copyFromChunk */);
|
||
|
};
|
||
|
const copyFail = (message) => {
|
||
|
return cstringMessage(102 /* copyFail */, message);
|
||
|
};
|
||
|
const codeOnlyBuffer = (code) => Buffer.from([code, 0x00, 0x00, 0x00, 0x04]);
|
||
|
const flushBuffer = codeOnlyBuffer(72 /* flush */);
|
||
|
const syncBuffer = codeOnlyBuffer(83 /* sync */);
|
||
|
const endBuffer = codeOnlyBuffer(88 /* end */);
|
||
|
const copyDoneBuffer = codeOnlyBuffer(99 /* copyDone */);
|
||
|
const serialize = {
|
||
|
startup,
|
||
|
password,
|
||
|
requestSsl,
|
||
|
sendSASLInitialResponseMessage,
|
||
|
sendSCRAMClientFinalMessage,
|
||
|
query,
|
||
|
parse,
|
||
|
bind,
|
||
|
execute,
|
||
|
describe,
|
||
|
close,
|
||
|
flush: () => flushBuffer,
|
||
|
sync: () => syncBuffer,
|
||
|
end: () => endBuffer,
|
||
|
copyData,
|
||
|
copyDone: () => copyDoneBuffer,
|
||
|
copyFail,
|
||
|
cancel,
|
||
|
};
|
||
|
exports.serialize = serialize;
|
||
|
//# sourceMappingURL=serializer.js.map
|