@pgxsinkit/pgwasm
Version:
374 lines (366 loc) • 12.8 kB
JavaScript
// @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