UNPKG

pg-server

Version:

Postgres DB server emulator, proxy or honeypot

233 lines 8.99 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ResponseWriter = void 0; const buffer_writer_1 = require("./buffer-writer"); const responses_1 = require("./responses"); const utils_1 = require("../utils"); const util_1 = __importDefault(require("util")); class ResponseWriter { constructor(socket) { this.socket = socket; this.writer = new buffer_writer_1.Writer(); } flush(code) { const buf = this.writer.flush(code); if (utils_1.isDebug) { if (code !== responses_1.ResponseCode.ErrorMessage) { console.log(` ✈ `, (0, responses_1.messageToStr)(code)); } } this.socket.write(buf); } bindComplete() { this.codeOnly(responses_1.ResponseCode.BindComplete); } parseComplete() { this.codeOnly(responses_1.ResponseCode.ParseComplete); } closeComplete() { this.codeOnly(responses_1.ResponseCode.CloseComplete); } noData() { this.codeOnly(responses_1.ResponseCode.NoData); } portalSuspended() { this.codeOnly(responses_1.ResponseCode.PortalSuspended); } copyDone() { this.codeOnly(responses_1.ResponseCode.CopyDone); } replicationStart() { this.codeOnly(responses_1.ResponseCode.ReplicationStart); } emptyQuery() { this.codeOnly(responses_1.ResponseCode.EmptyQuery); } readyForQuery(status) { var _a; this.writer.addString((_a = status === null || status === void 0 ? void 0 : status[0]) !== null && _a !== void 0 ? _a : 'I'); return this.flush(responses_1.ResponseCode.ReadyForQuery); } codeOnly(code) { return this.flush(code); } dataRow(row) { this.writer.addInt16(row.length); for (const r of row) { if ((0, utils_1.nullish)(r)) { this.writer.addInt32(-1); } else { const str = stringify(r); this.writer.addInt32(str.length); this.writer.addString(str); } } return this.flush(responses_1.ResponseCode.DataRow); } command(cmd) { switch (cmd.type) { case responses_1.ResponseCode.BackendKeyData: return this.backendKeyData(cmd.processID, cmd.secretKey); case responses_1.ResponseCode.BindComplete: return this.bindComplete(); case responses_1.ResponseCode.CloseComplete: return this.closeComplete(); case responses_1.ResponseCode.CommandComplete: return this.commandComplete(cmd.text); case responses_1.ResponseCode.CopyData: return this.copyData(cmd.data); case responses_1.ResponseCode.CopyDone: return this.copyDone(); case responses_1.ResponseCode.CopyIn: return this.copyIn(cmd.isBinary, cmd.columnTypes); case responses_1.ResponseCode.CopyOut: return this.copyOut(cmd.isBinary, cmd.columnTypes); case responses_1.ResponseCode.DataRow: return this.dataRow(cmd.fields); case responses_1.ResponseCode.EmptyQuery: return this.emptyQuery(); case responses_1.ResponseCode.ErrorMessage: return this.error(cmd.message); case responses_1.ResponseCode.NoData: return this.noData(); case responses_1.ResponseCode.NoticeMessage: return this.notice(cmd.message); case responses_1.ResponseCode.NotificationResponse: return this.notificationResponse(cmd.processId, cmd.channel, cmd.payload); case responses_1.ResponseCode.ParameterStatus: return this.parameterStatus(cmd.name, cmd.value); case responses_1.ResponseCode.ParseComplete: return this.parseComplete(); case responses_1.ResponseCode.PortalSuspended: return this.portalSuspended(); case responses_1.ResponseCode.ReadyForQuery: return this.readyForQuery(cmd.status); case responses_1.ResponseCode.ReplicationStart: return this.replicationStart(); case responses_1.ResponseCode.RowDescriptionMessage: return this.rowDescription(cmd.fields); case responses_1.ResponseCode.AuthenticationResponse: if (cmd.kind === 'ok') { return this.authenticationOk(); } throw new Error('Command has no writer: ' + (0, responses_1.messageToStr)(cmd.type)); default: (0, utils_1.assertNever)(cmd); } } commandComplete(message) { this.writer.addCString(message); return this.flush(responses_1.ResponseCode.CommandComplete); } notificationResponse(pid, channel, payload) { this.writer.addInt32(pid); this.writer.addCString(channel); this.writer.addCString(payload); return this.flush(responses_1.ResponseCode.NotificationResponse); } parameterStatus(name, value) { this.writer.addCString(name); this.writer.addCString(value); return this.flush(responses_1.ResponseCode.ParameterStatus); } backendKeyData(pid, secretKey) { this.writer.addInt32(pid); this.writer.addInt32(secretKey); return this.flush(responses_1.ResponseCode.BackendKeyData); } error(error) { this.errorMessage(error, responses_1.ResponseCode.ErrorMessage); } notice(error) { this.errorMessage(error, responses_1.ResponseCode.NoticeMessage); } errorMessage(error, code) { error = error instanceof Error ? util_1.default.inspect(error) : error; error = typeof error === 'string' ? { message: error } : error; if (utils_1.isDebug) { console.warn(` ✈⚠ `, error.message); } // https://www.postgresql.org/docs/12/protocol-error-fields.html for (const [k, v] of Object.entries(error)) { const mk = noticeMapping[k]; if (mk && typeof v === 'string' && v) { this.writer.addString(mk[0]); this.writer.addCString(v); } } this.writer.addString('\0'); return this.flush(code); } parameterDescription(types) { this.writer.addInt16(types.length); for (const t of types) { this.writer.addInt32(t); } return this.flush(responses_1.ResponseCode.ParameterDescription); } rowDescription(fieldDescs) { this.writer.addInt16(fieldDescs.length); for (const f of fieldDescs) { this.writer.addCString(f.name); this.writer.addInt32(f.tableID); this.writer.addInt16(f.columnID); this.writer.addInt32(f.dataTypeID); this.writer.addInt16(f.dataTypeSize); this.writer.addInt32(f.dataTypeModifier); this.writer.addInt16(f.mode === 'text' ? 0 : 1); } return this.flush(responses_1.ResponseCode.RowDescriptionMessage); } copyIn(isBinary, types) { this.copyMessage(isBinary, types, responses_1.ResponseCode.CopyIn); } copyOut(isBinary, types) { this.copyMessage(isBinary, types, responses_1.ResponseCode.CopyOut); } copyMessage(isBinary, types, code) { this.writer.byte(isBinary ? 1 : 0); this.writer.addInt16(types.length); for (const t of types) { this.writer.addInt16(t); } return this.flush(code); } copyData(data) { this.writer.add(data); return this.flush(responses_1.ResponseCode.CopyData); } authenticationOk() { this.writer.addInt32(0); return this.flush(responses_1.ResponseCode.AuthenticationResponse); } } exports.ResponseWriter = ResponseWriter; const noticeMapping = { 'message': 'M', 'severity': 'S', 'code': 'C', 'detail': 'D', 'hint': 'H', 'position': 'P', 'internalPosition': 'p', 'internalQuery': 'q', 'where': 'W', 'schema': 's', 'table': 't', 'column': 'c', 'dataType': 'd', 'constraint': 'n', 'file': 'F', 'line': 'L', 'routine': 'R', }; function stringify(value) { if ((0, utils_1.nullish)(value)) { return ''; } if (typeof value === 'string') { return value; } if (typeof value === 'number') { return String(value); } if (typeof value === 'boolean') { return value ? 't' : 'f'; } if (value instanceof Date) { return value.toISOString(); } if (Buffer.isBuffer(value)) { return value.toString('hex'); } return JSON.stringify(value); } //# sourceMappingURL=response-writer.js.map