@tdengine/websocket
Version:
The websocket Node.js connector for TDengine. TDengine versions 3.3.2.0 and above are recommended to use this connector.
330 lines (329 loc) • 14.5 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TopicPartition = exports.CommittedResp = exports.PartitionsResp = exports.SubscriptionResp = exports.AssignmentResp = exports.WSTmqFetchBlockInfo = exports.TaosTmqResult = exports.WsTmqQueryResponse = exports.WsPollResponse = void 0;
const wsResponse_1 = require("../client/wsResponse");
const constant_1 = require("../common/constant");
const taosResult_1 = require("../common/taosResult");
const wsError_1 = require("../common/wsError");
const constant_2 = require("./constant");
const utils_1 = require("../common/utils");
const log_1 = __importDefault(require("../common/log"));
class WsPollResponse {
constructor(resp) {
this.totalTime = resp.totalTime;
this.code = resp.msg.code;
this.message = resp.msg.message;
this.action = resp.msg.action;
this.req_id = resp.msg.req_id;
this.have_message = resp.msg.have_message;
this.topic = resp.msg.topic;
this.database = resp.msg.database;
this.vgroup_id = resp.msg.vgroup_id;
this.message_id = resp.msg.message_id;
this.message_type = resp.msg.message_type;
if (resp.msg.id) {
this.id = BigInt(resp.msg.id);
}
else {
this.id = BigInt(0);
}
}
}
exports.WsPollResponse = WsPollResponse;
// resp: {"code":0,"message":"","action":"fetch","req_id":4,"message_id":1,"completed":false,"table_name":"ct2","rows":1,"fields_count":4,"fields_names":["ts","c1","c2","c3"],"fields_types":[9,4,6,8],"fields_lengths":[8,4,4,10],"precision":0}
class WsTmqQueryResponse extends wsResponse_1.WSQueryResponse {
constructor(resp) {
super(resp);
this.completed = resp.msg.completed;
this.table_name = resp.msg.table_name;
this.rows = resp.msg.rows;
this.message_id = resp.msg.message_id;
}
}
exports.WsTmqQueryResponse = WsTmqQueryResponse;
class TaosTmqResult extends taosResult_1.TaosResult {
constructor(pollResp) {
super();
this.setTopic(pollResp.topic);
this.database = pollResp.database;
this.vgroup_id = pollResp.vgroup_id;
}
}
exports.TaosTmqResult = TaosTmqResult;
class WSTmqFetchBlockInfo {
constructor(dataView, taosResult) {
// this.totalTime = resp.totalTime
// this.blockData = resp.msg
this.textDecoder = new TextDecoder();
this.taosResult = taosResult;
this.schema = [];
this.schemaLen = 0;
let blockDataView = this.skipHead(dataView);
this.rows = this.parseBlockInfos(blockDataView);
}
getRows() {
return this.rows;
}
skipHead(dataView) {
let v = dataView.getUint8(0);
if (v >= 100) {
let skip = dataView.getUint32(1, true);
return new DataView(dataView.buffer, dataView.byteOffset + skip + 5);
}
let skip1 = this.getTypeSkip(v);
v = dataView.getUint8(1 + skip1);
let skip2 = this.getTypeSkip(v);
return new DataView(dataView.buffer, dataView.byteOffset + skip1 + 2 + skip2);
}
getTypeSkip(v) {
switch (v) {
case 1:
return 8;
case 2:
case 3:
return 16;
default:
throw (new wsError_1.TDWebSocketClientError(wsError_1.ErrorCode.ERR_INVALID_FETCH_MESSAGE_DATA, `FetchBlockRawResp getTypeSkip error, type: ${v}`));
}
}
parseBlockInfos(dataView) {
let blockNum = dataView.getUint32(0, true);
if (blockNum == 0) {
return 0;
}
this.withTableName = dataView.getUint8(4) == 1 ? true : false;
this.withSchema = dataView.getUint8(5) == 1 ? true : false;
// let dataBuffer = dataView.buffer.slice(6)
let dataBuffer = new DataView(dataView.buffer, dataView.byteOffset + 6);
let rows = 0;
// const parseStartTime = new Date().getTime();
// console.log("parseBlockInfos blockNum="+ blockNum)
for (let i = 0; i < blockNum; i++) {
let variableInfo = this.parseVariableByteInteger(dataBuffer);
this.taosResult.setPrecision(variableInfo[1].getUint8(17));
dataView = new DataView(variableInfo[1].buffer, variableInfo[1].byteOffset + 17);
let offset = variableInfo[0] - 17;
dataBuffer = this.parseSchemaInfo(dataView, offset);
rows += this.parseTmqBlock(dataView, 1);
}
// const parseEndTime = new Date().getTime();
// console.log("------------->", parseEndTime- parseStartTime, rows);
log_1.default.info("parseBlockInfos blockNum=" + blockNum + ", withTableName=" + this.withTableName + ", withSchema=" + this.withSchema + ", rows=" + rows);
return rows;
}
parseSchemaInfo(dataBuffer, offset) {
if (this.withSchema) {
let isSkip = this.schema.length > 0;
if (!isSkip) {
dataBuffer = new DataView(dataBuffer.buffer, dataBuffer.byteOffset + offset);
let variableInfo = this.parseVariableByteInteger(dataBuffer);
this.schemaLen = variableInfo[2];
let cols = (0, utils_1.zigzagDecode)(variableInfo[0]);
variableInfo = this.parseVariableByteInteger(variableInfo[1]);
this.schemaLen += variableInfo[2];
let dataView = variableInfo[1];
for (let index = 0; index < cols; index++) {
let schema = new constant_2.TMQRawDataSchema();
schema.colType = dataView.getInt8(0);
schema.flag = dataView.getInt8(1);
variableInfo = this.parseVariableByteInteger(dataView, 2);
this.schemaLen += 2 + variableInfo[2];
schema.bytes = BigInt((0, utils_1.zigzagDecode)(variableInfo[0]));
variableInfo = this.parseVariableByteInteger(variableInfo[1]);
this.schemaLen += variableInfo[2];
schema.colID = (0, utils_1.zigzagDecode)(variableInfo[0]);
variableInfo = this.parseVariableByteInteger(variableInfo[1]);
this.schemaLen += variableInfo[2];
schema.name = (0, taosResult_1.getString)(variableInfo[1], 0, variableInfo[0], this.textDecoder);
if (!isSkip) {
this.taosResult.setMeta({
name: schema.name,
type: schema.colType,
length: Number(schema.bytes)
});
this.schema.push(schema);
}
dataView = new DataView(variableInfo[1].buffer, variableInfo[1].byteOffset + variableInfo[0]);
this.schemaLen += variableInfo[0];
}
if (this.withTableName) {
variableInfo = this.parseVariableByteInteger(dataView);
this.schemaLen += variableInfo[2];
this.tableName = (0, taosResult_1.readVarchar)(variableInfo[1].buffer, variableInfo[1].byteOffset, variableInfo[0], this.textDecoder);
dataView = new DataView(variableInfo[1].buffer, variableInfo[1].byteOffset + variableInfo[0]);
this.schemaLen += variableInfo[0];
}
return dataView;
}
else {
return new DataView(dataBuffer.buffer, dataBuffer.byteOffset + this.schemaLen + offset);
}
}
return dataBuffer;
}
parseVariableByteInteger(dataView, offset = 0) {
let value = 0;
let multiplier = 1;
let count = 0;
while (true) {
let encodedByte = dataView.getUint8(count + offset);
value += (encodedByte & 127) * multiplier;
if ((encodedByte & 128) == 0) {
break;
}
multiplier *= 128;
count++;
}
return [value, new DataView(dataView.buffer, dataView.byteOffset + count + 1 + offset), count + 1];
}
parseTmqBlock(dataView, startOffset) {
// let dataView = new DataView(dataBuffer)
let rows = dataView.getInt32(8 + startOffset, true);
if (rows == 0) {
return rows;
}
let taosData = this.taosResult.getData();
let metaData = this.taosResult.getMeta();
if (metaData && rows && taosData) {
let dataList = new Array(rows);
//get bitmap length
let bitMapOffset = getBitmapLen(rows);
//skip data head
let bufferOffset = 28 + 5 * this.schema.length + startOffset;
let metaLens = [];
for (let i = 0; i < this.schema.length; i++) {
//get data len
metaLens.push(dataView.getInt32(bufferOffset + i * 4, true));
}
bufferOffset += this.schema.length * 4;
for (let i = 0; i < this.schema.length; i++) {
let data = [];
//get type code
let isVarType = (0, taosResult_1._isVarType)(this.schema[i].colType);
//fixed length type
if (isVarType == constant_1.ColumnsBlockType.SOLID) {
// let bitMapArr = dataBuffer.slice(bufferOffset, bufferOffset + bitMapOffset);
let bitMapArr = new Uint8Array(dataView.buffer, dataView.byteOffset + bufferOffset, bitMapOffset);
bufferOffset += bitMapOffset;
//decode column data, data is array
data = (0, taosResult_1.readSolidDataToArray)(dataView, bufferOffset, rows, this.schema[i].colType, bitMapArr);
}
else {
//Variable length type
let start = bufferOffset;
let offsets = [];
for (let i = 0; i < rows; i++, start += constant_1.TDengineTypeLength['INT']) {
//get data length, -1 is null
offsets.push(dataView.getInt32(start, true));
}
for (let i = 0; i < rows; i++) {
let value = '';
if (-1 == offsets[i]) {
value = null;
}
else {
let header = start + offsets[i];
let dataLength = dataView.getInt16(header, true) & 0xFFFF;
if (isVarType == constant_1.ColumnsBlockType.VARCHAR) {
//decode var char
value = (0, taosResult_1.readVarchar)(dataView.buffer, dataView.byteOffset + header + 2, dataLength, this.textDecoder);
}
else if (isVarType == constant_1.ColumnsBlockType.GEOMETRY || isVarType == constant_1.ColumnsBlockType.VARBINARY) {
//decode binary
value = (0, taosResult_1.readBinary)(dataView.buffer, dataView.byteOffset + header + 2, dataLength);
}
else {
//decode nchar
value = (0, taosResult_1.readNchar)(dataView.buffer, dataView.byteOffset + header + 2, dataLength);
}
}
data.push(value);
}
bufferOffset += rows * 4;
}
bufferOffset += metaLens[i];
//column data to row data
for (let row = 0; row < data.length; row++) {
if (dataList[row] == null) {
dataList[row] = [];
}
dataList[row].push(data[row]);
}
}
taosData.push(...dataList);
}
return rows;
}
}
exports.WSTmqFetchBlockInfo = WSTmqFetchBlockInfo;
class AssignmentResp {
constructor(resp, topic) {
this.timing = BigInt(resp.msg.timing);
this.code = resp.msg.code;
this.message = resp.msg.message;
this.req_id = resp.msg.req_id;
this.action = resp.msg.action;
this.totalTime = resp.totalTime;
this.topicPartition = resp.msg.assignment;
for (let i in this.topicPartition) {
this.topicPartition[i].topic = topic;
}
}
}
exports.AssignmentResp = AssignmentResp;
class SubscriptionResp {
constructor(resp) {
this.timing = BigInt(resp.msg.timing);
this.code = resp.msg.code;
this.message = resp.msg.message;
this.req_id = resp.msg.req_id;
this.action = resp.msg.action;
this.totalTime = resp.totalTime;
this.topics = resp.msg.topics;
}
}
exports.SubscriptionResp = SubscriptionResp;
class PartitionsResp {
constructor(resp) {
this.timing = BigInt(resp.msg.timing);
this.code = resp.msg.code;
this.message = resp.msg.message;
this.req_id = resp.msg.req_id;
this.action = resp.msg.action;
this.totalTime = resp.totalTime;
this.positions = resp.msg.position;
}
setTopicPartitions(topicPartitions) {
if (topicPartitions.length != this.positions.length) {
throw new wsError_1.WebSocketInterfaceError(wsError_1.ErrorCode.ERR_PARTITIONS_TOPIC_VGROUP_LENGTH_NOT_EQUAL, 'TopicPartitions and positions are not equal in length');
}
for (let i in this.positions) {
topicPartitions[i].offset = this.positions[i];
}
return topicPartitions;
}
}
exports.PartitionsResp = PartitionsResp;
class CommittedResp extends PartitionsResp {
constructor(resp) {
super(resp);
this.positions = resp.msg.committed;
}
}
exports.CommittedResp = CommittedResp;
class TopicPartition {
constructor(msg) {
this.vgroup_id = msg.vgroup_id;
this.offset = msg.offset;
this.begin = msg.begin;
this.end = msg.end;
this.topic = '';
}
}
exports.TopicPartition = TopicPartition;
function getBitmapLen(n) {
return (n + 0x7) >> 3;
}