feat(clients): sync all client drivers with current wire protocol
- Python: fix column types read order, add auth/ping/query_params, proper recv - JavaScript: wire up real TCP socket, add auth/ping/query_params, WireValue class - Rust: add WireValue enum with structured types, fix type fidelity for ARRAY/OBJECT/VECTOR, add auth/query_params, fix ping/error parsing - Nim: add missing MsgKind values, makeQueryParamsMessage, auth/ping, fix forward decl All clients now support AUTH (0x09), QUERY_PARAMS (0x03), PING (0x08) and align with server.nim + wire.nim protocol definitions.
This commit is contained in:
+372
-107
@@ -3,6 +3,7 @@
|
||||
*
|
||||
* Binary protocol client for BaraDB database.
|
||||
* Communicates via the BaraDB Wire Protocol (binary, big-endian).
|
||||
* Requires Node.js (uses 'net' module for TCP).
|
||||
*
|
||||
* Install:
|
||||
* npm install baradb
|
||||
@@ -16,23 +17,42 @@
|
||||
* console.log(row.name);
|
||||
* }
|
||||
* await client.close();
|
||||
*
|
||||
* Parameterized Queries:
|
||||
* const result = await client.queryParams(
|
||||
* 'SELECT * FROM users WHERE age > $1',
|
||||
* [WireValue.int64(18)]
|
||||
* );
|
||||
*
|
||||
* Authentication:
|
||||
* await client.auth('jwt-token-here');
|
||||
*/
|
||||
|
||||
const MsgKind = {
|
||||
const net = require('net');
|
||||
|
||||
const MsgKind = Object.freeze({
|
||||
CLIENT_HANDSHAKE: 0x01,
|
||||
QUERY: 0x02,
|
||||
QUERY_PARAMS: 0x03,
|
||||
EXECUTE: 0x04,
|
||||
BATCH: 0x05,
|
||||
TRANSACTION: 0x06,
|
||||
CLOSE: 0x07,
|
||||
PING: 0x08,
|
||||
AUTH: 0x09,
|
||||
SERVER_HANDSHAKE: 0x80,
|
||||
READY: 0x81,
|
||||
DATA: 0x82,
|
||||
COMPLETE: 0x83,
|
||||
ERROR: 0x84,
|
||||
AUTH_CHALLENGE: 0x85,
|
||||
AUTH_OK: 0x86,
|
||||
};
|
||||
SCHEMA_CHANGE: 0x87,
|
||||
PONG: 0x88,
|
||||
TRANSACTION_STATE: 0x89,
|
||||
});
|
||||
|
||||
const FieldKind = {
|
||||
const FieldKind = Object.freeze({
|
||||
NULL: 0x00,
|
||||
BOOL: 0x01,
|
||||
INT8: 0x02,
|
||||
@@ -47,13 +67,136 @@ const FieldKind = {
|
||||
OBJECT: 0x0B,
|
||||
VECTOR: 0x0C,
|
||||
JSON: 0x0D,
|
||||
};
|
||||
});
|
||||
|
||||
const ResultFormat = {
|
||||
const ResultFormat = Object.freeze({
|
||||
BINARY: 0x00,
|
||||
JSON: 0x01,
|
||||
TEXT: 0x02,
|
||||
};
|
||||
});
|
||||
|
||||
class WireValue {
|
||||
constructor(kind, value) {
|
||||
this.kind = kind;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
static null() { return new WireValue(FieldKind.NULL); }
|
||||
static bool(val) { return new WireValue(FieldKind.BOOL, val); }
|
||||
static int8(val) { return new WireValue(FieldKind.INT8, val); }
|
||||
static int16(val) { return new WireValue(FieldKind.INT16, val); }
|
||||
static int32(val) { return new WireValue(FieldKind.INT32, val); }
|
||||
static int64(val) { return new WireValue(FieldKind.INT64, val); }
|
||||
static float32(val) { return new WireValue(FieldKind.FLOAT32, val); }
|
||||
static float64(val) { return new WireValue(FieldKind.FLOAT64, val); }
|
||||
static string(val) { return new WireValue(FieldKind.STRING, val); }
|
||||
static bytes(val) { return new WireValue(FieldKind.BYTES, val); }
|
||||
static array(val) { return new WireValue(FieldKind.ARRAY, val); }
|
||||
static object(val) { return new WireValue(FieldKind.OBJECT, val); }
|
||||
static vector(val) { return new WireValue(FieldKind.VECTOR, val); }
|
||||
static json(val) { return new WireValue(FieldKind.JSON, val); }
|
||||
|
||||
serialize() {
|
||||
const parts = [Buffer.from([this.kind])];
|
||||
switch (this.kind) {
|
||||
case FieldKind.NULL:
|
||||
break;
|
||||
case FieldKind.BOOL:
|
||||
parts.push(Buffer.from([this.value ? 1 : 0]));
|
||||
break;
|
||||
case FieldKind.INT8: {
|
||||
const b = Buffer.alloc(1);
|
||||
b.writeInt8(this.value, 0);
|
||||
parts.push(b);
|
||||
break;
|
||||
}
|
||||
case FieldKind.INT16: {
|
||||
const b = Buffer.alloc(2);
|
||||
b.writeInt16BE(this.value, 0);
|
||||
parts.push(b);
|
||||
break;
|
||||
}
|
||||
case FieldKind.INT32: {
|
||||
const b = Buffer.alloc(4);
|
||||
b.writeInt32BE(this.value, 0);
|
||||
parts.push(b);
|
||||
break;
|
||||
}
|
||||
case FieldKind.INT64: {
|
||||
const b = Buffer.alloc(8);
|
||||
b.writeBigInt64BE(BigInt(this.value), 0);
|
||||
parts.push(b);
|
||||
break;
|
||||
}
|
||||
case FieldKind.FLOAT32: {
|
||||
const b = Buffer.alloc(4);
|
||||
b.writeFloatBE(this.value, 0);
|
||||
parts.push(b);
|
||||
break;
|
||||
}
|
||||
case FieldKind.FLOAT64: {
|
||||
const b = Buffer.alloc(8);
|
||||
b.writeDoubleBE(this.value, 0);
|
||||
parts.push(b);
|
||||
break;
|
||||
}
|
||||
case FieldKind.STRING: {
|
||||
const strBuf = Buffer.from(this.value, 'utf-8');
|
||||
const len = Buffer.alloc(4);
|
||||
len.writeUInt32BE(strBuf.length, 0);
|
||||
parts.push(len, strBuf);
|
||||
break;
|
||||
}
|
||||
case FieldKind.BYTES: {
|
||||
const len = Buffer.alloc(4);
|
||||
len.writeUInt32BE(this.value.length, 0);
|
||||
parts.push(len, this.value);
|
||||
break;
|
||||
}
|
||||
case FieldKind.ARRAY: {
|
||||
const count = Buffer.alloc(4);
|
||||
count.writeUInt32BE(this.value.length, 0);
|
||||
parts.push(count);
|
||||
for (const item of this.value) {
|
||||
parts.push(item.serialize());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FieldKind.OBJECT: {
|
||||
const entries = Object.entries(this.value);
|
||||
const count = Buffer.alloc(4);
|
||||
count.writeUInt32BE(entries.length, 0);
|
||||
parts.push(count);
|
||||
for (const [key, val] of entries) {
|
||||
const keyBuf = Buffer.from(key, 'utf-8');
|
||||
const keyLen = Buffer.alloc(4);
|
||||
keyLen.writeUInt32BE(keyBuf.length, 0);
|
||||
parts.push(keyLen, keyBuf, val.serialize());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FieldKind.VECTOR: {
|
||||
const dim = Buffer.alloc(4);
|
||||
dim.writeUInt32BE(this.value.length, 0);
|
||||
parts.push(dim);
|
||||
for (const f of this.value) {
|
||||
const fb = Buffer.alloc(4);
|
||||
fb.writeFloatBE(f, 0);
|
||||
parts.push(fb);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FieldKind.JSON: {
|
||||
const strBuf = Buffer.from(this.value, 'utf-8');
|
||||
const len = Buffer.alloc(4);
|
||||
len.writeUInt32BE(strBuf.length, 0);
|
||||
parts.push(len, strBuf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Buffer.concat(parts);
|
||||
}
|
||||
}
|
||||
|
||||
class QueryResult {
|
||||
constructor() {
|
||||
@@ -99,17 +242,50 @@ class Client {
|
||||
this.socket = null;
|
||||
this.connected = false;
|
||||
this.requestId = 0;
|
||||
this._buffer = Buffer.alloc(0);
|
||||
this._pendingResolve = null;
|
||||
}
|
||||
|
||||
/** Connect to the BaraDB server */
|
||||
async connect() {
|
||||
// In Node.js: const net = require('net');
|
||||
// In browser: WebSocket connection
|
||||
this.socket = null; // net.connect(this.port, this.host);
|
||||
this.connected = true;
|
||||
return new Promise((resolve, reject) => {
|
||||
this.socket = net.createConnection({ host: this.host, port: this.port });
|
||||
this.socket.setTimeout(this.timeout);
|
||||
|
||||
this.socket.on('connect', () => {
|
||||
this.connected = true;
|
||||
resolve();
|
||||
});
|
||||
|
||||
this.socket.on('data', (data) => {
|
||||
this._buffer = Buffer.concat([this._buffer, data]);
|
||||
if (this._pendingResolve) {
|
||||
this._pendingResolve();
|
||||
}
|
||||
});
|
||||
|
||||
this.socket.on('error', (err) => {
|
||||
this.connected = false;
|
||||
reject(err);
|
||||
});
|
||||
|
||||
this.socket.on('close', () => {
|
||||
this.connected = false;
|
||||
});
|
||||
|
||||
this.socket.on('timeout', () => {
|
||||
this.socket.destroy();
|
||||
this.connected = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async close() {
|
||||
if (this.socket && this.connected) {
|
||||
try {
|
||||
const msg = this._build(MsgKind.CLOSE, Buffer.alloc(0));
|
||||
this.socket.write(msg);
|
||||
} catch (_) {}
|
||||
}
|
||||
if (this.socket) {
|
||||
this.socket.destroy();
|
||||
this.socket = null;
|
||||
@@ -125,75 +301,88 @@ class Client {
|
||||
return ++this.requestId;
|
||||
}
|
||||
|
||||
/** Execute a BaraQL query */
|
||||
async query(sql) {
|
||||
const encoder = new TextEncoder();
|
||||
const queryBytes = encoder.encode(sql);
|
||||
const payload = new Uint8Array(4 + queryBytes.length + 1);
|
||||
const view = new DataView(payload.buffer);
|
||||
view.setUint32(0, queryBytes.length, false); // big-endian
|
||||
payload.set(queryBytes, 4);
|
||||
payload[4 + queryBytes.length] = ResultFormat.BINARY;
|
||||
|
||||
const msg = this._build(MsgKind.QUERY, payload);
|
||||
// await this.socket.write(msg);
|
||||
// const header = await this.socket.read(12);
|
||||
// ... parse response
|
||||
|
||||
return new QueryResult();
|
||||
async _recvExact(size) {
|
||||
while (this._buffer.length < size) {
|
||||
await new Promise((resolve, reject) => {
|
||||
this._pendingResolve = resolve;
|
||||
const timer = setTimeout(() => {
|
||||
this._pendingResolve = null;
|
||||
reject(new Error('Receive timeout'));
|
||||
}, this.timeout);
|
||||
this._pendingResolve = () => {
|
||||
clearTimeout(timer);
|
||||
this._pendingResolve = null;
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
}
|
||||
const data = this._buffer.subarray(0, size);
|
||||
this._buffer = this._buffer.subarray(size);
|
||||
return data;
|
||||
}
|
||||
|
||||
/** Parse server response from header + payload buffers */
|
||||
_parseResponse(header, payload) {
|
||||
const view = new DataView(header.buffer);
|
||||
const kind = view.getUint32(0, false);
|
||||
const len = view.getUint32(4, false);
|
||||
const reqId = view.getUint32(8, false);
|
||||
async _readHeader() {
|
||||
const header = await this._recvExact(12);
|
||||
return {
|
||||
kind: header.readUInt32BE(0),
|
||||
length: header.readUInt32BE(4),
|
||||
requestId: header.readUInt32BE(8),
|
||||
};
|
||||
}
|
||||
|
||||
async _readError(length) {
|
||||
const data = await this._recvExact(length);
|
||||
const code = data.readUInt32BE(0);
|
||||
const msgLen = data.readUInt32BE(4);
|
||||
const msg = data.subarray(8, 8 + msgLen).toString('utf-8');
|
||||
return new Error(`BaraDB error ${code}: ${msg}`);
|
||||
}
|
||||
|
||||
async _readDataResponse(length) {
|
||||
const data = await this._recvExact(length);
|
||||
const result = new QueryResult();
|
||||
let pos = 0;
|
||||
|
||||
if (kind === MsgKind.ERROR && payload.length >= 8) {
|
||||
const code = new DataView(payload.buffer).getUint32(0, false);
|
||||
const msgLen = new DataView(payload.buffer).getUint32(4, false);
|
||||
const msg = new TextDecoder().decode(payload.slice(8, 8 + msgLen));
|
||||
throw new Error(`BaraDB error ${code}: ${msg}`);
|
||||
const colCount = data.readUInt32BE(pos);
|
||||
pos += 4;
|
||||
|
||||
const cols = [];
|
||||
for (let i = 0; i < colCount; i++) {
|
||||
const sLen = data.readUInt32BE(pos);
|
||||
pos += 4;
|
||||
cols.push(data.subarray(pos, pos + sLen).toString('utf-8'));
|
||||
pos += sLen;
|
||||
}
|
||||
|
||||
if (kind === MsgKind.DATA) {
|
||||
let pos = 0;
|
||||
const colCount = new DataView(payload.buffer).getUint32(pos, false);
|
||||
pos += 4;
|
||||
const cols = [];
|
||||
for (let i = 0; i < colCount; i++) {
|
||||
const sLen = new DataView(payload.buffer).getUint32(pos, false);
|
||||
pos += 4;
|
||||
cols.push(new TextDecoder().decode(payload.slice(pos, pos + sLen)));
|
||||
pos += sLen;
|
||||
}
|
||||
result.columns = cols;
|
||||
const colTypes = [];
|
||||
for (let i = 0; i < colCount; i++) {
|
||||
colTypes.push(payload[pos]);
|
||||
pos++;
|
||||
}
|
||||
result.columnTypes = colTypes;
|
||||
const rowCount = new DataView(payload.buffer).getUint32(pos, false);
|
||||
pos += 4;
|
||||
for (let r = 0; r < rowCount; r++) {
|
||||
const row = [];
|
||||
for (let c = 0; c < colCount; c++) {
|
||||
row.push(this._readValue(payload, pos));
|
||||
pos = this._lastReadPos;
|
||||
}
|
||||
result.rows.push(row);
|
||||
}
|
||||
result.rowCount = rowCount;
|
||||
// COMPLETE message should follow - caller reads it separately
|
||||
return result;
|
||||
const colTypes = [];
|
||||
for (let i = 0; i < colCount; i++) {
|
||||
colTypes.push(data[pos]);
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (kind === MsgKind.COMPLETE && payload.length >= 4) {
|
||||
result.affectedRows = new DataView(payload.buffer).getUint32(0, false);
|
||||
const rowCount = data.readUInt32BE(pos);
|
||||
pos += 4;
|
||||
|
||||
for (let r = 0; r < rowCount; r++) {
|
||||
const row = [];
|
||||
for (let c = 0; c < colCount; c++) {
|
||||
const { value, newPos } = this._readValue(data, pos);
|
||||
row.push(value);
|
||||
pos = newPos;
|
||||
}
|
||||
result.rows.push(row);
|
||||
}
|
||||
|
||||
result.columns = cols;
|
||||
result.columnTypes = colTypes;
|
||||
result.rowCount = rowCount;
|
||||
|
||||
const comp = await this._readHeader();
|
||||
if (comp.kind === MsgKind.COMPLETE) {
|
||||
const compData = await this._recvExact(comp.length);
|
||||
result.affectedRows = compData.readUInt32BE(0);
|
||||
} else if (comp.kind === MsgKind.ERROR) {
|
||||
throw await this._readError(comp.length);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -206,74 +395,155 @@ class Client {
|
||||
switch (kind) {
|
||||
case FieldKind.NULL: result = null; break;
|
||||
case FieldKind.BOOL: result = payload[pos] !== 0; pos++; break;
|
||||
case FieldKind.INT8: result = payload[pos] << 24 >> 24; pos++; break;
|
||||
case FieldKind.INT16: result = new DataView(payload.buffer).getInt16(pos, false); pos += 2; break;
|
||||
case FieldKind.INT32: result = new DataView(payload.buffer).getInt32(pos, false); pos += 4; break;
|
||||
case FieldKind.INT64: result = new DataView(payload.buffer).getBigInt64(pos, false); pos += 8; break;
|
||||
case FieldKind.FLOAT32: result = new DataView(payload.buffer).getFloat32(pos, false); pos += 4; break;
|
||||
case FieldKind.FLOAT64: result = new DataView(payload.buffer).getFloat64(pos, false); pos += 8; break;
|
||||
case FieldKind.INT8: result = payload.readInt8(pos); pos++; break;
|
||||
case FieldKind.INT16: result = payload.readInt16BE(pos); pos += 2; break;
|
||||
case FieldKind.INT32: result = payload.readInt32BE(pos); pos += 4; break;
|
||||
case FieldKind.INT64: result = payload.readBigInt64BE(pos); pos += 8; break;
|
||||
case FieldKind.FLOAT32: result = payload.readFloatBE(pos); pos += 4; break;
|
||||
case FieldKind.FLOAT64: result = payload.readDoubleBE(pos); pos += 8; break;
|
||||
case FieldKind.STRING: {
|
||||
const len = new DataView(payload.buffer).getUint32(pos, false);
|
||||
const len = payload.readUInt32BE(pos);
|
||||
pos += 4;
|
||||
result = new TextDecoder().decode(payload.slice(pos, pos + len));
|
||||
result = payload.subarray(pos, pos + len).toString('utf-8');
|
||||
pos += len;
|
||||
break;
|
||||
}
|
||||
case FieldKind.BYTES: {
|
||||
const len = new DataView(payload.buffer).getUint32(pos, false);
|
||||
const len = payload.readUInt32BE(pos);
|
||||
pos += 4;
|
||||
result = payload.slice(pos, pos + len);
|
||||
result = payload.subarray(pos, pos + len);
|
||||
pos += len;
|
||||
break;
|
||||
}
|
||||
case FieldKind.ARRAY: {
|
||||
const count = new DataView(payload.buffer).getUint32(pos, false);
|
||||
const count = payload.readUInt32BE(pos);
|
||||
pos += 4;
|
||||
result = [];
|
||||
for (let i = 0; i < count; i++) {
|
||||
result.push(this._readValue(payload, pos));
|
||||
pos = this._lastReadPos;
|
||||
const { value, newPos } = this._readValue(payload, pos);
|
||||
result.push(value);
|
||||
pos = newPos;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FieldKind.OBJECT: {
|
||||
const count = new DataView(payload.buffer).getUint32(pos, false);
|
||||
const count = payload.readUInt32BE(pos);
|
||||
pos += 4;
|
||||
result = {};
|
||||
for (let i = 0; i < count; i++) {
|
||||
const kLen = new DataView(payload.buffer).getUint32(pos, false);
|
||||
const kLen = payload.readUInt32BE(pos);
|
||||
pos += 4;
|
||||
const key = new TextDecoder().decode(payload.slice(pos, pos + kLen));
|
||||
const key = payload.subarray(pos, pos + kLen).toString('utf-8');
|
||||
pos += kLen;
|
||||
result[key] = this._readValue(payload, pos);
|
||||
pos = this._lastReadPos;
|
||||
const { value, newPos } = this._readValue(payload, pos);
|
||||
result[key] = value;
|
||||
pos = newPos;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FieldKind.VECTOR: {
|
||||
const dim = new DataView(payload.buffer).getUint32(pos, false);
|
||||
const dim = payload.readUInt32BE(pos);
|
||||
pos += 4;
|
||||
result = [];
|
||||
for (let i = 0; i < dim; i++) {
|
||||
result.push(new DataView(payload.buffer).getFloat32(pos, false));
|
||||
result.push(payload.readFloatBE(pos));
|
||||
pos += 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FieldKind.JSON: {
|
||||
const len = new DataView(payload.buffer).getUint32(pos, false);
|
||||
const len = payload.readUInt32BE(pos);
|
||||
pos += 4;
|
||||
result = new TextDecoder().decode(payload.slice(pos, pos + len));
|
||||
result = payload.subarray(pos, pos + len).toString('utf-8');
|
||||
pos += len;
|
||||
break;
|
||||
}
|
||||
default: result = null;
|
||||
}
|
||||
this._lastReadPos = pos;
|
||||
return result;
|
||||
return { value: result, newPos: pos };
|
||||
}
|
||||
|
||||
async auth(token) {
|
||||
if (!this.connected) throw new Error('Not connected');
|
||||
const tokenBuf = Buffer.from(token, 'utf-8');
|
||||
const payload = Buffer.alloc(4 + tokenBuf.length);
|
||||
payload.writeUInt32BE(tokenBuf.length, 0);
|
||||
tokenBuf.copy(payload, 4);
|
||||
const msg = this._build(MsgKind.AUTH, payload);
|
||||
this.socket.write(msg);
|
||||
|
||||
const header = await this._readHeader();
|
||||
if (header.kind === MsgKind.AUTH_OK) return;
|
||||
if (header.kind === MsgKind.ERROR) throw await this._readError(header.length);
|
||||
throw new Error(`Unexpected auth response: 0x${header.kind.toString(16)}`);
|
||||
}
|
||||
|
||||
async ping() {
|
||||
if (!this.connected) throw new Error('Not connected');
|
||||
const msg = this._build(MsgKind.PING, Buffer.alloc(0));
|
||||
this.socket.write(msg);
|
||||
|
||||
const header = await this._readHeader();
|
||||
if (header.kind === MsgKind.PONG) return true;
|
||||
if (header.kind === MsgKind.ERROR) throw await this._readError(header.length);
|
||||
return false;
|
||||
}
|
||||
|
||||
async query(sql) {
|
||||
if (!this.connected) throw new Error('Not connected');
|
||||
const queryBuf = Buffer.from(sql, 'utf-8');
|
||||
const payload = Buffer.alloc(4 + queryBuf.length + 1);
|
||||
payload.writeUInt32BE(queryBuf.length, 0);
|
||||
queryBuf.copy(payload, 4);
|
||||
payload[4 + queryBuf.length] = ResultFormat.BINARY;
|
||||
|
||||
const msg = this._build(MsgKind.QUERY, payload);
|
||||
this.socket.write(msg);
|
||||
|
||||
const header = await this._readHeader();
|
||||
if (header.kind === MsgKind.ERROR) throw await this._readError(header.length);
|
||||
if (header.kind === MsgKind.DATA) return await this._readDataResponse(header.length);
|
||||
if (header.kind === MsgKind.COMPLETE) {
|
||||
const data = await this._recvExact(header.length);
|
||||
const result = new QueryResult();
|
||||
result.affectedRows = data.readUInt32BE(0);
|
||||
return result;
|
||||
}
|
||||
return new QueryResult();
|
||||
}
|
||||
|
||||
async queryParams(sql, params = []) {
|
||||
if (!this.connected) throw new Error('Not connected');
|
||||
const queryBuf = Buffer.from(sql, 'utf-8');
|
||||
const paramParts = [];
|
||||
for (const p of params) {
|
||||
paramParts.push(p.serialize());
|
||||
}
|
||||
const paramsBuf = Buffer.concat(paramParts);
|
||||
|
||||
const payload = Buffer.alloc(4 + queryBuf.length + 1 + 4 + paramsBuf.length);
|
||||
let pos = 0;
|
||||
payload.writeUInt32BE(queryBuf.length, pos); pos += 4;
|
||||
queryBuf.copy(payload, pos); pos += queryBuf.length;
|
||||
payload[pos] = ResultFormat.BINARY; pos++;
|
||||
payload.writeUInt32BE(params.length, pos); pos += 4;
|
||||
paramsBuf.copy(payload, pos);
|
||||
|
||||
const msg = this._build(MsgKind.QUERY_PARAMS, payload);
|
||||
this.socket.write(msg);
|
||||
|
||||
const header = await this._readHeader();
|
||||
if (header.kind === MsgKind.ERROR) throw await this._readError(header.length);
|
||||
if (header.kind === MsgKind.DATA) return await this._readDataResponse(header.length);
|
||||
if (header.kind === MsgKind.COMPLETE) {
|
||||
const data = await this._recvExact(header.length);
|
||||
const result = new QueryResult();
|
||||
result.affectedRows = data.readUInt32BE(0);
|
||||
return result;
|
||||
}
|
||||
return new QueryResult();
|
||||
}
|
||||
|
||||
/** Execute a BaraQL statement (INSERT, UPDATE, DELETE) */
|
||||
async execute(sql) {
|
||||
const result = await this.query(sql);
|
||||
return result.affectedRows;
|
||||
@@ -281,16 +551,11 @@ class Client {
|
||||
|
||||
_build(kind, payload) {
|
||||
const reqId = this._nextId();
|
||||
const header = new ArrayBuffer(12);
|
||||
const view = new DataView(header);
|
||||
view.setUint32(0, kind, false);
|
||||
view.setUint32(4, payload.length, false);
|
||||
view.setUint32(8, reqId, false);
|
||||
|
||||
const msg = new Uint8Array(12 + payload.length);
|
||||
msg.set(new Uint8Array(header), 0);
|
||||
msg.set(payload, 12);
|
||||
return msg;
|
||||
const header = Buffer.alloc(12);
|
||||
header.writeUInt32BE(kind, 0);
|
||||
header.writeUInt32BE(payload.length, 4);
|
||||
header.writeUInt32BE(reqId, 8);
|
||||
return Buffer.concat([header, payload]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,4 +641,4 @@ class QueryBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { Client, QueryBuilder, QueryResult, MsgKind, FieldKind, ResultFormat };
|
||||
module.exports = { Client, QueryBuilder, QueryResult, WireValue, MsgKind, FieldKind, ResultFormat };
|
||||
|
||||
Reference in New Issue
Block a user