Skip to content

Commit 1810ce2

Browse files
authored
feat: supports decimal type (#88)
* feat: add timezone to the connection parameters * feat: modify test ip * feat: modify user test case * feat: modify conntect pool key * feat: modify test case ip * feat: add set connect option * feat: modify url param adapter * feat: add bind exception cases * feat: modify test case note * feat: add set timezone cases * feat: add set default db * feat: add clear timezone config * feat: delete invalid code * feat: support decimal type * feat: tmq support decimal type * feat: add test case check * feat: modify test case ip * feat: modiy cloud test case * feat: modiy sql test case * feat: modiy query test case * feat: modiy decimal case ip * feat: format code * feat: modify and parse decimal comments
1 parent 28a2d3a commit 1810ce2

15 files changed

Lines changed: 294 additions & 61 deletions

File tree

nodejs/src/client/wsClient.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ export class WsClient {
6161
throw(new TDWebSocketClientError(ErrorCode.ERR_WEBSOCKET_CONNECTION_FAIL, `connection creation failed, url: ${this._url}, code:${e.code}, msg:${e.message}`));
6262
}
6363

64-
6564
}
6665

6766
async setOptionConnection(option: TSDB_OPTION_CONNECTION, value: string | null): Promise<void> {

nodejs/src/client/wsResponse.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ export class WSQueryResponse {
3333
fields_names?: Array<string> | null;
3434
fields_types?: Array<number> | null;
3535
fields_lengths?: Array<number> | null;
36+
fields_precisions?: Array<bigint> | null;
37+
fields_scales?: Array<bigint> | null;
3638
precision?: number;
3739

3840
constructor(resp:MessageResp) {
@@ -58,6 +60,8 @@ export class WSQueryResponse {
5860
this.fields_types = msg.fields_types;
5961
this.fields_lengths = msg.fields_lengths;
6062
this.precision = msg.precision;
63+
this.fields_precisions = msg.fields_precisions ? msg.fields_precisions.map((p: number) => BigInt(p)) : [];
64+
this.fields_scales = msg.fields_scales ? msg.fields_scales.map((s: number) => BigInt(s)) : [];
6165
}
6266
}
6367

nodejs/src/common/constant.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ export enum TDengineTypeCode {
5858
BIGINT_UNSIGNED = 14,
5959
JSON = 15,
6060
VARBINARY = 16,
61+
DECIMAL = 17,
6162
GEOMETRY = 20,
63+
DECIMAL64 = 21,
6264
}
6365

6466
export enum TSDB_OPTION_CONNECTION {

nodejs/src/common/taosResult.ts

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { ColumnsBlockType, TDengineTypeCode, TDengineTypeName } from './constant
33
import { ErrorCode, TaosResultError, WebSocketQueryInterFaceError } from "./wsError";
44
import { appendRune } from "./ut8Helper"
55
import logger from "./log";
6+
import { decimalToString } from "./utils";
7+
import { TMQRawDataSchema } from "../tmq/constant";
68

79
export interface TDengineMeta {
810
name: string,
@@ -29,7 +31,9 @@ export class TaosResult {
2931
private _precision: number | null | undefined;
3032
protected _affectRows: number | null | undefined;
3133
private _totalTime = 0;
32-
34+
private fields_precisions?: Array<bigint> | null;
35+
private fields_scales?: Array<bigint> | null;
36+
3337
/** unit nano seconds */
3438
private _timing: bigint | null | undefined;
3539
constructor(queryResponse?: WSQueryResponse) {
@@ -65,6 +69,8 @@ export class TaosResult {
6569
this._timing = queryResponse.timing
6670
this._precision = queryResponse.precision
6771
this._totalTime = queryResponse.totalTime
72+
this.fields_precisions = queryResponse.fields_precisions
73+
this.fields_scales = queryResponse.fields_scales
6874
}
6975

7076
public setPrecision(precision: number) {
@@ -134,6 +140,14 @@ export class TaosResult {
134140
this._timing = this._timing + timing
135141
}
136142
}
143+
144+
public getFieldsScales(index: number): bigint | null {
145+
if (this.fields_scales) {
146+
return this.fields_scales[index];
147+
}
148+
return null;
149+
}
150+
137151
/**
138152
* Mapping the WebSocket response type code to TDengine's type name.
139153
*/
@@ -201,7 +215,7 @@ export function parseBlock(blocks: WSFetchBlockResponse, taosResult: TaosResult)
201215
if (bitFlag == 1) {
202216
row.push("NULL")
203217
} else {
204-
row.push(readSolidData(dataView, colDataHead, metaList[j]))
218+
row.push(readSolidData(dataView, colDataHead, metaList[j], taosResult.getFieldsScales(j)));
205219
}
206220

207221
colBlockHead = colBlockHead + bitMapSize + dataView.getInt32(INT_32_SIZE * j, true)
@@ -261,7 +275,7 @@ export function _isVarType(metaType: number): Number {
261275
}
262276
}
263277
export function readSolidDataToArray(dataBuffer: DataView, colBlockHead:number,
264-
rows:number, metaType: number, bitMapArr: Uint8Array): any[] {
278+
rows:number, metaType: number, bitMapArr: Uint8Array, startOffset: number, colIndex: number): any[] {
265279

266280
let result:any[] = []
267281
switch (metaType) {
@@ -369,14 +383,40 @@ export function readSolidDataToArray(dataBuffer: DataView, colBlockHead:number,
369383
}
370384
break;
371385
}
386+
case TDengineTypeCode.DECIMAL64: {
387+
let scale = getScaleFromRowBlock(dataBuffer, colIndex, startOffset);
388+
for (let i = 0; i < rows; i++, colBlockHead += 8) {
389+
if (isNull(bitMapArr, i)) {
390+
result.push(null);
391+
}else{
392+
let decimalVal = dataBuffer.getBigInt64(colBlockHead, true)
393+
result.push(decimalToString(decimalVal.toString(), BigInt(scale)));
394+
}
395+
}
396+
break;
397+
}
398+
case TDengineTypeCode.DECIMAL: {
399+
let scale = getScaleFromRowBlock(dataBuffer, colIndex, startOffset);
400+
for (let i = 0; i < rows; i++, colBlockHead += 16) {
401+
if (isNull(bitMapArr, i)) {
402+
result.push(null);
403+
}else{
404+
let decimalHighPart = dataBuffer.getBigInt64(colBlockHead + 8, true);
405+
const decimalLowPart = dataBuffer.getBigUint64(colBlockHead, true);
406+
const decimalCombined = (decimalHighPart << 64n) | decimalLowPart;
407+
result.push(decimalToString(decimalCombined.toString(), BigInt(scale)));
408+
}
409+
}
410+
break;
411+
}
372412
default: {
373-
throw new WebSocketQueryInterFaceError(ErrorCode.ERR_UNSUPPORTED_TDENGINE_TYPE, `unspported type ${metaType}`)
413+
throw new WebSocketQueryInterFaceError(ErrorCode.ERR_UNSUPPORTED_TDENGINE_TYPE, `unsupported type ${metaType}`)
374414
}
375415
}
376416
return result;
377417

378418
}
379-
export function readSolidData(dataBuffer: DataView, colDataHead: number, meta: ResponseMeta): Number | Boolean | BigInt {
419+
export function readSolidData(dataBuffer: DataView, colDataHead: number, meta: ResponseMeta, fields_scale: bigint | null): Number | Boolean | BigInt | string {
380420

381421
switch (meta.type) {
382422
case TDengineTypeCode.BOOL: {
@@ -416,8 +456,18 @@ export function readSolidData(dataBuffer: DataView, colDataHead: number, meta: R
416456
return dataBuffer.getBigInt64(colDataHead, true);
417457
// could change
418458
}
459+
case TDengineTypeCode.DECIMAL: {
460+
let decimalHighPart = dataBuffer.getBigInt64(colDataHead + 8, true);
461+
const decimalLowPart = dataBuffer.getBigUint64(colDataHead, true);
462+
const decimalCombined = (decimalHighPart << 64n) | decimalLowPart;
463+
return decimalToString(decimalCombined.toString(), fields_scale);
464+
}
465+
case TDengineTypeCode.DECIMAL64: {
466+
let decimalVal = dataBuffer.getBigInt64(colDataHead, true);
467+
return decimalToString(decimalVal.toString(), fields_scale);
468+
}
419469
default: {
420-
throw new WebSocketQueryInterFaceError(ErrorCode.ERR_UNSUPPORTED_TDENGINE_TYPE, `unspported type ${meta.type} for column ${meta.name}`)
470+
throw new WebSocketQueryInterFaceError(ErrorCode.ERR_UNSUPPORTED_TDENGINE_TYPE, `unsupported type ${meta.type} for column ${meta.name}`)
421471
}
422472
}
423473
}
@@ -480,4 +530,12 @@ function bitPos(n:number):number {
480530

481531
export function bitmapLen(n: number): number {
482532
return ((n) + ((1 << 3) - 1)) >> 3
533+
}
534+
535+
function getScaleFromRowBlock(buffer: DataView, colIndex: number, startOffset: number): number {
536+
// for decimal: |___bytes___|__empty__|___prec___|__scale___|
537+
let backupPos = buffer.byteOffset + startOffset + 28 + colIndex * 5 + 1;
538+
let scaleBuffer = new DataView(buffer.buffer, backupPos);
539+
let scale = scaleBuffer.getInt32(0, true);
540+
return scale & 0xFF;
483541
}

nodejs/src/common/utils.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,24 @@ function comparePreReleases(pre1: string | null, pre2: string | null): number {
156156
return pre1.localeCompare(pre2);
157157
}
158158

159+
export function decimalToString(valueStr: string, fields_scale: bigint | null): string {
160+
let decimalStr = valueStr;
161+
if (fields_scale && fields_scale > 0) {
162+
const scale = Number(fields_scale);
163+
const isNegative = decimalStr.startsWith('-');
164+
const absStr = isNegative ? decimalStr.slice(1) : decimalStr;
165+
166+
if (absStr.length <= scale) {
167+
// If the length of the number is less than or equal to the precision, add 0 before it.
168+
const paddedStr = absStr.padStart(scale + 1, '0');
169+
decimalStr = (isNegative ? '-' : '') + '0.' + paddedStr.slice(1);
170+
} else {
171+
// 在指定位置插入小数点
172+
const integerPart = absStr.slice(0, absStr.length - scale);
173+
const decimalPart = absStr.slice(absStr.length - scale);
174+
decimalStr = (isNegative ? '-' : '') + integerPart + '.' + decimalPart;
175+
}
176+
}
177+
return decimalStr;
178+
}
159179

nodejs/src/stmt/wsProto.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export interface StmtMessageInfo {
1111
interface StmtParamsInfo {
1212
req_id: number;
1313
sql?: string | undefined | null;
14-
stmt_id?: number | undefined | null;
14+
stmt_id?: bigint | undefined | null;
1515
name?: string | undefined | null;
1616
tags?: Array<any> | undefined | null;
1717
paramArray?: Array<Array<any>> | undefined | null;
@@ -20,10 +20,10 @@ interface StmtParamsInfo {
2020

2121
export class WsStmtQueryResponse extends WSQueryResponse {
2222
affected:number | undefined | null;
23-
stmt_id?: number | undefined | null;
23+
stmt_id?: bigint | undefined | null;
2424
constructor(resp:MessageResp) {
2525
super(resp);
26-
this.stmt_id = resp.msg.stmt_id
26+
this.stmt_id = BigInt(resp.msg.stmt_id)
2727
this.affected = resp.msg.affected
2828
}
2929
}
@@ -34,7 +34,7 @@ export const enum StmtBindType {
3434
}
3535

3636

37-
export function binaryBlockEncode(bindParams :StmtBindParams, bindType:StmtBindType, stmtId:number, reqId:bigint, row:number): ArrayBuffer {
37+
export function binaryBlockEncode(bindParams :StmtBindParams, bindType:StmtBindType, stmtId:bigint, reqId:bigint, row:number): ArrayBuffer {
3838
//Computing the length of data
3939
let columns = bindParams.getParams().length;
4040
let length = TDengineTypeLength['BIGINT'] * 4;
@@ -46,7 +46,7 @@ export function binaryBlockEncode(bindParams :StmtBindParams, bindType:StmtBindT
4646
let arrayView = new DataView(arrayBuffer)
4747

4848
arrayView.setBigUint64(0, reqId, true);
49-
arrayView.setBigUint64(8, BigInt(stmtId), true);
49+
arrayView.setBigUint64(8, stmtId, true);
5050
arrayView.setBigUint64(16, BigInt(bindType), true);
5151
//version int32
5252
arrayView.setUint32(24, 1, true);

nodejs/src/stmt/wsStmt.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import JSONBig from 'json-bigint';
12
import { WsClient } from '../client/wsClient';
23
import { ErrorCode, TDWebSocketClientError, TaosError, TaosResultError } from '../common/wsError';
34
import { WsStmtQueryResponse, StmtMessageInfo, binaryBlockEncode, StmtBindType } from './wsProto';
@@ -8,7 +9,7 @@ import logger from '../common/log';
89

910
export class WsStmt {
1011
private _wsClient: WsClient;
11-
private _stmt_id: number | undefined | null;
12+
private _stmt_id: bigint | undefined | null;
1213
private _precision:number = PrecisionLength['ms'];
1314

1415
private lastAffected: number | undefined | null;
@@ -147,7 +148,7 @@ export class WsStmt {
147148
return await this.execute(queryMsg, false);
148149
}
149150

150-
public getStmtId(): number | undefined | null {
151+
public getStmtId(): bigint | undefined | null {
151152
return this._stmt_id;
152153
}
153154

@@ -156,7 +157,7 @@ export class WsStmt {
156157
if (this._wsClient.getState() <= 0) {
157158
throw new TDWebSocketClientError(ErrorCode.ERR_CONNECTION_CLOSED, "websocket connect has closed!");
158159
}
159-
let reqMsg = JSON.stringify(queryMsg);
160+
let reqMsg = JSONBig.stringify(queryMsg);
160161
if (register) {
161162
let result = await this._wsClient.exec(reqMsg, false);
162163
let resp = new WsStmtQueryResponse(result)

nodejs/src/tmq/constant.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,27 +102,31 @@ export class TMQMessageType {
102102
}
103103

104104
export class TMQBlockInfo {
105-
rawBlock?: ArrayBuffer;
106-
precision?: number;
107-
schema: Array<TMQRawDataSchema>;
108-
tableName?: string;
105+
rawBlock?: ArrayBuffer;
106+
precision?: number;
107+
schema: Array<TMQRawDataSchema>;
108+
tableName?: string;
109109
constructor() {
110110
this.schema = [];
111111
}
112112
}
113113

114114
export class TMQRawDataSchema {
115-
colType: number;
116-
flag: number;
117-
bytes: bigint;
118-
colID: number
119-
name: string;
115+
colType: number;
116+
flag: number;
117+
bytes: bigint;
118+
colID: number
119+
name: string;
120+
precision: number;
121+
scale: number;
120122
constructor() {
121123
this.bytes = BigInt(0);
122124
this.colID = -1;
123125
this.colType = -1;
124126
this.flag = -1;
125127
this.name = "";
128+
this.scale = 0;
129+
this.precision = 0;
126130

127131
}
128132
}

nodejs/src/tmq/tmqResponse.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { WSQueryResponse } from "../client/wsResponse";
2-
import { ColumnsBlockType, TDengineTypeLength } from "../common/constant";
2+
import { ColumnsBlockType, TDengineTypeCode, TDengineTypeLength } from "../common/constant";
33
import { MessageResp, TaosResult, _isVarType, getString, readBinary, readNchar, readSolidDataToArray, readVarchar } from "../common/taosResult";
44
import { WebSocketInterfaceError, ErrorCode, TDWebSocketClientError } from "../common/wsError";
55
import { TMQBlockInfo, TMQRawDataSchema } from "./constant";
@@ -15,7 +15,7 @@ export class WsPollResponse {
1515
topic: string;
1616
database: string;
1717
vgroup_id:number;
18-
message_id: number;
18+
message_id: bigint;
1919
id: bigint;
2020
message_type:number;
2121
totalTime:number;
@@ -29,7 +29,7 @@ export class WsPollResponse {
2929
this.topic = resp.msg.topic;
3030
this.database = resp.msg.database;
3131
this.vgroup_id = resp.msg.vgroup_id;
32-
this.message_id = resp.msg.message_id;
32+
this.message_id = BigInt(resp.msg.message_id);
3333
this.message_type = resp.msg.message_type;
3434
if (resp.msg.id) {
3535
this.id = BigInt(resp.msg.id);
@@ -245,7 +245,7 @@ export class WSTmqFetchBlockInfo {
245245
let bitMapArr = new Uint8Array(dataView.buffer, dataView.byteOffset + bufferOffset, bitMapOffset)
246246
bufferOffset += bitMapOffset;
247247
//decode column data, data is array
248-
data = readSolidDataToArray(dataView, bufferOffset, rows, this.schema[i].colType, bitMapArr);
248+
data = readSolidDataToArray(dataView, bufferOffset, rows, this.schema[i].colType, bitMapArr, startOffset, i);
249249

250250
} else {
251251
//Variable length type
@@ -347,15 +347,15 @@ export class PartitionsResp{
347347
action: string;
348348
totalTime: number;
349349
timing:bigint;
350-
positions:number[];
350+
positions:bigint[];
351351
constructor(resp:MessageResp) {
352352
this.timing = BigInt(resp.msg.timing);
353353
this.code = resp.msg.code;
354354
this.message = resp.msg.message;
355355
this.req_id = resp.msg.req_id;
356356
this.action = resp.msg.action;
357357
this.totalTime = resp.totalTime;
358-
this.positions = resp.msg.position;
358+
this.positions = resp.msg.position ? resp.msg.position.map((pos: number) => BigInt(pos)) : [];
359359
}
360360

361361
setTopicPartitions(topicPartitions:TopicPartition[]):TopicPartition[] {
@@ -373,21 +373,21 @@ export class PartitionsResp{
373373
export class CommittedResp extends PartitionsResp {
374374
constructor(resp:MessageResp) {
375375
super(resp);
376-
this.positions = resp.msg.committed
376+
this.positions = resp.msg.committed ? resp.msg.committed.map((pos: number) => BigInt(pos)) : [];
377377
}
378378
}
379379

380380
export class TopicPartition {
381381
topic :string;
382382
vgroup_id :number;
383-
offset ?:number;
384-
begin ?:number;
385-
end ?:number;
383+
offset ?:bigint;
384+
begin ?:bigint;
385+
end ?:bigint;
386386
constructor(msg:any) {
387387
this.vgroup_id = msg.vgroup_id;
388-
this.offset = msg.offset;
389-
this.begin = msg.begin;
390-
this.end = msg.end;
388+
this.offset = BigInt(msg.offset);
389+
this.begin = BigInt(msg.begin);
390+
this.end = BigInt(msg.end);
391391
this.topic = ''
392392
}
393393
}

0 commit comments

Comments
 (0)