Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
8961c92
docs: add stmt2 decimal support design spec
qevolg Apr 13, 2026
47c5fc9
docs: update stmt2 decimal spec - bind-time type resolution, stmt1 er…
qevolg Apr 13, 2026
1bd834d
docs: simplify spec - stmt2 bind is full-bind only
qevolg Apr 13, 2026
80e8cff
fix: standardize error message casing in WsStmt2 and optimize readSol…
qevolg Apr 13, 2026
d7531aa
feat: remove decimal support design document and related code
qevolg Apr 14, 2026
c701fea
refactor: streamline constructor and addParams method in Stmt2BindPar…
qevolg Apr 14, 2026
bbaaeb5
Merge branch 'main' into feat/stmt2/decimal
qevolg Apr 14, 2026
a53064a
feat: add decimal support in Stmt2 with validation and encoding logic
qevolg Apr 14, 2026
aa18dfe
fix: update data type checks and improve decimal handling in Stmt2Bin…
qevolg Apr 14, 2026
e718a7e
feat: add comprehensive tests for Stmt2 decimal binding behavior
qevolg Apr 14, 2026
5096d03
feat: enhance Stmt2 decimal handling with comprehensive boundary test…
qevolg Apr 14, 2026
90b905b
feat: update setBlob method to indicate unsupported usage in stmt1 an…
qevolg Apr 14, 2026
b89e45c
feat: enhance decimal binding behavior to prevent mutation of caller …
qevolg Apr 14, 2026
a301bb4
docs: add stmt2 decimal bind zero-copy optimization spec
qevolg Apr 14, 2026
bd0d3ab
feat: remove redundant deep copy in non-full-binding for decimal bind…
qevolg Apr 14, 2026
881f11d
fix: update error message for invalid params in SetTags method
qevolg Apr 15, 2026
c042c87
fix: update TDengine download and installation script to use nightly …
qevolg Apr 15, 2026
dced932
feat: normalize decimal column types in bind parameters and update re…
qevolg Apr 15, 2026
cafdd02
feat: enhance decimal column type handling in binding process
qevolg Apr 15, 2026
722b4ac
feat: add test for stmt2 with dynamic columns supporting various row …
qevolg Apr 15, 2026
225f35b
feat: implement session readiness handling in WebSocketConnector and …
qevolg Apr 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/enterprise.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ jobs:
node-version: [16.x, 20.x]
steps:
- name: Get TDengine
run: wget "${{ secrets.NIGHTLY_TDENGINE_ENTERPRISE_BASE_URL }}/tsdb-nightly-3.0.tar.gz?v=$(date +%s)" -O tsdb-nightly-3.0.tar.gz
run: wget "${{ secrets.NIGHTLY_TDENGINE_ENTERPRISE_BASE_URL }}/tsdb-nightly-main.tar.gz?v=$(date +%s)" -O tsdb-nightly-main.tar.gz

- name: Install TDengine
run: |
tar -zxf tsdb-nightly-3.0.tar.gz
rm -rf tsdb-nightly-3.0.tar.gz
cd tsdb-nightly-3.0
tar -zxf tsdb-nightly-main.tar.gz
rm -rf tsdb-nightly-main.tar.gz
cd tsdb-nightly-main
sudo ./install.sh

- name: Start TDengine
Expand Down
4 changes: 4 additions & 0 deletions nodejs/src/common/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ export const TDengineTypeName: IndexableString = {
14: "BIGINT UNSIGNED",
15: "JSON",
16: "VARBINARY",
17: "DECIMAL",
18: "BLOB",
20: "GEOMETRY",
21: "DECIMAL64",
};

