UNPKG

@pgxsinkit/pgwasm

Version:
374 lines (366 loc) • 12.8 kB
// @bun import { parseComplete, bindComplete, closeComplete, noData, portalSuspended, replicationStart, emptyQuery, copyDone, AuthenticationOk, AuthenticationCleartextPassword, AuthenticationMD5Password, AuthenticationSASL, AuthenticationSASLContinue, AuthenticationSASLFinal, DatabaseError2, CopyDataMessage, CopyResponse, Field, RowDescriptionMessage, ParameterDescriptionMessage, ParameterStatusMessage, BackendKeyDataMessage, NotificationResponseMessage, ReadyForQueryMessage, CommandCompleteMessage, DataRowMessage, NoticeMessage } from "./chunk-74gcrk47.js"; // packages/pgwasm/src/protocol/wire/types.ts var Modes2 = { text: 0, binary: 1 }; // packages/pgwasm/src/protocol/wire/buffer-reader.ts var emptyBuffer = new ArrayBuffer(0); class BufferReader { #bufferView = new DataView(emptyBuffer); #offset; #decoder = new TextDecoder("utf-8"); constructor(offset = 0) { this.#offset = offset; } setBuffer(offset, buffer) { this.#offset = offset; this.#bufferView = new DataView(buffer); } int16() { const result = this.#bufferView.getInt16(this.#offset, false); this.#offset += 2; return result; } byte() { const result = this.#bufferView.getUint8(this.#offset); this.#offset++; return result; } int32() { const result = this.#bufferView.getInt32(this.#offset, false); this.#offset += 4; return result; } string(length) { return this.#decoder.decode(this.bytes(length)); } cstring() { const start = this.#offset; let end = start; while (this.#bufferView.getUint8(end++) !== 0) {} const result = this.string(end - start - 1); this.#offset = end; return result; } bytes(length) { const result = this.#bufferView.buffer.slice(this.#offset, this.#offset + length); this.#offset += length; return new Uint8Array(result); } } // packages/pgwasm/src/protocol/wire/parser.ts var CODE_LENGTH = 1; var LEN_LENGTH = 4; var HEADER_LENGTH = CODE_LENGTH + LEN_LENGTH; var emptyBuffer2 = new ArrayBuffer(0); var MessageCodes = { DataRow: 68, ParseComplete: 49, BindComplete: 50, CloseComplete: 51, CommandComplete: 67, ReadyForQuery: 90, NoData: 110, NotificationResponse: 65, AuthenticationResponse: 82, ParameterStatus: 83, BackendKeyData: 75, ErrorMessage: 69, NoticeMessage: 78, RowDescriptionMessage: 84, ParameterDescriptionMessage: 116, PortalSuspended: 115, ReplicationStart: 87, EmptyQuery: 73, CopyIn: 71, CopyOut: 72, CopyDone: 99, CopyData: 100 }; class Parser2 { #bufferView = new DataView(emptyBuffer2); #bufferRemainingLength = 0; #bufferOffset = 0; #reader = new BufferReader; parse(buffer, callback) { this.#mergeBuffer(ArrayBuffer.isView(buffer) ? buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength) : buffer); const bufferFullLength = this.#bufferOffset + this.#bufferRemainingLength; let offset = this.#bufferOffset; while (offset + HEADER_LENGTH <= bufferFullLength) { const code = this.#bufferView.getUint8(offset); const length = this.#bufferView.getUint32(offset + CODE_LENGTH, false); const fullMessageLength = CODE_LENGTH + length; if (fullMessageLength + offset <= bufferFullLength && length > 0) { let message; try { message = this.#handlePacket(offset + HEADER_LENGTH, code, length, this.#bufferView.buffer); } catch (error) { this.#resetBuffer(); throw error; } callback(message); offset += fullMessageLength; } else { break; } } if (offset === bufferFullLength) { this.#resetBuffer(); } else { this.#bufferRemainingLength = bufferFullLength - offset; this.#bufferOffset = offset; } } #resetBuffer() { this.#bufferView = new DataView(emptyBuffer2); this.#bufferRemainingLength = 0; this.#bufferOffset = 0; } #mergeBuffer(buffer) { if (this.#bufferRemainingLength > 0) { const newLength = this.#bufferRemainingLength + buffer.byteLength; const newFullLength = newLength + this.#bufferOffset; if (newFullLength > this.#bufferView.byteLength) { let newBuffer; if (newLength <= this.#bufferView.byteLength && this.#bufferOffset >= this.#bufferRemainingLength) { newBuffer = this.#bufferView.buffer; } else { let newBufferLength = this.#bufferView.byteLength * 2; while (newLength >= newBufferLength) { newBufferLength *= 2; } newBuffer = new ArrayBuffer(newBufferLength); } new Uint8Array(newBuffer).set(new Uint8Array(this.#bufferView.buffer, this.#bufferOffset, this.#bufferRemainingLength)); this.#bufferView = new DataView(newBuffer); this.#bufferOffset = 0; } new Uint8Array(this.#bufferView.buffer).set(new Uint8Array(buffer), this.#bufferOffset + this.#bufferRemainingLength); this.#bufferRemainingLength = newLength; } else { this.#bufferView = new DataView(buffer); this.#bufferOffset = 0; this.#bufferRemainingLength = buffer.byteLength; } } #handlePacket(offset, code, length, bytes) { switch (code) { case MessageCodes.BindComplete: return bindComplete; case MessageCodes.ParseComplete: return parseComplete; case MessageCodes.CloseComplete: return closeComplete; case MessageCodes.NoData: return noData; case MessageCodes.PortalSuspended: return portalSuspended; case MessageCodes.CopyDone: return copyDone; case MessageCodes.ReplicationStart: return replicationStart; case MessageCodes.EmptyQuery: return emptyQuery; case MessageCodes.DataRow: return this.#parseDataRowMessage(offset, length, bytes); case MessageCodes.CommandComplete: return this.#parseCommandCompleteMessage(offset, length, bytes); case MessageCodes.ReadyForQuery: return this.#parseReadyForQueryMessage(offset, length, bytes); case MessageCodes.NotificationResponse: return this.#parseNotificationMessage(offset, length, bytes); case MessageCodes.AuthenticationResponse: return this.#parseAuthenticationResponse(offset, length, bytes); case MessageCodes.ParameterStatus: return this.#parseParameterStatusMessage(offset, length, bytes); case MessageCodes.BackendKeyData: return this.#parseBackendKeyData(offset, length, bytes); case MessageCodes.ErrorMessage: return this.#parseErrorMessage(offset, length, bytes, "error"); case MessageCodes.NoticeMessage: return this.#parseErrorMessage(offset, length, bytes, "notice"); case MessageCodes.RowDescriptionMessage: return this.#parseRowDescriptionMessage(offset, length, bytes); case MessageCodes.ParameterDescriptionMessage: return this.#parseParameterDescriptionMessage(offset, length, bytes); case MessageCodes.CopyIn: return this.#parseCopyMessage(offset, length, bytes, "copyInResponse"); case MessageCodes.CopyOut: return this.#parseCopyMessage(offset, length, bytes, "copyOutResponse"); case MessageCodes.CopyData: return this.#parseCopyData(offset, length, bytes); default: return new DatabaseError2(`received invalid response: ${code.toString(16)}`, length, "error"); } } #setBuffer(offset, bytes) { this.#reader.setBuffer(offset, bytes); } #parseReadyForQueryMessage(offset, length, bytes) { this.#setBuffer(offset, bytes); return new ReadyForQueryMessage(length, this.#reader.string(1)); } #parseCommandCompleteMessage(offset, length, bytes) { this.#setBuffer(offset, bytes); return new CommandCompleteMessage(length, this.#reader.cstring()); } #parseCopyData(offset, length, bytes) { const chunk = bytes.slice(offset, offset + (length - 4)); return new CopyDataMessage(length, new Uint8Array(chunk)); } #parseCopyMessage(offset, length, bytes, messageName) { this.#setBuffer(offset, bytes); const isBinary = this.#reader.byte() !== 0; const columnCount = this.#reader.int16(); const message = new CopyResponse(length, messageName, isBinary, columnCount); for (let i = 0;i < columnCount; i++) { message.columnTypes[i] = this.#reader.int16(); } return message; } #parseNotificationMessage(offset, length, bytes) { this.#setBuffer(offset, bytes); const processId = this.#reader.int32(); const channel = this.#reader.cstring(); const payload = this.#reader.cstring(); return new NotificationResponseMessage(length, processId, channel, payload); } #parseRowDescriptionMessage(offset, length, bytes) { this.#setBuffer(offset, bytes); const fieldCount = this.#reader.int16(); const fields = []; for (let i = 0;i < fieldCount; i++) { fields.push(this.#parseField()); } return new RowDescriptionMessage(length, fields); } #parseField() { const name = this.#reader.cstring(); const tableID = this.#reader.int32(); const columnID = this.#reader.int16(); const dataTypeID = this.#reader.int32(); const dataTypeSize = this.#reader.int16(); const dataTypeModifier = this.#reader.int32(); const mode = this.#reader.int16() === 0 ? Modes2.text : Modes2.binary; return new Field(name, tableID, columnID, dataTypeID, dataTypeSize, dataTypeModifier, mode); } #parseParameterDescriptionMessage(offset, length, bytes) { this.#setBuffer(offset, bytes); const parameterCount = this.#reader.int16(); const dataTypeIDs = []; for (let i = 0;i < parameterCount; i++) { dataTypeIDs.push(this.#reader.int32()); } return new ParameterDescriptionMessage(length, dataTypeIDs); } #parseDataRowMessage(offset, length, bytes) { this.#setBuffer(offset, bytes); const fieldCount = this.#reader.int16(); const fields = []; for (let i = 0;i < fieldCount; i++) { const len = this.#reader.int32(); fields.push(len === -1 ? null : this.#reader.string(len)); } return new DataRowMessage(length, fields); } #parseParameterStatusMessage(offset, length, bytes) { this.#setBuffer(offset, bytes); const name = this.#reader.cstring(); const value = this.#reader.cstring(); return new ParameterStatusMessage(length, name, value); } #parseBackendKeyData(offset, length, bytes) { this.#setBuffer(offset, bytes); const processID = this.#reader.int32(); const secretKey = this.#reader.int32(); return new BackendKeyDataMessage(length, processID, secretKey); } #parseAuthenticationResponse(offset, length, bytes) { this.#setBuffer(offset, bytes); const code = this.#reader.int32(); switch (code) { case 0: return new AuthenticationOk(length); case 3: return new AuthenticationCleartextPassword(length); case 5: return new AuthenticationMD5Password(length, this.#reader.bytes(4)); case 10: { const mechanisms = []; for (;; ) { const mechanism = this.#reader.cstring(); if (mechanism.length === 0) { return new AuthenticationSASL(length, mechanisms); } mechanisms.push(mechanism); } } case 11: return new AuthenticationSASLContinue(length, this.#reader.string(length - 8)); case 12: return new AuthenticationSASLFinal(length, this.#reader.string(length - 8)); default: throw new Error(`Unknown authenticationOk message type ${code}`); } } #parseErrorMessage(offset, length, bytes, name) { this.#setBuffer(offset, bytes); const fields = {}; let fieldType = this.#reader.string(1); while (fieldType !== "\x00") { fields[fieldType] = this.#reader.cstring(); fieldType = this.#reader.string(1); } const messageValue = fields["M"]; const message = name === "notice" ? new NoticeMessage(length, messageValue) : new DatabaseError2(messageValue ?? "", length, name); message.severity = fields["S"]; message.code = fields["C"]; message.detail = fields["D"]; message.hint = fields["H"]; message.position = fields["P"]; message.internalPosition = fields["p"]; message.internalQuery = fields["q"]; message.where = fields["W"]; message.schema = fields["s"]; message.table = fields["t"]; message.column = fields["c"]; message.dataType = fields["d"]; message.constraint = fields["n"]; message.file = fields["F"]; message.line = fields["L"]; message.routine = fields["R"]; return message; } } export { Modes2, Parser2 }; //# debugId=9FFE7FE642E6BBAB64756E2164756E21