postgrejs
Version:
Professional PostgreSQL client NodeJS
263 lines (262 loc) • 8.25 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Backend = void 0;
const buffer_reader_js_1 = require("./buffer-reader.js");
const protocol_js_1 = require("./protocol.js");
// 1 byte message type, 4 byte frame length
const HEADER_LENGTH = 5;
const ErrorFieldTypes = {
M: 'message',
S: 'severity',
V: 'severity',
C: 'code',
D: 'detail',
H: 'hint',
P: 'position',
p: 'internalPosition',
q: 'internalQuery',
W: 'where',
s: 'schema',
t: 'table',
c: 'column',
d: 'dataType',
n: 'constraint',
F: 'file',
L: 'line',
R: 'routine',
};
class Backend {
reset() {
this._buf = undefined;
}
parse(data, callback) {
if (this._buf) {
data = Buffer.concat([this._buf, data]);
this._buf = undefined;
}
const io = new buffer_reader_js_1.BufferReader(data);
let offsetBookmark;
while (io.length - io.offset >= HEADER_LENGTH) {
offsetBookmark = io.offset;
const code = io.readUInt8();
const len = io.readUInt32BE();
// Check if frame data not received yet
if (io.length - io.offset < len - 4) {
io.offset = offsetBookmark;
this._buf = io.readBuffer();
return;
}
const parser = MessageParsers[code];
const v = parser && parser(io, code, len);
callback(code, v);
// Set offset to next message
io.offset = offsetBookmark + len + 1;
}
if (io.offset < io.length)
this._buf = io.readBuffer(io.length - io.offset);
}
}
exports.Backend = Backend;
const MessageParsers = {
[protocol_js_1.Protocol.BackendMessageCode.Authentication]: parseAuthentication,
[protocol_js_1.Protocol.BackendMessageCode.BackendKeyData]: parseBackendKeyData,
[protocol_js_1.Protocol.BackendMessageCode.CommandComplete]: parseCommandComplete,
[protocol_js_1.Protocol.BackendMessageCode.CopyData]: parseCopyData,
[protocol_js_1.Protocol.BackendMessageCode.CopyInResponse]: parseCopyResponse,
[protocol_js_1.Protocol.BackendMessageCode.CopyOutResponse]: parseCopyResponse,
[protocol_js_1.Protocol.BackendMessageCode.CopyBothResponse]: parseCopyResponse,
[protocol_js_1.Protocol.BackendMessageCode.DataRow]: parseDataRow,
[protocol_js_1.Protocol.BackendMessageCode.ErrorResponse]: parseErrorResponse,
[protocol_js_1.Protocol.BackendMessageCode.NoticeResponse]: parseErrorResponse,
[protocol_js_1.Protocol.BackendMessageCode.NotificationResponse]: parseNotificationResponse,
[protocol_js_1.Protocol.BackendMessageCode.FunctionCallResponse]: parseFunctionCallResponse,
[protocol_js_1.Protocol.BackendMessageCode.NegotiateProtocolVersion]: parseNegotiateProtocolVersion,
[protocol_js_1.Protocol.BackendMessageCode.ParameterDescription]: parseParameterDescription,
[protocol_js_1.Protocol.BackendMessageCode.ParameterStatus]: parseParameterStatus,
[protocol_js_1.Protocol.BackendMessageCode.ReadyForQuery]: parseReadyForQuery,
[protocol_js_1.Protocol.BackendMessageCode.RowDescription]: parseRowDescription,
};
function parseAuthentication(io, code, len) {
const kind = io.readUInt32BE();
switch (kind) {
case 0:
return; // AuthenticationOk
case 2:
return {
kind: 'KerberosV5',
};
case 3:
return {
kind: 'CleartextPassword',
};
case 5:
return {
kind: 'MD5Password',
salt: io.readBuffer(len - 8),
};
case 6:
return {
kind: 'SCMCredential',
};
case 7:
return {
kind: 'GSS',
};
case 9:
return {
kind: 'SSPI',
};
case 8:
return {
kind: 'GSSContinue',
data: io.readBuffer(len - 8),
};
case 10: {
const out = {
kind: 'SASL',
mechanisms: [],
};
let mechanism;
while ((mechanism = io.readCString())) {
out.mechanisms.push(mechanism);
}
return out;
}
case 11:
return {
kind: 'SASLContinue',
data: io.readLString(len - 8, 'utf8'),
};
case 12:
return {
kind: 'SASLFinal',
data: io.readLString(len - 8, 'utf8'),
};
default:
throw new Error(`Unknown authentication kind (${kind})`);
}
}
function parseBackendKeyData(io) {
return {
processID: io.readUInt32BE(),
secretKey: io.readUInt32BE(),
};
}
function parseCommandComplete(io) {
return {
command: io.readCString('utf8'),
};
}
function parseCopyData(io, code, len) {
return {
data: io.readBuffer(len - 4),
};
}
function parseCopyResponse(io) {
const out = {
overallFormat: io.readUInt8() === 0
? protocol_js_1.Protocol.DataFormat.text
: protocol_js_1.Protocol.DataFormat.binary,
columnCount: io.readUInt16BE(),
};
if (out.columnCount) {
out.columnFormats = [];
for (let i = 0; i < out.columnCount; i++) {
out.columnFormats.push(io.readUInt16BE() === 0
? protocol_js_1.Protocol.DataFormat.text
: protocol_js_1.Protocol.DataFormat.binary);
}
}
return out;
}
function parseDataRow(io) {
const out = {
columnCount: io.readUInt16BE(),
};
if (out.columnCount) {
out.columns = [];
for (let i = 0; i < out.columnCount; i++) {
// The length of the column value, in bytes (this count does not include itself).
// Can be zero. As a special case, -1 indicates a NULL column value.
// No value bytes follow in the NULL case.
const l = io.readInt32BE();
if (l < 0)
out.columns.push(null);
else
out.columns.push(io.readBuffer(l));
}
}
return out;
}
function parseErrorResponse(io) {
const out = {};
let fieldType;
while ((fieldType = io.readLString(1)) !== '\0') {
const value = io.readCString('utf8');
const key = ErrorFieldTypes[fieldType];
if (key)
out[key] = value;
}
return out;
}
function parseNotificationResponse(io) {
return {
processId: io.readUInt32BE(),
channel: io.readCString(),
payload: io.readCString(),
};
}
function parseFunctionCallResponse(io, code, len) {
return {
result: io.readBuffer(len - 4),
};
}
function parseNegotiateProtocolVersion(io) {
return {
supportedVersionMinor: io.readUInt32BE(),
numberOfNotSupportedVersions: io.readUInt32BE(),
option: io.readCString('utf8'),
};
}
function parseParameterDescription(io) {
const out = {
parameterCount: io.readUInt32BE(),
parameterIds: [],
};
for (let i = 0; i < out.parameterCount; i++) {
out.parameterIds.push(io.readUInt32BE());
}
return out;
}
function parseParameterStatus(io) {
return {
name: io.readCString('utf8'),
value: io.readCString('utf8'),
};
}
function parseReadyForQuery(io) {
return {
status: io.readLString(1),
};
}
function parseRowDescription(io) {
const fieldCount = io.readUInt16BE();
const out = {
fields: [],
};
for (let i = 0; i < fieldCount; i++) {
const field = {
fieldName: io.readCString('utf8'),
tableId: io.readInt32BE(),
columnId: io.readInt16BE(),
dataTypeId: io.readInt32BE(),
fixedSize: io.readInt16BE(),
modifier: io.readInt32BE(),
format: io.readInt16BE() === 0
? protocol_js_1.Protocol.DataFormat.text
: protocol_js_1.Protocol.DataFormat.binary,
};
out.fields.push(field);
}
return out;
}