export const ColumnsBlockType: StringIndexable = {
Expand Down Expand Up @@ -105,6 +107,8 @@ export const TDengineTypeLength: NumberIndexable = {
[TDengineTypeCode.SMALLINT_UNSIGNED]: 2,
[TDengineTypeCode.INT_UNSIGNED]: 4,
[TDengineTypeCode.BIGINT_UNSIGNED]: 8,
[TDengineTypeCode.DECIMAL]: 16,
[TDengineTypeCode.DECIMAL64]: 8,
};

export const PrecisionLength: StringIndexable = {
Expand Down
24 changes: 5 additions & 19 deletions nodejs/src/common/taosResult.ts
Original file line number Diff line number Diff line change
Expand Up @@ -476,9 +476,7 @@ export function readSolidDataToArray(
result.push(null);
} else {
let decimalVal = dataBuffer.getBigInt64(colBlockHead, true);
result.push(
decimalToString(decimalVal.toString(), BigInt(scale))
);
result.push(decimalToString(decimalVal.toString(), BigInt(scale)));
}
}
break;
Expand All @@ -489,22 +487,10 @@ export function readSolidDataToArray(
if (isNull(bitMapArr, i)) {
result.push(null);
} else {
let decimalHighPart = dataBuffer.getBigInt64(
colBlockHead + 8,
true
);
const decimalLowPart = dataBuffer.getBigUint64(
colBlockHead,
true
);
const decimalCombined =
(decimalHighPart << 64n) | decimalLowPart;
result.push(
decimalToString(
decimalCombined.toString(),
BigInt(scale)
)
);
let decimalHighPart = dataBuffer.getBigInt64(colBlockHead + 8, true);
const decimalLowPart = dataBuffer.getBigUint64(colBlockHead, true);
const decimalCombined = (decimalHighPart << 64n) | decimalLowPart;
result.push(decimalToString(decimalCombined.toString(), BigInt(scale)));
}
}
break;
Expand Down
3 changes: 1 addition & 2 deletions nodejs/src/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,7 @@ export function decimalToString(
} else {
const integerPart = absStr.slice(0, absStr.length - scale);
const decimalPart = absStr.slice(absStr.length - scale);
decimalStr =
(isNegative ? "-" : "") + integerPart + "." + decimalPart;
decimalStr = (isNegative ? "-" : "") + integerPart + "." + decimalPart;
}
}
return decimalStr;
Expand Down
6 changes: 0 additions & 6 deletions nodejs/src/stmt/wsParams1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,6 @@ export class Stmt1BindParams extends StmtBindParams implements IDataEncoder {
"StmtBindParams params is invalid!"
);
}
if (columnType === TDengineTypeCode.BLOB) {
throw new TaosError(
ErrorCode.ERR_INVALID_PARAMS,
"BLOB parameters are not supported for stmt1"
);
}
if (
dataType === "number" ||
dataType === "bigint" ||
Expand Down
118 changes: 70 additions & 48 deletions nodejs/src/stmt/wsParams2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import {
ColumnsBlockType,
FieldBindType,
PrecisionLength,
TDengineTypeCode,
TDengineTypeName,
} from "../common/constant";
import { ErrorCode, TaosError } from "../common/wsError";
import { isEmpty } from "../common/utils";
Expand All @@ -16,35 +18,25 @@ export class Stmt2BindParams extends StmtBindParams implements IDataEncoder {
private _fields: Array<StmtFieldInfo>;
protected paramIndex: number = 0;

constructor(
paramsCount?: number,
precision?: number,
fields?: Array<StmtFieldInfo>
) {
constructor(paramsCount?: number, precision?: number, fields?: Array<StmtFieldInfo>) {
super(precision, paramsCount);
this._fields = fields || [];
}

addParams(
params: any[],
dataType: string,
typeLen: number,
columnType: number
): void {
addParams(params: any[], dataType: string, typeLen: number, columnType: number): void {
if (!params || params.length == 0) {
throw new TaosError(
ErrorCode.ERR_INVALID_PARAMS,
"StmtBindParams params is invalid!"
);
}

if (this._fieldParams) {
if (this.paramsCount > 0) {
if (this._fieldParams[this.paramIndex]) {
if (
this._fieldParams[this.paramIndex].dataType !==
dataType ||
this._fieldParams[this.paramIndex].columnType !==
columnType
this._fieldParams[this.paramIndex].dataType !== dataType ||
this._fieldParams[this.paramIndex].columnType !== columnType
) {
throw new TaosError(
ErrorCode.ERR_INVALID_PARAMS,
Expand All @@ -53,11 +45,8 @@ export class Stmt2BindParams extends StmtBindParams implements IDataEncoder {
dataType,
columnType,
})} vs ${JSONBig.stringify({
dataType:
this._fieldParams[this.paramIndex].dataType,
columnType:
this._fieldParams[this.paramIndex]
.columnType,
dataType: this._fieldParams[this.paramIndex].dataType,
columnType: this._fieldParams[this.paramIndex].columnType,
})}`
);
}
Expand Down Expand Up @@ -103,6 +92,7 @@ export class Stmt2BindParams extends StmtBindParams implements IDataEncoder {
"StmtBindParams params is invalid!"
);
}

this.paramIndex = 0;
for (let i = 0; i < bindParams._fieldParams.length; i++) {
let fieldParam = bindParams._fieldParams[i];
Expand All @@ -117,6 +107,44 @@ export class Stmt2BindParams extends StmtBindParams implements IDataEncoder {
}
}

setDecimal(params: any[]) {
if (!params || params.length == 0) {
throw new TaosError(
ErrorCode.ERR_INVALID_PARAMS,
"SetDecimalColumn params is invalid!"
);
}
for (let i = 0; i < params.length; i++) {
if (!isEmpty(params[i]) && typeof params[i] !== "string") {
throw new TaosError(
ErrorCode.ERR_INVALID_PARAMS,
"SetDecimalColumn params is invalid!"
);
}
}
this.addParams(
params,
TDengineTypeName[TDengineTypeCode.DECIMAL],
0,
TDengineTypeCode.DECIMAL
);
}
Comment thread
qevolg marked this conversation as resolved.

setBlob(params: any[]) {
if (!params || params.length == 0) {
throw new TaosError(
ErrorCode.ERR_INVALID_PARAMS,
"SetBlobColumn params is invalid!"
);
}
this.addParams(
params,
TDengineTypeName[TDengineTypeCode.BLOB],
0,
TDengineTypeCode.BLOB
);
}

encode(): void {
this.paramIndex = 0;
if (!this._fieldParams || this._fieldParams.length == 0) {
Expand All @@ -136,42 +164,38 @@ export class Stmt2BindParams extends StmtBindParams implements IDataEncoder {
} else {
this._rows = this._fieldParams[0].params.length;
}

for (let i = 0; i < this._fieldParams.length; i++) {
let fieldParam = this._fieldParams[i];
if (!fieldParam) {
continue;
}

let isVarType = _isVarType(fieldParam.columnType);
if (isVarType == ColumnsBlockType.SOLID) {
if (fieldParam.dataType === "TIMESTAMP") {
this._params.push(
this.encodeTimestampColumn(
fieldParam.params,
fieldParam.typeLen,
fieldParam.columnType
)
);
} else {
this._params.push(
this.encodeDigitColumns(
fieldParam.params,
fieldParam.dataType,
fieldParam.typeLen,
fieldParam.columnType
)
);
}
} else {
this._params.push(
this.encodeVarColumns(
const isSolidType = _isVarType(fieldParam.columnType) === ColumnsBlockType.SOLID;
const useSolidEncoder = isSolidType &&
fieldParam.columnType !== TDengineTypeCode.DECIMAL &&
fieldParam.columnType !== TDengineTypeCode.DECIMAL64;

const columnInfo = useSolidEncoder
? fieldParam.dataType === TDengineTypeName[TDengineTypeCode.TIMESTAMP]
? this.encodeTimestampColumn(
fieldParam.params,
fieldParam.typeLen,
fieldParam.columnType
)
: this.encodeDigitColumns(
fieldParam.params,
fieldParam.dataType,
fieldParam.typeLen,
fieldParam.columnType
)
: this.encodeVarColumns(
fieldParam.params,
fieldParam.dataType,
fieldParam.typeLen,
fieldParam.columnType
);
}
this._params.push(columnInfo);
}
}

Expand Down Expand Up @@ -210,6 +234,7 @@ export class Stmt2BindParams extends StmtBindParams implements IDataEncoder {
}
} else {
isNull.push(1);
dataLengths.push(0);
}
}
this._dataTotalLen += totalLength;
Expand Down Expand Up @@ -299,10 +324,7 @@ export class Stmt2BindParams extends StmtBindParams implements IDataEncoder {
} else {
timeStamp = BigInt(date.getTime());
}
} else if (
typeof params[i] == "bigint" ||
typeof params[i] == "number"
) {
} else if (typeof params[i] == "bigint" || typeof params[i] == "number") {
if (typeof params[i] == "number") {
timeStamp = BigInt(params[i]);
} else {
Expand Down
23 changes: 11 additions & 12 deletions nodejs/src/stmt/wsParamsBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,13 +307,10 @@ export abstract class StmtBindParams {
}

setBlob(params: any[]) {
if (!params || params.length == 0) {
throw new TaosError(
ErrorCode.ERR_INVALID_PARAMS,
"SetBlobColumn params is invalid!"
);
}
this.addParams(params, TDengineTypeName[18], 0, TDengineTypeCode.BLOB);
throw new TaosError(
ErrorCode.ERR_UNSUPPORTED_TDENGINE_TYPE,
"setBlob is not supported in stmt1, please use stmt2 instead!"
);
}

setGeometry(params: any[]) {
Expand All @@ -326,6 +323,13 @@ export abstract class StmtBindParams {
this.addParams(params, TDengineTypeName[20], 0, TDengineTypeCode.GEOMETRY);
}

setDecimal(params: any[]) {
throw new TaosError(
ErrorCode.ERR_UNSUPPORTED_TDENGINE_TYPE,
"setDecimal is not supported in stmt1, please use stmt2 instead!"
);
}

setTimestamp(params: any[]) {
if (!params || params.length == 0) {
throw new TaosError(
Expand Down Expand Up @@ -375,28 +379,23 @@ export abstract class StmtBindParams {
dataBuffer.setUint16(i * 2, params, true);
break;
}

case TDengineTypeCode.INT: {
dataBuffer.setInt32(i * 4, params, true);
break;
}

case TDengineTypeCode.INT_UNSIGNED: {
dataBuffer.setUint32(i * 4, params, true);
break;
}

case TDengineTypeCode.BIGINT:
case TDengineTypeCode.TIMESTAMP: {
dataBuffer.setBigInt64(i * 8, params, true);
break;
}

case TDengineTypeCode.BIGINT_UNSIGNED: {
dataBuffer.setBigUint64(i * 8, params, true);
break;
}

case TDengineTypeCode.FLOAT: {
dataBuffer.setFloat32(i * 4, params, true);
break;
Expand Down
12 changes: 4 additions & 8 deletions nodejs/src/stmt/wsProto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,7 @@ export function stmt2BinaryBlockEncode(
colSizeList: new Array<number>(listLength),
};

const hasTableName =
toBeBindTableNameIndex != null && toBeBindTableNameIndex >= 0;
const hasTableName = toBeBindTableNameIndex != null && toBeBindTableNameIndex >= 0;
const hasTags = toBeBindTagCount > 0;
for (let i = 0; i < listLength; i++) {
const tableInfo = stmtTableInfoList[i];
Expand Down Expand Up @@ -238,10 +237,8 @@ export function stmt2BinaryBlockEncode(
result.totalColSize += size;
}

let totalSize =
result.totalTableNameSize + result.totalTagSize + result.totalColSize;
let toBeBindTableNameCount =
toBeBindTableNameIndex != null && toBeBindTableNameIndex >= 0 ? 1 : 0;
let totalSize = result.totalTableNameSize + result.totalTagSize + result.totalColSize;
let toBeBindTableNameCount = toBeBindTableNameIndex != null && toBeBindTableNameIndex >= 0 ? 1 : 0;
Comment thread
qevolg marked this conversation as resolved.
totalSize +=
stmtTableInfoList.length *
(toBeBindTableNameCount * 2 +
Expand Down Expand Up @@ -295,8 +292,7 @@ export function stmt2BinaryBlockEncode(
if (toBeBindColCount > 0) {
let skipSize = 0;
if (toBeBindTableNameCount > 0) {
skipSize +=
result.totalTableNameSize + 2 * stmtTableInfoList.length;
skipSize += result.totalTableNameSize + 2 * stmtTableInfoList.length;
}

if (toBeBindTagCount > 0) {
Expand Down
Loading
Loading