UNPKG

postgrejs

Version:

Professional PostgreSQL client NodeJS

263 lines (262 loc) 8.25 kB
"use strict"; 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; }