未验证 提交 a3154a8a 编写于 作者: X xiaolei li 提交者: GitHub

fix:move nodejs and ts-rest to independent repo (#11967)

* fix:move nodejs and ts-rest to independent repo

* docs:fix node reference markdown sample hyperlink
上级 4305ed31
......@@ -205,22 +205,22 @@ let cursor = conn.cursor();
| 示例程序 | 示例程序描述 |
| --------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- |
| [connection](https://github.com/taosdata/TDengine/blob/develop/src/connector/nodejs/examples/cursorClose.js) | 建立连接的示例。 |
| [stmtBindBatch](https://github.com/taosdata/TDengine/blob/develop/src/connector/nodejs/examples/stmtBindParamBatchSample.js) | 绑定多行参数插入的示例。 |
| [stmtBind](https://github.com/taosdata/TDengine/blob/develop/src/connector/nodejs/examples/stmtBindParamSample.js) | 一行一行绑定参数插入的示例。 |
| [stmtBindSingleParamBatch](https://github.com/taosdata/TDengine/blob/develop/src/connector/nodejs/examples/stmtBindSingleParamBatchSample.js) | 按列绑定参数插入的示例。 |
| [stmtUseResult](https://github.com/taosdata/TDengine/blob/develop/src/connector/nodejs/examples/stmtUseResultSample.js) | 绑定参数查询的示例。 |
| [json tag](https://github.com/taosdata/TDengine/blob/develop/src/connector/nodejs/examples/testJsonTag.js) | Json tag 的使用示例。 |
| [Nanosecond](https://github.com/taosdata/TDengine/blob/develop/src/connector/nodejs/examples/testNanoseconds.js) | 时间戳为纳秒精度的使用的示例。 |
| [Microsecond](https://github.com/taosdata/TDengine/blob/develop/src/connector/nodejs/examples/testMicroseconds.js) | 时间戳为微秒精度的使用的示例。 |
| [schemless insert](https://github.com/taosdata/TDengine/blob/develop/src/connector/nodejs/examples/testSchemalessInsert.js) | schemless 插入的示例。 |
| [subscribe](https://github.com/taosdata/TDengine/blob/develop/src/connector/nodejs/examples/testSubscribe.js) | 订阅的使用示例。 |
| [asyncQuery](https://github.com/taosdata/TDengine/blob/develop/src/connector/nodejs/examples/tset.js) | 异步查询的使用示例。 |
| [REST](https://github.com/taosdata/TDengine/tree/develop/src/connector/TypeScript-REST/example) | 使用 REST 连接的 TypeScript 使用示例。 |
| [connection](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/cursorClose.js) | 建立连接的示例。 |
| [stmtBindBatch](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/stmtBindParamBatchSample.js) | 绑定多行参数插入的示例。 |
| [stmtBind](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/stmtBindParamSample.js) | 一行一行绑定参数插入的示例。 |
| [stmtBindSingleParamBatch](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/stmtBindSingleParamBatchSample.js) | 按列绑定参数插入的示例。 |
| [stmtUseResult](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/stmtUseResultSample.js) | 绑定参数查询的示例。 |
| [json tag](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/testJsonTag.js) | Json tag 的使用示例。 |
| [Nanosecond](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/testNanoseconds.js) | 时间戳为纳秒精度的使用的示例。 |
| [Microsecond](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/testMicroseconds.js) | 时间戳为微秒精度的使用的示例。 |
| [schemless insert](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/testSchemalessInsert.js) | schemless 插入的示例。 |
| [subscribe](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/testSubscribe.js) | 订阅的使用示例。 |
| [asyncQuery](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/tset.js) | 异步查询的使用示例。 |
| [REST](https://github.com/taosdata/taos-connector-node/blob/develop/typescript-rest/example/example.ts) | 使用 REST 连接的 TypeScript 使用示例。 |
## 使用限制
Node.js 连接器 >= v2.0.6 目前支持 node 的版本为: 支持 >=v12.8.0 <= v12.9.1 || >=v10.20.0 <= v10.9.0 ;2.0.5 及更早版本支持 v10.x 版本,其他版本可能存在包兼容性的问题。
Node.js 连接器 >= v2.0.6 目前支持 node 的版本为:支持 >=v12.8.0 <= v12.9.1 || >=v10.20.0 <= v10.9.0 ;2.0.5 及更早版本支持 v10.x 版本,其他版本可能存在包兼容性的问题。
## 其他说明
......@@ -236,7 +236,7 @@ Node.js 连接器的使用参见[视频教程](https://www.taosdata.com/blog/202
2. 连接器 >v2.0.6 目前兼容的 Node.js 为:>=v10.20.0 <= v10.9.0 || >=v12.8.0 <= v12.9.1
3. "Unable to establish connection","Unable to resolve FQDN", 一般都是应为为配置 FQDN 可以参考[如何彻底搞懂 TDengine 的 FQDN](https://www.taosdata.com/blog/2021/07/29/2741.html) 。
3. "Unable to establish connection","Unable to resolve FQDN" 等问题一般都是因为配置 FQDN 不正确。 可以参考[如何彻底搞懂 TDengine 的 FQDN](https://www.taosdata.com/blog/2021/07/29/2741.html) 。
## 重要更新记录
......@@ -244,15 +244,15 @@ Node.js 连接器的使用参见[视频教程](https://www.taosdata.com/blog/202
| td2.0-connector 版本 | 说明 |
| -------------------- | -------------------------------------------------------------------- |
| 2.0.10 | 支持连接管理,同步查询、异步查询、获取系统信息、错误信息、订阅功能。 |
| 2.0.11 | 支持绑定参数、json tag、schemaless insert。 |
| 2.0.12 | 修复 cursor.close() 报错的问题。 |
| 2.0.10 | 支持连接管理,普通查询、连续查询、获取系统信息、订阅功能等功能。 |
| 2.0.11 | 支持绑定参数、json tag、schemaless 接口等功能。 |
| 2.0.12 | 修复 cursor.close() 报错的bug。 |
### REST 接口连接器
| td2.0-rest-connector 版本 | 说明 |
| ------------------------- | ------------------------------------------------------------ |
| 1.0.3 | 支持连接管理、同步查询、获取系统信息、错误信息、schemeless。 |
| 1.0.3 | 支持连接管理、普通查询、获取系统信息、错误信息、连续查询等功能。 |
## API 参考
......
import { options, connect } from '../tdengine_rest'
options.path = '/rest/sqlt'
options.host = 'localhost'
const db = 'rest_ts_db';
const table = 'rest'
const createDB = `create database if not exists ${db} keep 3650`;
const dropDB = `drop database if exists ${db}`;
const createTB = `create table if not exists ${db}.${table}(ts timestamp,i8 tinyint,i16 smallint,i32 int,i64 bigint,bnr binary(40),nchr nchar(40))`;
const addColumn = `alter table ${db}.${table} add column new_column nchar(40) `;
const dropColumn = `alter table ${db}.${table} drop column new_column`;
const insertSql = `insert into ${db}.${table} values('2022-03-30 18:30:51.567',1,2,3,4,'binary1','nchar1')` +
`('2022-03-30 18:30:51.568',5,6,7,8,'binary2','nchar2')` +
`('2022-03-30 18:30:51.569',9,0,1,2,'binary3','nchar3')`;
const querySql = `select * from ${db}.${table}`;
const errorSql = 'show database';
let conn = connect(options);
let cursor = conn.cursor();
async function execute(sql: string, pure = false) {
let result = await cursor.query(sql, pure);
// print query result as taos shell
result.toString();
// Get Result object, return Result object.
console.log("result.getResult()",result.getResult());
// Get status, return 'succ'|'error'.
console.log("result.getStatus()",result.getStatus());
// Get head,return response head (Array<any>|undefined,when execute failed this is undefined).
console.log("result.getHead()",result.getHead());
// Get Meta data, return Meta[]|undefined(when execute failed this is undefined).
console.log("result.getMeta()",result.getMeta());
// Get data,return Array<Array<any>>|undefined(when execute failed this is undefined).
console.log("result.getData()",result.getData());
// Get affect rows,return number|undefined(when execute failed this is undefined).
console.log("result.getAffectRows()",result.getAffectRows());
// Get command,return SQL send to server(need to `query(sql,false)`,set 'pure=false',default true).
console.log("result.getCommand()",result.getCommand());
// Get error code ,return number|undefined(when execute failed this is undefined).
console.log("result.getErrCode()",result.getErrCode());
// Get error string,return string|undefined(when execute failed this is undefined).
console.log("result.getErrStr()",result.getErrStr());
}
(async () => {
let start = new Date().getTime(); // 开始时间
await execute(createDB);
await execute(createTB);
await execute(addColumn);
await execute(dropColumn);
await execute(insertSql);
await execute(querySql);
await execute(errorSql);
await execute(dropDB);
let end = new Date().getTime(); // 结束时间
console.log("total spend time:%d ms",end - start);
})()
import {TDConnect,Options} from './src/connect';
let options:Options = {
host : '127.0.0.1',
port : 6041,
path : '/rest/sql',
user : 'root',
passwd : 'taosdata'
}
let connect = function connect(option:Options){
return new TDConnect(option);
}
export {options,connect}
\ No newline at end of file
{
"name": "td2.0-rest-connector",
"version": "1.0.3",
"description": "A REST connector for TDengine",
"source": "tdengine_rest.ts",
"main": "dist/main/rest.js",
"module": "dist/module/rest.mjs",
"types": "dist/types.d.ts",
"directories": {
"example": "example",
"test": "test"
},
"scripts": {
"test": "parcel build --no-source-maps && node ./dist/test/test.main.js && node ./dist/test/test.module.mjs",
"example": "tsc && node ./tsc/example/example.js",
"build": "parcel build --no-source-maps",
"watch": "parcel watch"
},
"repository": {
"type": "git",
"url": "https://github.com/taosdata/TDengine/tree/develop/src/connector/TypeScript-REST"
},
"keywords": [
"REST",
"Node.js",
"Typescript",
"TDengine",
"taos",
"IOT",
"node-fetch"
],
"author": "TaosData Inc.",
"license": "MIT",
"dependencies": {
"node-fetch": "^2.6.7",
"@types/node": "^17.0.23"
},
"devDependencies": {
"@parcel/packager-ts": "^2.4.1",
"@parcel/transformer-typescript-types": "^2.4.1",
"@types/node-fetch": "^2.6.1",
"parcel": "^2.4.0",
"typescript": "^4.6.3"
},
"bugs": {
"url": "https://github.com/taosdata/tdengine/issues"
},
"homepage": "https://github.com/taosdata/tdengine#readme"
}
import { TDengineCursor } from './cursor'
import { Uri, User } from './options'
/**
* Options used to connect with REST(taosAdapter)
* Need to set options with 'host','path','port','user','passwd'.
* connWith is optional attribute for further use.
*/
export interface Options extends Uri, User {
connWith?: 'rest' | 'taosc'
}
/**
* Create connect with TDengine,actually pass options
* to `Cursor` which send and receive HTTP request.
*/
export class TDConnect {
_connOption: Options;
constructor(connOption: Options) {
this._connOption = connOption
}
cursor(): TDengineCursor {
return new TDengineCursor(this._connOption);
}
}
import { Uri, User } from './options'
import { TDResRequest } from './request'
import { Result } from './result'
export class TDengineCursor {
field: Array<any>;
data: Array<any>
_rowCount: number;
_uri: Uri;
_user: User;
constructor(options: any) {
this._uri = {
host: options.host,
path: options.path,
port: options.port,
}
this._user = {
user: options.user,
passwd: options.passwd,
}
this._rowCount = 0;
this.field = [];
this.data = [];
}
async query(sql: string, pure = true): Promise<Result> {
let req = new TDResRequest(this._uri, this._user);
let response = await req.request(sql);
let res_json = await response.json();
if (pure == false) {
return new Result(res_json, sql);
} else {
return new Result(res_json);
}
}
}
\ No newline at end of file
export interface FetchOptions {
method: 'POST';
body: string;
headers: Record<string, string>
}
export interface User {
user: string;
passwd: string;
}
export interface Uri {
host: '127.0.0.1'|string;
path: "/rest/sqlt" | '/rest/sqlutc' | '/rest/sql';
port: 6041;
}
\ No newline at end of file
import { Uri,User,FetchOptions } from "./options";
import fetch from 'node-fetch';
export class TDResRequest {
uri: Uri;
options: FetchOptions;
user:User;
constructor(url: Uri, user:User) {
this.uri = url;
this.user = user;
this.options = {
method: 'POST',
body:'',
headers:{'Authorization':this._token()}
}
}
_makeUrl(): string {
return `http://${this.uri.host}:${this.uri.port}${this.uri.path}`;
}
_token(): string {
return`Basic ${Buffer.from(`${this.user.user}:${this.user.passwd}`).toString('base64')}`
}
_body(command:string):void{
this.options.body = command;
}
request(command:string): Promise<any> {
this._body(command);
return fetch(this._makeUrl(), this.options);
}
}
interface IResult {
status: string;
head?: Array<string>;
column_meta?: Array<Array<any>>;
data?: Array<Array<any>>;
rows?: number;
command?: string;
//for error
code?: number;
desc?: string;
}
interface meta {
columnName: string;
code: number;
size: number;
typeName?: string;
}
export class Result {
private _status: string;
private _head?: string[];
private _column_meta?: Array<meta>;
private _data?: Array<Array<any>>;
private _rows?: number;
private _command?: string;
//for error
private _code?: number;
private _desc?: string;
constructor(res: IResult, commands?: string) {
let meta_list_length = res.column_meta == undefined ? 0 : res.column_meta.length
if (res.status === 'succ') {
this._status = res.status;
this._head = res.head;
this._column_meta = new Array(meta_list_length);
this._data = res.data;
this._rows = res.rows;
this._command = commands;
this._initMeta(res);
this._code = undefined;
this._desc = undefined;
} else {
this._status = res.status;
this._head = undefined;
this._column_meta = undefined;
this._data = undefined;
this._rows = undefined;
this._command = commands;
this._code = res.code;
this._desc = res.desc;
}
}
private _initMeta(res: IResult): void {
if (res.column_meta != undefined) {
res.column_meta.forEach((item, index) => {
if (this._column_meta != undefined)
this._column_meta[index] = {
columnName: item[0],
code: item[1],
size: item[2],
typeName: typeNameMap[item[1]]
}
})
}
}
getResult(): Result {
return this;
}
getStatus(): string {
return this._status;
}
getHead(): Array<any> | undefined {
return this._head;
}
getMeta(): Array<meta> | undefined {
return this._column_meta;
}
getData(): Array<Array<any>> | undefined {
return this._data;
}
getAffectRows(): number | undefined {
return this._rows;
}
getCommand(): string | undefined {
return this._command;
}
getErrCode(): number | undefined {
return this._code;
}
getErrStr(): string | undefined {
return this._desc;
}
toString(): void {
let str = '';
if(this._command != undefined){
console.log(this._command);
}
if (this._status === 'succ' && this._column_meta != undefined && this._data != undefined) {
str = this._prettyStr(this._column_meta, this._data)
} else {
str = `Execute ${this._status},reason:${this._desc}. error_no:${this._code} `;
console.log(str)
}
}
private _prettyStr(fields: Array<meta>, data: Array<Array<any>>): string {
let colName = new Array<string>();
let colType = new Array<string | undefined>();
let colSize = new Array<number>();
let colStr = '';
for (let i = 0; i < fields.length; i++) {
colName.push(fields[i].columnName)
colType.push(fields[i].typeName);
if ((fields[i].code) == 8 || (fields[i].code) == 10) {
colSize.push(Math.max(fields[i].columnName.length, fields[i].size)); //max(column_name.length,column_type_precision)
} else {
colSize.push(Math.max(fields[i].columnName.length, suggestedMinWidths[fields[i].size]));// max(column_name.length,suggest_column_with_suggestion)
}
// console.log(colSize)
}
colName.forEach((name, i) => {
colStr += this._fillEmpty(Math.floor(colSize[i] / 2 - name.length / 2)) + name.toString() + this._fillEmpty(Math.ceil(colSize[i] / 2 - name.length / 2)) + " | "
})
let strSperator = ""
let sizeSum = colSize.reduce((a, b) => a += b, (0)) + colSize.length * 3
strSperator = this._printN("=", sizeSum)
console.log("\n" + colStr)
console.log(strSperator)
data.forEach((row) => {
let rowStr = ""
row.forEach((cell, index) => {
rowStr += cell == null ? 'null' : cell.toString();
rowStr += this._fillEmpty(colSize[index] - cell.toString().length) + " | "
})
console.log(rowStr)
})
return colStr
}
private _fillEmpty(n:number) {
let str = "";
for (let i = 0; i < n; i++) {
str += " ";
}
return str;
}
private _printN(s:string, n:number) {
let f = "";
for (let i = 0; i < n; i++) {
f += s;
}
return f;
}
}
interface indexableString {
[index: number]: string
}
/**
* this file record TDengine's data type and code.
*/
const typeNameMap: indexableString = {
0: 'null',
1: 'bool',
2: 'tinyint',
3: 'smallint',
4: 'int',
5: 'bigint',
6: 'float',
7: 'double',
8: 'binary',
9: 'timestamp',
10: 'nchar',
11: 'unsigned tinyint',
12: 'unsigned smallint',
13: 'unsigned int',
14: 'unsigned bigint',
15: 'json'
}
interface indexableNumber {
[index: number]: number
}
const suggestedMinWidths: indexableNumber = {
0: 4,
1: 4,
2: 4,
3: 6,
4: 11,
5: 12,
6: 24,
7: 24,
8: 10,
9: 25,
10: 10,
}
import {TDConnect,Options} from './src/connect';
let options:Options = {
host : '127.0.0.1',
port : 6041,
path : '/rest/sql',
user : 'root',
passwd : 'taosdata'
}
let connect = function connect(option:Options){
return new TDConnect(option);
}
export {options,connect}
\ No newline at end of file
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Projects */
// "incremental": true, /* Enable incremental compilation */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
// "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
/* Modules */
"module": "commonjs", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "resolveJsonModule": true, /* Enable importing .json files */
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
"outDir": "./tsc/", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
// "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
// "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}
const taos = require('../tdengine');
const conn = taos.connect({
host: "localhost",
});
const cursor = conn.cursor();
function createDatabase() {
cursor.execute("CREATE DATABASE if not exists test_cursor_close");
cursor.execute("USE test_cursor_close");
}
function insertData() {
const lines = [
"meters,location=Beijing.Haidian,groupid=2 current=11.8,voltage=221,phase=0.28 1648432611249",
"meters,location=Beijing.Haidian,groupid=2 current=13.4,voltage=223,phase=0.29 1648432611250",
"meters,location=Beijing.Haidian,groupid=3 current=10.8,voltage=223,phase=0.29 1648432611249",
"meters,location=Beijing.Haidian,groupid=3 current=11.3,voltage=221,phase=0.35 1648432611250",
];
cursor.schemalessInsert(
lines,
taos.SCHEMALESS_PROTOCOL.TSDB_SML_LINE_PROTOCOL,
taos.SCHEMALESS_PRECISION.TSDB_SML_TIMESTAMP_MILLI_SECONDS
);
}
function query() {
let promise = cursor.query("select * from test_cursor_close.meters");
promise.execute().then(result => result.pretty()).catch(err => console.log(e));
}
function destructData() {
cursor.execute("drop database if exists test_cursor_close");
}
function main() {
try {
createDatabase();
insertData();
query();
destructData();
} finally {
cursor.close();
conn.close();
}
}
main();
\ No newline at end of file
function memoryUsageData() {
let s = process.memoryUsage()
for (key in s) {
s[key] = (s[key]/1000000).toFixed(3) + "MB";
}
return s;
}
console.log("initial mem usage:", memoryUsageData());
const { PerformanceObserver, performance } = require('perf_hooks');
const taos = require('../tdengine');
var conn = taos.connect({host:"127.0.0.1", user:"root", password:"taosdata", config:"/etc/taos",port:0});
var c1 = conn.cursor();
// Initialize env
c1.execute('create database if not exists td_connector_test;');
c1.execute('use td_connector_test;')
c1.execute('create table if not exists all_types (ts timestamp, _int int, _bigint bigint, _float float, _double double, _binary binary(40), _smallint smallint, _tinyint tinyint, _bool bool, _nchar nchar(40));');
c1.execute('create table if not exists stabletest (ts timestamp, v1 int, v2 int, v3 int, v4 double) tags (id int, location binary(20));')
// Insertion into single table Performance Test
var dataPrepTime = 0;
var insertTime = 0;
var insertTime5000 = 0;
var avgInsert5ktime = 0;
const obs = new PerformanceObserver((items) => {
let entry = items.getEntries()[0];
if (entry.name == 'Data Prep') {
dataPrepTime += entry.duration;
}
else if (entry.name == 'Insert'){
insertTime += entry.duration
}
else {
console.log(entry.name + ': ' + (entry.duration/1000).toFixed(8) + 's');
}
performance.clearMarks();
});
obs.observe({ entryTypes: ['measure'] });
function R(l,r) {
return Math.random() * (r - l) - r;
}
function randomBool() {
if (Math.random() < 0.5) {
return true;
}
return false;
}
function insertN(n) {
for (let i = 0; i < n; i++) {
performance.mark('A3');
let insertData = ["now + " + i + "m", // Timestamp
parseInt( R(-Math.pow(2,31) + 1 , Math.pow(2,31) - 1) ), // Int
parseInt( R(-Math.pow(2,31) + 1 , Math.pow(2,31) - 1) ), // BigInt
parseFloat( R(-3.4E38, 3.4E38) ), // Float
parseFloat( R(-1.7E308, 1.7E308) ), // Double
"\"Long Binary\"", // Binary
parseInt( R(-32767, 32767) ), // Small Int
parseInt( R(-127, 127) ), // Tiny Int
randomBool(),
"\"Nchars 一些中文字幕\""]; // Bool
let query = 'insert into td_connector_test.all_types values(' + insertData.join(',') + ' );';
performance.mark('B3');
performance.measure('Data Prep', 'A3', 'B3');
performance.mark('A2');
c1.execute(query, {quiet:true});
performance.mark('B2');
performance.measure('Insert', 'A2', 'B2');
if ( i % 5000 == 4999) {
console.log("Insert # " + (i+1));
console.log('Insert 5k records: ' + ((insertTime - insertTime5000)/1000).toFixed(8) + 's');
insertTime5000 = insertTime;
avgInsert5ktime = (avgInsert5ktime/1000 * Math.floor(i / 5000) + insertTime5000/1000) / Math.ceil( i / 5000);
console.log('DataPrepTime So Far: ' + (dataPrepTime/1000).toFixed(8) + 's | Inserting time So Far: ' + (insertTime/1000).toFixed(8) + 's | Avg. Insert 5k time: ' + avgInsert5ktime.toFixed(8));
}
}
}
performance.mark('insert 1E5')
insertN(1E5);
performance.mark('insert 1E5 2')
performance.measure('Insert With Logs', 'insert 1E5', 'insert 1E5 2');
console.log('DataPrepTime: ' + (dataPrepTime/1000).toFixed(8) + 's | Inserting time: ' + (insertTime/1000).toFixed(8) + 's');
dataPrepTime = 0; insertTime = 0;
//'insert into td_connector_test.all_types values (now, null,null,null,null,null,null,null,null,null);'
const taos = require('../tdengine');
var conn = taos.connect({ host: "localhost" });
var cursor = conn.cursor();
function executeUpdate(updateSql) {
console.log(updateSql);
cursor.execute(updateSql);
}
function executeQuery(querySql) {
let query = cursor.query(querySql);
query.execute().then((result => {
console.log(querySql);
result.pretty();
}));
}
function stmtBindParamBatchSample() {
let db = 'node_test_db';
let table = 'stmt_taos_bind_param_batch';
let createDB = `create database if not exists ${db} keep 3650;`;
let dropDB = `drop database if exists ${db};`;
let useDB = `use ${db}`;
let createTable = `create table if not exists ${table} ` +
`(ts timestamp,` +
`bl bool,` +
`i8 tinyint,` +
`i16 smallint,` +
`i32 int,` +
`i64 bigint,` +
`f32 float,` +
`d64 double,` +
`bnr binary(20),` +
`nchr nchar(20),` +
`u8 tinyint unsigned,` +
`u16 smallint unsigned,` +
`u32 int unsigned,` +
`u64 bigint unsigned` +
`)tags(` +
`t_bl bool,` +
`t_i8 tinyint,` +
`t_i16 smallint,` +
`t_i32 int,` +
`t_i64 bigint,` +
`t_f32 float,` +
`t_d64 double,` +
`t_bnr binary(20),` +
`t_nchr nchar(20),` +
`t_u8 tinyint unsigned,` +
`t_u16 smallint unsigned,` +
`t_u32 int unsigned,` +
`t_u64 bigint unsigned` +
`);`;
let querySql = `select * from ${table};`;
let insertSql = `insert into ? using ${table} tags(?,?,?,?,?,?,?,?,?,?,?,?,?) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?);`;
executeUpdate(dropDB);
executeUpdate(createDB);
executeUpdate(useDB);
executeUpdate(createTable);
let mBinds = new taos.TaosMultiBindArr(14);
mBinds.multiBindTimestamp([1642435200000, 1642435300000, 1642435400000, 1642435500000, 1642435600000]);
mBinds.multiBindBool([true, false, true, undefined, null]);
mBinds.multiBindTinyInt([-127, 3, 127, null, undefined]);
mBinds.multiBindSmallInt([-256, 0, 256, null, undefined]);
mBinds.multiBindInt([-1299, 0, 1233, null, undefined]);
mBinds.multiBindBigInt([16424352000002222n, -16424354000001111n, 0, null, undefined]);
mBinds.multiBindFloat([12.33, 0, -3.1415, null, undefined]);
mBinds.multiBindDouble([3.141592653, 0, -3.141592653, null, undefined]);
mBinds.multiBindBinary(['TDengine_Binary', '', 'taosdata涛思数据', null, undefined]);
mBinds.multiBindNchar(['taos_data_nchar', 'taosdata涛思数据', '', null, undefined]);
mBinds.multiBindUTinyInt([0, 127, 254, null, undefined]);
mBinds.multiBindUSmallInt([0, 256, 512, null, undefined]);
mBinds.multiBindUInt([0, 1233, 4294967294, null, undefined]);
mBinds.multiBindUBigInt([16424352000002222n, 36424354000001111n, 0, null, undefined]);
let tags = new taos.TaosBind(13);
tags.bindBool(true);
tags.bindTinyInt(127);
tags.bindSmallInt(32767);
tags.bindInt(1234555);
tags.bindBigInt(-164243520000011111n);
tags.bindFloat(214.02);
tags.bindDouble(2.01);
tags.bindBinary('taosdata涛思数据');
tags.bindNchar('TDengine数据');
tags.bindUTinyInt(254);
tags.bindUSmallInt(65534);
tags.bindUInt(4294967290);
tags.bindUBigInt(164243520000011111n);
cursor.stmtInit();
cursor.stmtPrepare(insertSql);
cursor.stmtSetTbnameTags('s_01', tags.getBind());
cursor.stmtBindParamBatch(mBinds.getMultiBindArr());
cursor.stmtAddBatch();
cursor.stmtExecute();
cursor.stmtClose();
executeQuery(querySql);
executeUpdate(dropDB);
}
stmtBindParamBatchSample();
setTimeout(() => {
conn.close();
}, 2000);
// const TaosBind = require('../nodetaos/taosBind');
const taos = require('../tdengine');
var conn = taos.connect({ host: "localhost" });
var cursor = conn.cursor();
function executeUpdate(updateSql) {
console.log(updateSql);
cursor.execute(updateSql);
}
function executeQuery(querySql) {
let query = cursor.query(querySql);
query.execute().then((result => {
console.log(querySql);
result.pretty();
}));
}
function stmtBindParamSample() {
let db = 'node_test_db';
let table = 'stmt_taos_bind_sample';
let createDB = `create database if not exists ${db} keep 3650;`;
let dropDB = `drop database if exists ${db};`;
let useDB = `use ${db}`;
let createTable = `create table if not exists ${table} ` +
`(ts timestamp,` +
`nil int,` +
`bl bool,` +
`i8 tinyint,` +
`i16 smallint,` +
`i32 int,` +
`i64 bigint,` +
`f32 float,` +
`d64 double,` +
`bnr binary(20),` +
`nchr nchar(20),` +
`u8 tinyint unsigned,` +
`u16 smallint unsigned,` +
`u32 int unsigned,` +
`u64 bigint unsigned);`;
let querySql = `select * from ${table};`;
let insertSql = `insert into ? values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);`
executeUpdate(dropDB);
executeUpdate(createDB);
executeUpdate(useDB);
executeUpdate(createTable);
let binds = new taos.TaosBind(15);
binds.bindTimestamp(1642435200000);
binds.bindNil();
binds.bindBool(true);
binds.bindTinyInt(127);
binds.bindSmallInt(32767);
binds.bindInt(1234555);
binds.bindBigInt(-164243520000011111n);
binds.bindFloat(214.02);
binds.bindDouble(2.01);
binds.bindBinary('taosdata涛思数据');
binds.bindNchar('TDengine数据');
binds.bindUTinyInt(254);
binds.bindUSmallInt(65534);
binds.bindUInt(4294967294);
binds.bindUBigInt(164243520000011111n);
cursor.stmtInit();
cursor.stmtPrepare(insertSql);
cursor.stmtSetTbname(table);
cursor.stmtBindParam(binds.getBind());
cursor.stmtAddBatch();
cursor.stmtExecute();
cursor.stmtClose();
executeQuery(querySql);
executeUpdate(dropDB);
}
stmtBindParamSample();
setTimeout(() => {
conn.close();
}, 2000);
\ No newline at end of file
const taos = require('../tdengine');
var conn = taos.connect({ host: "localhost" });
var cursor = conn.cursor();
function executeUpdate(updateSql) {
console.log(updateSql);
cursor.execute(updateSql);
}
function executeQuery(querySql) {
let query = cursor.query(querySql);
query.execute().then((result => {
console.log(querySql);
result.pretty();
}));
}
function stmtSingleParaBatchSample() {
let db = 'node_test_db';
let table = 'stmt_taos_bind_single_bind_batch';
let createDB = `create database if not exists ${db} keep 3650;`;
let dropDB = `drop database if exists ${db};`;
let useDB = `use ${db}`;
let createTable = `create table if not exists ${table} ` +
`(ts timestamp,` +
`bl bool,` +
`i8 tinyint,` +
`i16 smallint,` +
`i32 int,` +
`i64 bigint,` +
`f32 float,` +
`d64 double,` +
`bnr binary(20),` +
`nchr nchar(20),` +
`u8 tinyint unsigned,` +
`u16 smallint unsigned,` +
`u32 int unsigned,` +
`u64 bigint unsigned` +
`)tags(` +
`jsonTag json` +
`);`;
let querySql = `select * from ${table};`;
let insertSql = `insert into ? using ${table} tags(?) values(?,?,?,?,?,?,?,?,?,?,?,?,?,?);`
executeUpdate(dropDB);
executeUpdate(createDB);
executeUpdate(useDB);
executeUpdate(createTable);
// normal colum values.
let mbind = new taos.TaosMultiBind();
let tsMBind = mbind.multiBindTimestamp([1642435200000, 1642435300000, 1642435400000, 1642435500000, 1642435600000])
let boolMbind = mbind.multiBindBool([true, false, true, undefined, null]);
let tinyIntMbind = mbind.multiBindTinyInt([-127, 3, 127, null, undefined]);
let smallIntMbind = mbind.multiBindSmallInt([-256, 0, 256, null, undefined]);
let intMbind = mbind.multiBindInt([-1299, 0, 1233, null, undefined]);
let bigIntMbind = mbind.multiBindBigInt([16424352000002222n, -16424354000001111n, 0, null, undefined]);
let floatMbind = mbind.multiBindFloat([12.33, 0, -3.1415, null, undefined]);
let doubleMbind = mbind.multiBindDouble([3.141592653, 0, -3.141592653, null, undefined]);
let binaryMbind = mbind.multiBindBinary(['TDengine_Binary', '', 'taosdata涛思数据', null, undefined]);
let ncharMbind = mbind.multiBindNchar(['taos_data_nchar', 'taosdata涛思数据', '', null, undefined]);
let uTinyIntMbind = mbind.multiBindUTinyInt([0, 127, 254, null, undefined]);
let uSmallIntMbind = mbind.multiBindUSmallInt([0, 256, 512, null, undefined]);
let uIntMbind = mbind.multiBindUInt([0, 1233, 4294967294, null, undefined]);
let uBigIntMbind = mbind.multiBindUBigInt([16424352000002222n, 36424354000001111n, 0, null, undefined]);
// tags value.
let tags = new taos.TaosBind(1);
tags.bindJson('{\"key1\":\"taosdata\",\"key2\":null,\"key3\":\"TDengine涛思数据\",\"key4\":3.2}');
cursor.stmtInit();
cursor.stmtPrepare(insertSql);
cursor.stmtSetTbnameTags('s_01', tags.getBind());
cursor.stmtBindSingleParamBatch(tsMBind, 0);
cursor.stmtBindSingleParamBatch(boolMbind, 1);
cursor.stmtBindSingleParamBatch(tinyIntMbind, 2);
cursor.stmtBindSingleParamBatch(smallIntMbind, 3);
cursor.stmtBindSingleParamBatch(intMbind, 4);
cursor.stmtBindSingleParamBatch(bigIntMbind, 5);
cursor.stmtBindSingleParamBatch(floatMbind, 6);
cursor.stmtBindSingleParamBatch(doubleMbind, 7);
cursor.stmtBindSingleParamBatch(binaryMbind, 8);
cursor.stmtBindSingleParamBatch(ncharMbind, 9);
cursor.stmtBindSingleParamBatch(uTinyIntMbind, 10);
cursor.stmtBindSingleParamBatch(uSmallIntMbind, 11);
cursor.stmtBindSingleParamBatch(uIntMbind, 12);
cursor.stmtBindSingleParamBatch(uBigIntMbind, 13);
cursor.stmtAddBatch();
cursor.stmtExecute();
cursor.stmtClose();
executeQuery(querySql);
executeUpdate(dropDB);
}
stmtSingleParaBatchSample();
setTimeout(() => {
conn.close();
}, 2000);
const taos = require('../tdengine');
var conn = taos.connect({ host: "localhost" });
var cursor = conn.cursor();
function executeUpdate(updateSql) {
console.log(updateSql);
cursor.execute(updateSql);
}
function executeQuery(querySql) {
let query = cursor.query(querySql);
query.execute().then((result => {
console.log(querySql);
result.pretty();
}));
}
function stmtUseResultSample() {
let db = 'node_test_db';
let table = 'stmt_use_result';
let subTable = 's1_0';
let createDB = `create database if not exists ${db} keep 3650;`;
let dropDB = `drop database if exists ${db};`;
let useDB = `use ${db}`;
let createTable = `create table if not exists ${table} ` +
`(ts timestamp,` +
`bl bool,` +
`i8 tinyint,` +
`i16 smallint,` +
`i32 int,` +
`i64 bigint,` +
`f32 float,` +
`d64 double,` +
`bnr binary(20),` +
`nchr nchar(20),` +
`u8 tinyint unsigned,` +
`u16 smallint unsigned,` +
`u32 int unsigned,` +
`u64 bigint unsigned` +
`)tags(` +
`jsonTag json` +
`);`;
let createSubTable = `create table if not exists ${subTable} using ${table} tags('{\"key1\":\"taosdata\",\"key2\":null,\"key3\":\"TDengine涛思数据\",\"key4\":3.2}')`;
let querySql = `select * from ${table} where i32>? and bnr = ? `;
let insertSql = `insert into ? values(?,?,?,?,?,?,?,?,?,?,?,?,?,?);`;
let mBinds = new taos.TaosMultiBindArr(14);
mBinds.multiBindTimestamp([1642435200000,1642435300000,1642435400000,1642435500000,1642435600000]);
mBinds.multiBindBool([true,false,true,undefined,null]);
mBinds.multiBindTinyInt([-127,3,127,null,undefined]);
mBinds.multiBindSmallInt([-256,0,256,null,undefined]);
mBinds.multiBindInt([-1299,0,1233,null,undefined]);
mBinds.multiBindBigInt([16424352000002222n,-16424354000001111n,0,null,undefined]);
mBinds.multiBindFloat([12.33,0,-3.1415,null,undefined]);
mBinds.multiBindDouble([3.141592653,0,-3.141592653,null,undefined]);
mBinds.multiBindBinary(['TDengine_Binary','','taosdata涛思数据',null,undefined]);
mBinds.multiBindNchar(['taos_data_nchar','taosdata涛思数据','',null,undefined]);
mBinds.multiBindUTinyInt([0,127, 254,null,undefined]);
mBinds.multiBindUSmallInt([0,256,512,null,undefined]);
mBinds.multiBindUInt([0,1233,4294967294,null,undefined]);
mBinds.multiBindUBigInt([16424352000002222n,36424354000001111n,0,null,undefined]);
// executeUpdate(dropDB);
executeUpdate(createDB);
executeUpdate(useDB);
executeUpdate(createTable);
executeUpdate(createSubTable);
//stmt bind values
cursor.stmtInit();
cursor.stmtPrepare(insertSql);
cursor.loadTableInfo([subTable]);
cursor.stmtSetTbname(subTable);
cursor.stmtBindParamBatch(mBinds.getMultiBindArr());
cursor.stmtAddBatch();
cursor.stmtExecute();
cursor.stmtClose();
// stmt select with normal column.
let condition1 = new taos.TaosBind(2);
condition1.bindInt(0);
condition1.bindNchar('taosdata涛思数据');
cursor.stmtInit();
cursor.stmtPrepare(querySql);
cursor.stmtBindParam(condition1.getBind());
cursor.stmtExecute();
cursor.stmtUseResult();
cursor.stmtClose();
cursor.fetchall();
console.log(cursor.fields);
console.log(cursor.data);
executeUpdate(dropDB);
}
stmtUseResultSample();
setTimeout(() => {
conn.close();
}, 2000);
\ No newline at end of file
// const TaosBind = require('../nodetaos/taosBind');
const taos = require('../tdengine');
var conn = taos.connect({ host: "localhost" });
var cursor = conn.cursor();
function executeUpdate(updateSql){
console.log(updateSql);
cursor.execute(updateSql);
}
function executeQuery(querySql){
let query = cursor.query(querySql);
query.execute().then((result=>{
console.log(querySql);
result.pretty();
}));
}
function stmtBindParamSample(){
let db = 'node_test_db';
let table = 'stmt_taos_bind_sample';
let createDB = `create database if not exists ${db} keep 3650;`;
let dropDB = `drop database if exists ${db};`;
let useDB = `use ${db}`;
let createTable = `create table if not exists ${table} `+
`(ts timestamp,`+
`nil int,`+
`bl bool,`+
`i8 tinyint,`+
`i16 smallint,`+
`i32 int,`+
`i64 bigint,`+
`f32 float,`+
`d64 double,`+
`bnr binary(20),`+
`nchr nchar(20),`+
`u8 tinyint unsigned,`+
`u16 smallint unsigned,`+
`u32 int unsigned,`+
`u64 bigint unsigned);`;
let querySql = `select * from ${table};`;
let insertSql = `insert into ? values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);`
executeUpdate(dropDB);
executeUpdate(createDB);
executeUpdate(useDB);
executeUpdate(createTable);
let binds = new taos.TaosBind(15);
binds.bindTimestamp(1642435200000);
binds.bindNil();
binds.bindBool(true);
binds.bindTinyInt(127);
binds.bindSmallInt(32767);
binds.bindInt(1234555);
binds.bindBigInt(-164243520000011111n);
binds.bindFloat(214.02);
binds.bindDouble(2.01);
binds.bindBinary('taosdata涛思数据');
binds.bindNchar('TDengine数据');
binds.bindUTinyInt(254);
binds.bindUSmallInt(65534);
binds.bindUInt(4294967294);
binds.bindUBigInt(164243520000011111n);
cursor.stmtInit();
cursor.stmtPrepare(insertSql);
cursor.stmtSetTbname(table);
cursor.bindParam(binds.getBind());
cursor.addBatch();
cursor.stmtExecute();
cursor.stmtClose();
executeQuery(querySql);
executeUpdate(dropDB);
}
stmtBindParamSample();
setTimeout(()=>{
conn.close();
},2000);
\ No newline at end of file
const taos = require('../tdengine');
var conn = taos.connect({ host: "127.0.0.1", user: "root", password: "taosdata", config: "/etc/taos", port: 10 });
var c1 = conn.cursor();
function executeUpdate(sql) {
console.log(sql);
c1.execute(sql);
}
function executeQuery(sql, flag = "all") {
console.log(sql);
c1.execute(sql)
var data = c1.fetchall();
if (flag == "metadata" || flag == "all") {
// Latest query's Field metadata is stored in cursor.fields
console.log(c1.fields);
} 2
if (flag == "data" || flag == "all") {
// Latest query's result data is stored in cursor.data, also returned by fetchall.
console.log(c1.data);
}
console.log("");
}
function prettyQuery(sql) {
try {
c1.query(sql).execute().then(function (result) {
result.pretty();
});
}
catch (err) {
conn.close();
throw err;
}
}
function executeError(sql) {
console.log(sql);
try {
c1.execute(sql)
} catch (e) {
console.log(e.message);
console.log("");
}
}
executeUpdate("create database if not exists nodedb keep 36500;");
executeUpdate("use nodedb;");
console.log("# STEP 1 prepare data & validate json string");
executeUpdate("create table if not exists jsons1(ts timestamp, dataInt int, dataBool bool, dataStr nchar(50), dataStrBin binary(150)) tags(jtag json);");
executeUpdate("insert into jsons1_1 using jsons1 tags('{\"tag1\":\"fff\",\"tag2\":5, \"tag3\":true}') values(1591060618000, 1, false, 'json1', '涛思数据') (1591060608000, 23, true, '涛思数据', 'json')");
executeUpdate("insert into jsons1_2 using jsons1 tags('{\"tag1\":5,\"tag2\":\"beijing\"}') values (1591060628000, 2, true, 'json2', 'sss')");
executeUpdate("insert into jsons1_3 using jsons1 tags('{\"tag1\":false,\"tag2\":\"beijing\"}') values (1591060668000, 3, false, 'json3', 'efwe')");
executeUpdate("insert into jsons1_4 using jsons1 tags('{\"tag1\":null,\"tag2\":\"shanghai\",\"tag3\":\"hello\"}') values (1591060728000, 4, true, 'json4', '323sd')");
executeUpdate("insert into jsons1_5 using jsons1 tags('{\"tag1\":1.232, \"tag2\":null}') values(1591060928000, 1, false, '涛思数据', 'ewe')");
executeUpdate("insert into jsons1_6 using jsons1 tags('{\"tag1\":11,\"tag2\":\"\",\"tag2\":null}') values(1591061628000, 11, false, '涛思数据','')");
executeUpdate("insert into jsons1_7 using jsons1 tags('{\"tag1\":\"涛思数据\",\"tag2\":\"\",\"tag3\":null}') values(1591062628000, 2, NULL, '涛思数据', 'dws')");
console.log("## test duplicate key using the first one. elimate empty key");
executeUpdate("CREATE TABLE if not exists jsons1_8 using jsons1 tags('{\"tag1\":null, \"tag1\":true, \"tag1\":45, \"1tag$\":2, \" \":90}')");
console.log("## test empty json string, save as jtag is NULL");
executeUpdate("insert into jsons1_9 using jsons1 tags('\t') values (1591062328000, 24, NULL, '涛思数据', '2sdw')");
executeUpdate("CREATE TABLE if not exists jsons1_10 using jsons1 tags('')");
executeUpdate("CREATE TABLE if not exists jsons1_11 using jsons1 tags(' ')");
executeUpdate("CREATE TABLE if not exists jsons1_12 using jsons1 tags('{}')");
executeUpdate("CREATE TABLE if not exists jsons1_13 using jsons1 tags('null')");
console.log("## test invalidate json");
executeError("CREATE TABLE if not exists jsons1_14 using jsons1 tags('\"efwewf\"')");
executeError("CREATE TABLE if not exists jsons1_14 using jsons1 tags('3333')");
executeError("CREATE TABLE if not exists jsons1_14 using jsons1 tags('33.33')");
executeError("CREATE TABLE if not exists jsons1_14 using jsons1 tags('false')");
executeError("CREATE TABLE if not exists jsons1_14 using jsons1 tags('[1,true]')");
executeError("CREATE TABLE if not exists jsons1_14 using jsons1 tags('{222}')");
executeError("CREATE TABLE if not exists jsons1_14 using jsons1 tags('{\"fe\"}')");
executeQuery("select * from jsons1;", "data");
console.log("## test invalidate json key, key must can be printed assic char=");
executeError("CREATE TABLE if not exists jsons1_14 using jsons1 tags('{\"tag1\":[1,true]}')");
executeError("CREATE TABLE if not exists jsons1_14 using jsons1 tags('{\"tag1\":{}}')");
executeError("CREATE TABLE if not exists jsons1_14 using jsons1 tags('{\"。loc\":\"fff\"}')");
executeError("CREATE TABLE if not exists jsons1_14 using jsons1 tags('{\"\":\"fff\"}')");
executeError("CREATE TABLE if not exists jsons1_14 using jsons1 tags('{\"\t\":\"fff\"}')");
executeError("CREATE TABLE if not exists jsons1_14 using jsons1 tags('{\"涛思数据\":\"fff\"}')");
console.log("# STEP 2 alter table json tag");
executeError("ALTER STABLE jsons1 add tag tag2 nchar(20)");
executeError("ALTER STABLE jsons1 drop tag jtag");
executeError("ALTER TABLE jsons1_1 SET TAG jtag=4");
executeUpdate("ALTER TABLE jsons1_1 SET TAG jtag='{\"tag1\":\"femail\",\"tag2\":35,\"tag3\":true}'")
console.log("# STEP 3 query table");
console.log("## test error syntax");
executeError("select * from jsons1 where jtag->tag1='beijing'");
executeError("select * from jsons1 where jtag->'location'");
executeError("select * from jsons1 where jtag->''");
executeError("select * from jsons1 where jtag->''=9");
executeError("select -> from jsons1");
executeError("select * from jsons1 where contains");
executeError("select * from jsons1 where jtag->");
executeError("select jtag->location from jsons1");
executeError("select jtag contains location from jsons1");
executeError("select * from jsons1 where jtag contains location");
executeError("select * from jsons1 where jtag contains''");
executeError("select * from jsons1 where jtag contains 'location'='beijing'");
console.log("## test select normal column");
executeQuery("select dataint from jsons1");
console.log("## test select json tag");
executeQuery("select * from jsons1", "data")
executeQuery("select jtag from jsons1", "data");
executeQuery("select jtag from jsons1 where jtag is null", "data");
executeQuery("select jtag from jsons1 where jtag is not null", "data");
executeQuery("select jtag from jsons1_8", "data");
executeQuery("select jtag from jsons1_1", "data");
console.log("## test jtag is NULL");
executeQuery("select jtag from jsons1_9", "data");
console.log("## test select json tag->'key', value is string");
executeQuery("select jtag->'tag1' from jsons1_1", "data");
executeQuery("select jtag->'tag2' from jsons1_6", "data");
console.log("### test select json tag->'key', value is int");
executeQuery("select jtag->'tag2' from jsons1_1", "data");
console.log("### test select json tag->'key', value is bool");
executeQuery("select jtag->'tag3' from jsons1_1", "data");
console.log("### test select json tag->'key', value is null");
executeQuery("select jtag->'tag1' from jsons1_4", "data");
console.log("### test select json tag->'key', value is double");
executeQuery("select jtag->'tag1' from jsons1_5", "data");
console.log("### test select json tag->'key', key is not exist");
executeQuery("select jtag->'tag10' from jsons1_4", "data");
executeQuery("select jtag->'tag1' from jsons1", "data");
console.log("### test header name");
executeQuery("select jtag->'tag1' from jsons1", "metadata");
console.log("## test where with json tag");
executeError("select * from jsons1_1 where jtag is not null");
executeError("select * from jsons1 where jtag='{\"tag1\":11,\"tag2\":\"\"}'");
executeError("select * from jsons1 where jtag->'tag1'={}");
console.log("### where json value is string");
executeQuery("select * from jsons1 where jtag->'tag2'='beijing'", "data");
executeQuery("select dataint,tbname,jtag->'tag1',jtag from jsons1 where jtag->'tag2'='beijing'");
executeQuery("select * from jsons1 where jtag->'tag1'='beijing'", "data");
executeQuery("select * from jsons1 where jtag->'tag1'='涛思数据'", "data");
executeQuery("select * from jsons1 where jtag->'tag2'>'beijing'", "data");
executeQuery("select * from jsons1 where jtag->'tag2'>='beijing'", "data");
executeQuery("select * from jsons1 where jtag->'tag2'<'beijing'", "data");
executeQuery("select * from jsons1 where jtag->'tag2'<='beijing'", "data");
executeQuery("select * from jsons1 where jtag->'tag2'!='beijing'", "data");
executeQuery("select * from jsons1 where jtag->'tag2'=''", "data");
console.log("### where json value is int");
executeQuery("select * from jsons1 where jtag->'tag1'=5", "data");
executeQuery("select * from jsons1 where jtag->'tag1'=10", "data");
executeQuery("select * from jsons1 where jtag->'tag1'<54", "data");
executeQuery("select * from jsons1 where jtag->'tag1'<=11", "data");
executeQuery("select * from jsons1 where jtag->'tag1'>4", "data");
executeQuery("select * from jsons1 where jtag->'tag1'>=5", "data");
executeQuery("select * from jsons1 where jtag->'tag1'!=5", "data");
executeQuery("select * from jsons1 where jtag->'tag1'!=55", "data");
console.log("### where json value is double");
executeQuery("select * from jsons1 where jtag->'tag1'=1.232", "data");
executeQuery("select * from jsons1 where jtag->'tag1'<1.232", "data");
executeQuery("select * from jsons1 where jtag->'tag1'<=1.232", "data");
executeQuery("select * from jsons1 where jtag->'tag1'>1.23", "data");
executeQuery("select * from jsons1 where jtag->'tag1'>=1.232", "data");
executeQuery("select * from jsons1 where jtag->'tag1'!=1.232", "data");
executeQuery("select * from jsons1 where jtag->'tag1'!=3.232", "data");
executeError("select * from jsons1 where jtag->'tag1'/0=3", "data");
executeError("select * from jsons1 where jtag->'tag1'/5=1", "data");
console.log("### where json value is bool");
executeQuery("select * from jsons1 where jtag->'tag1'=true", "data");
executeQuery("select * from jsons1 where jtag->'tag1'=false", "data");
executeQuery("select * from jsons1 where jtag->'tag1'!=false", "data");
executeError("select * from jsons1 where jtag->'tag1'>false");
console.log("### where json value is null");
executeQuery("select * from jsons1 where jtag->'tag1'=null"); //only json suport =null. This synatx will change later.
console.log("### where json is null");
executeQuery("select * from jsons1 where jtag is null", "data");
executeQuery("select * from jsons1 where jtag is not null", "data");
console.log("### where json key is null");
executeQuery("select * from jsons1 where jtag->'tag_no_exist'=3", "data")
console.log("### where json value is not exist");
executeQuery("select * from jsons1 where jtag->'tag1' is null", "data");
executeQuery("select * from jsons1 where jtag->'tag4' is null", "data");
executeQuery("select * from jsons1 where jtag->'tag3' is not null", "data")
console.log("### test contains");
executeQuery("select * from jsons1 where jtag contains 'tag1'", "data")
executeQuery("select * from jsons1 where jtag contains 'tag3'", "data")
executeQuery("select * from jsons1 where jtag contains 'tag_no_exist'", "data")
console.log("### test json tag in where condition with and/or");
executeQuery("select * from jsons1 where jtag->'tag1'=false and jtag->'tag2'='beijing'", "data");
executeQuery("select * from jsons1 where jtag->'tag1'=false or jtag->'tag2'='beijing'", "data");
executeQuery("select * from jsons1 where jtag->'tag1'=false and jtag->'tag2'='shanghai'", "data");
executeQuery("select * from jsons1 where jtag->'tag1'=false and jtag->'tag2'='shanghai'", "data");
executeQuery("select * from jsons1 where jtag->'tag1'=13 or jtag->'tag2'>35", "data");
executeQuery("select * from jsons1 where jtag->'tag1'=13 or jtag->'tag2'>35", "data");
executeQuery("select * from jsons1 where jtag->'tag1' is not null and jtag contains 'tag3'", "data");
executeQuery("select * from jsons1 where jtag->'tag1'='femail' and jtag contains 'tag3'", "data");
console.log("### test with tbname/normal column");
executeQuery("select * from jsons1 where tbname = 'jsons1_1'", "data")
executeQuery("select * from jsons1 where tbname = 'jsons1_1' and jtag contains 'tag3'", "data")
executeQuery("select * from jsons1 where tbname = 'jsons1_1' and jtag contains 'tag3' and dataint=3", "data")
executeQuery("select * from jsons1 where tbname = 'jsons1_1' and jtag contains 'tag3' and dataint=23", "data")
console.log("### test where condition like");
executeQuery("select *,tbname from jsons1 where jtag->'tag2' like 'bei%'", "data");
executeQuery("select *,tbname from jsons1 where jtag->'tag1' like 'fe%' and jtag->'tag2' is not null", "data");
console.log("### test where condition in no support in");
executeError("select * from jsons1 where jtag->'tag1' in ('beijing')");
console.log("### test where condition match");
executeQuery("select * from jsons1 where jtag->'tag1' match 'ma'", "data");
executeQuery("select * from jsons1 where jtag->'tag1' match 'ma$'", "data");
executeQuery("select * from jsons1 where jtag->'tag2' match 'jing$'", "data");
executeQuery("select * from jsons1 where jtag->'tag1' match '收到'", "data");
console.log("### test distinct");
executeUpdate("insert into jsons1_14 using jsons1 tags('{\"tag1\":\"涛思数据\",\"tag2\":\"\",\"tag3\":null}') values(1591062628000, 2, NULL, '涛思数据', 'dws')", "data");
executeQuery("select distinct jtag->'tag1' from jsons1", "data");
executeQuery("select distinct jtag from jsons1", "data");
console.log("### test dumplicate key with normal colomn");
executeUpdate("INSERT INTO jsons1_15 using jsons1 tags('{\"tbname\":\"tt\",\"databool\":true,\"datastr\":\"涛思数据\"}') values(1591060828000, 4, false, 'jjsf', \"涛思数据\")");
executeQuery("select *,tbname,jtag from jsons1 where jtag->'datastr' match '涛思' and datastr match 'js'", "data");
executeQuery("select tbname,jtag->'tbname' from jsons1 where jtag->'tbname'='tt' and tbname='jsons1_14'", "data");
console.log("## test join");
executeUpdate("create table if not exists jsons2(ts timestamp, dataInt int, dataBool bool, dataStr nchar(50), dataStrBin binary(150)) tags(jtag json)")
executeUpdate("insert into jsons2_1 using jsons2 tags('{\"tag1\":\"fff\",\"tag2\":5, \"tag3\":true}') values(1591060618000, 2, false, 'json2', '你是2')")
executeUpdate("insert into jsons2_2 using jsons2 tags('{\"tag1\":5,\"tag2\":null}') values (1591060628000, 2, true, 'json2', 'sss')")
executeUpdate("create table if not exists jsons3(ts timestamp, dataInt int, dataBool bool, dataStr nchar(50), dataStrBin binary(150)) tags(jtag json)")
executeUpdate("insert into jsons3_1 using jsons3 tags('{\"tag1\":\"fff\",\"tag2\":5, \"tag3\":true}') values(1591060618000, 3, false, 'json3', '你是3')")
executeUpdate("insert into jsons3_2 using jsons3 tags('{\"tag1\":5,\"tag2\":\"beijing\"}') values (1591060638000, 2, true, 'json3', 'sss')")
executeQuery("select 'sss',33,a.jtag->'tag3' from jsons2 a,jsons3 b where a.ts=b.ts and a.jtag->'tag1'=b.jtag->'tag1'", "data");
executeQuery("select 'sss',33,a.jtag->'tag3' from jsons2 a,jsons3 b where a.ts=b.ts and a.jtag->'tag1'=b.jtag->'tag1'", "metadata");
console.log("## test group by & order by json tag");
executeQuery("select count(*) from jsons1 group by jtag->'tag1' order by jtag->'tag1' desc", "data");
executeQuery("select count(*) from jsons1 group by jtag->'tag1' order by jtag->'tag1' asc", "data");
console.log("## test stddev with group by json tag");
executeQuery("select stddev(dataint) from jsons1 group by jtag->'tag1'", "data");
executeQuery("select stddev(dataint) from jsons1 group by jsons1.jtag->'tag1'", "metadata");
console.log("## test top/bottom with group by json tag");
executeQuery("select top(dataint,100) from jsons1 group by jtag->'tag1'", "metadata");
console.log("## subquery with json tag");
executeQuery("select * from (select jtag, dataint from jsons1)", "metadata");
executeQuery("select jtag->'tag1' from (select jtag->'tag1', dataint from jsons1)", "metadata");
executeQuery("select jtag->'tag1' from (select jtag->'tag1', dataint from jsons1)", "metada");
executeQuery("select ts,tbname,jtag->'tag1' from (select jtag->'tag1',tbname,ts from jsons1 order by ts)", "data")
executeUpdate("drop database nodedb;");
setTimeout(() => conn.close(), 2000);
const taos = require('../tdengine');
var conn = taos.connect();
var c1 = conn.cursor();
let stime = new Date();
let interval = 1000;
function convertDateToTS(date) {
let tsArr = date.toISOString().split("T")
return "\"" + tsArr[0] + " " + tsArr[1].substring(0, tsArr[1].length - 1) + "\"";
}
function R(l, r) {
return Math.random() * (r - l) - r;
}
function randomBool() {
if (Math.random() < 0.5) {
return true;
}
return false;
}
// Initialize
//c1.execute('drop database td_connector_test;');
const dbname = 'nodejs_test_us';
c1.execute('create database if not exists ' + dbname + ' precision "us"');
c1.execute('use ' + dbname)
c1.execute('create table if not exists tstest (ts timestamp, _int int);');
c1.execute('insert into tstest values(1625801548423914, 0)');
// Select
console.log('select * from tstest');
c1.execute('select * from tstest');
var d = c1.fetchall();
console.log(c1.fields);
let ts = d[0][0];
console.log(ts);
if (ts.taosTimestamp() != 1625801548423914) {
throw "microseconds not match!";
}
if (ts.getMicroseconds() % 1000 !== 914) {
throw "micronsecond precision error";
}
setTimeout(function () {
c1.query('drop database nodejs_us_test;');
}, 200);
setTimeout(function () {
conn.close();
}, 2000);
const taos = require('../tdengine');
var conn = taos.connect();
var c1 = conn.cursor();
let stime = new Date();
let interval = 1000;
function convertDateToTS(date) {
let tsArr = date.toISOString().split("T")
return "\"" + tsArr[0] + " " + tsArr[1].substring(0, tsArr[1].length - 1) + "\"";
}
function R(l, r) {
return Math.random() * (r - l) - r;
}
function randomBool() {
if (Math.random() < 0.5) {
return true;
}
return false;
}
// Initialize
//c1.execute('drop database td_connector_test;');
const dbname = 'nodejs_test_ns';
c1.execute('create database if not exists ' + dbname + ' precision "ns"');
c1.execute('use ' + dbname)
c1.execute('create table if not exists tstest (ts timestamp, _int int);');
c1.execute('insert into tstest values(1625801548423914405, 0)');
// Select
console.log('select * from tstest');
c1.execute('select * from tstest');
var d = c1.fetchall();
console.log(c1.fields);
let ts = d[0][0];
console.log(ts);
if (ts.taosTimestamp() != 1625801548423914405) {
throw "nanosecond not match!";
}
if (ts.getNanoseconds() % 1000000 !== 914405) {
throw "nanosecond precision error";
}
setTimeout(function () {
c1.query('drop database nodejs_ns_test;');
}, 200);
setTimeout(function () {
conn.close();
}, 2000);
const _ = require('lodash');
const taos = require('../tdengine');
var conn = taos.connect({ host: "127.0.0.1", user: "root", password: "taosdata", config: "/etc/taos", port: 10 });
var c1 = conn.cursor();
executeUpdate("drop database if exists nodedb;");
executeUpdate("create database if not exists nodedb ;");
executeUpdate("use nodedb;");
let tbname1 = "line_protocol_arr";
let tbname2 = "json_protocol_arr";
let tbname3 = "json_protocol_str";
let tbname4 = "line_protocol_str";
let line1 = [tbname1 + ",t1=3i64,t2=4f64,t3=\"t3\" c1=3i64,c3=L\"passit\",c2=false,c4=4f64 1626006833639000000",
tbname1 + ",t1=4i64,t3=\"t4\",t2=5f64,t4=5f64 c1=3i64,c3=L\"passitagin\",c2=true,c4=5f64,c5=5f64 1626006833641000000"
];
let line2 = ["{"
+ "\"metric\": \"" + tbname2 + "\","
+ "\"timestamp\": 1626006833,"
+ "\"value\": 10,"
+ "\"tags\": {"
+ " \"t1\": true,"
+ "\"t2\": false,"
+ "\"t3\": 10,"
+ "\"t4\": \"123_abc_.!@#$%^&*:;,./?|+-=()[]{}<>\""
+ "}"
+ "}"
];
let line3 = "{"
+ "\"metric\": \"" + tbname3 + "\","
+ "\"timestamp\": 1626006833000,"
+ "\"value\": 10,"
+ "\"tags\": {"
+ " \"t1\": true,"
+ "\"t2\": false,"
+ "\"t3\": 10,"
+ "\"t4\": \"123_abc_.!@#$%^&*:;,./?|+-=()[]{}<>\""
+ "}"
+ "}";
let line4 = tbname4 + ",t1=3i64,t2=4f64,t3=\"t3\" c1=3i64,c3=L\"passit\",c2=false,c4=4f64 1626006833639";
try {
c1.schemalessInsert(line1, taos.SCHEMALESS_PROTOCOL.TSDB_SML_LINE_PROTOCOL, taos.SCHEMALESS_PRECISION.TSDB_SML_TIMESTAMP_NANO_SECONDS);
testSchemaless(tbname1, line1.length);
c1.schemalessInsert(line2, taos.SCHEMALESS_PROTOCOL.TSDB_SML_JSON_PROTOCOL, taos.SCHEMALESS_PRECISION.TSDB_SML_TIMESTAMP_SECONDS);
testSchemaless(tbname2, line2.length);
c1.schemalessInsert(line3, taos.SCHEMALESS_PROTOCOL.TSDB_SML_JSON_PROTOCOL, taos.SCHEMALESS_PRECISION.TSDB_SML_TIMESTAMP_MILLI_SECONDS);
testSchemaless(tbname3, 1);
c1.schemalessInsert(line4, taos.SCHEMALESS_PROTOCOL.TSDB_SML_LINE_PROTOCOL, taos.SCHEMALESS_PRECISION.TSDB_SML_TIMESTAMP_MILLI_SECONDS);
testSchemaless(tbname4, 1);
} catch (err) {
console.log(err)
}
function executeUpdate(sql) {
console.log(sql);
c1.execute(sql);
}
function testSchemaless(tbname, numLines) {
let sql = "select count(*) from " + tbname + ";";
executeUpdate(sql);
let affectRows = _.first(c1.fetchall());
if (affectRows != numLines) {
console.log(1);
console.log(line2);
throw "protocol " + tbname + " schemaless insert success,but can't select as expect."
}
else {
console.log("protocol " + tbname + " schemaless insert success, can select as expect.")
}
console.log("===================")
}
setTimeout(() => conn.close(), 2000);
const taos = require('../tdengine');
var conn = taos.connect({host:"127.0.0.1", user:"root", password:"taosdata", config:"/etc/taos",port:10});
var c1 = conn.cursor();
let stime = new Date();
let interval = 1000;
c1.execute('use td_connector_test');
let sub = c1.subscribe({
restart: true,
sql: "select AVG(_int) from td_connector_test.all_Types;",
topic: 'all_Types',
interval: 1000
});
c1.consumeData(sub, (data, fields) => {
console.log(data);
});
\ No newline at end of file
const taos = require('../tdengine');
var conn = taos.connect({ host: "127.0.0.1", user: "root", password: "taosdata", config: "/etc/taos", port: 10 });
var c1 = conn.cursor();
function executeUpdate(sql) {
console.log(sql);
c1.execute(sql);
}
function executeQuery(sql) {
c1.execute(sql)
var data = c1.fetchall();
// Latest query's Field metadata is stored in cursor.fields
console.log(c1.fields);
// Latest query's result data is stored in cursor.data, also returned by fetchall.
console.log(c1.data);
}
function prettyQuery(sql){
try {
c1.query(sql).execute().then(function(result){
result.pretty();
});
}
catch (err) {
conn.close();
throw err;
}
}
executeUpdate("create database nodedb;");
executeUpdate("use nodedb;");
executeUpdate("create table unsigntest(ts timestamp,ut tinyint unsigned,us smallint unsigned,ui int unsigned,ub bigint unsigned,bi bigint);");
executeUpdate("insert into unsigntest values (now, 254,65534,4294967294,18446744073709551614,9223372036854775807);");
executeUpdate("insert into unsigntest values (now, 0,0,0,0,-9223372036854775807);");
executeQuery("select * from unsigntest;");
prettyQuery("select * from unsigntest;");
executeUpdate("drop database nodedb;");
setTimeout(()=>conn.close(),2000);
const taos = require('../tdengine');
var conn = taos.connect({ host: "localhost" });
var c1 = conn.cursor();
function checkData(data, row, col, expect) {
let checkdata = data[row][col];
if (checkdata == expect) {
// console.log('check pass')
}
else {
console.log('check failed, expect ' + expect + ', but is ' + checkdata)
}
}
c1.execute('drop database if exists testnodejsnchar')
c1.execute('create database testnodejsnchar')
c1.execute('use testnodejsnchar');
c1.execute('create table tb (ts timestamp, value float, text binary(200))')
c1.execute("insert into tb values('2021-06-10 00:00:00', 24.7, '中文10000000000000000000000');") -
c1.execute('insert into tb values(1623254400150, 24.7, NULL);')
c1.execute('import into tb values(1623254400300, 24.7, "中文3中文10000000000000000000000中文10000000000000000000000中文10000000000000000000000中文10000000000000000000000");')
sql = 'select * from tb;'
console.log('*******************************************')
c1.execute(sql);
data = c1.fetchall();
console.log(data)
//check data about insert data
checkData(data, 0, 2, '中文10000000000000000000000')
checkData(data, 1, 2, null)
checkData(data, 2, 2, '中文3中文10000000000000000000000中文10000000000000000000000中文10000000000000000000000中文10000000000000000000000')
\ No newline at end of file
const taos = require('../tdengine');
var conn = taos.connect();
var c1 = conn.cursor();
let stime = new Date();
let interval = 1000;
function convertDateToTS(date) {
let tsArr = date.toISOString().split("T")
return "\"" + tsArr[0] + " " + tsArr[1].substring(0, tsArr[1].length-1) + "\"";
}
function R(l,r) {
return Math.random() * (r - l) - r;
}
function randomBool() {
if (Math.random() < 0.5) {
return true;
}
return false;
}
// Initialize
//c1.execute('drop database td_connector_test;');
c1.execute('create database if not exists td_connector_test;');
c1.execute('use td_connector_test;')
c1.execute('create table if not exists all_types (ts timestamp, _int int, _bigint bigint, _float float, _double double, _binary binary(40), _smallint smallint, _tinyint tinyint, _bool bool, _nchar nchar(40));');
c1.execute('create table if not exists stabletest (ts timestamp, v1 int, v2 int, v3 int, v4 double) tags (id int, location binary(20));')
// Shell Test : The following uses the cursor to imitate the taos shell
// Insert
for (let i = 0; i < 10000; i++) {
let insertData = ["now+" + i + "s", // Timestamp
parseInt( R(-Math.pow(2,31) + 1 , Math.pow(2,31) - 1) ), // Int
parseInt( R(-Math.pow(2,31) + 1 , Math.pow(2,31) - 1) ), // BigInt
parseFloat( R(-3.4E38, 3.4E38) ), // Float
parseFloat( R(-1.7E30, 1.7E30) ), // Double
"\"Long Binary\"", // Binary
parseInt( R(-32767, 32767) ), // Small Int
parseInt( R(-127, 127) ), // Tiny Int
randomBool(),
"\"Nchars\""]; // Bool
c1.execute('insert into td_connector_test.all_types values(' + insertData.join(',') + ' );', {quiet:true});
if (i % 1000 == 0) {
console.log("Insert # " , i);
}
}
// Select
console.log('select * from td_connector_test.all_types limit 3 offset 100;');
c1.execute('select * from td_connector_test.all_types limit 2 offset 100;');
var d = c1.fetchall();
console.log(c1.fields);
console.log(d);
// Functions
console.log('select count(*), avg(_int), sum(_float), max(_bigint), min(_double) from td_connector_test.all_types;')
c1.execute('select count(*), avg(_int), sum(_float), max(_bigint), min(_double) from td_connector_test.all_types;');
var d = c1.fetchall();
console.log(c1.fields);
console.log(d);
// Immediate Execution like the Shell
c1.query('select count(*), stddev(_double), min(_tinyint) from all_types where _tinyint > 50 and _int < 0;', true).then(function(result){
result.pretty();
})
c1.query('select _tinyint, _bool from all_types where _tinyint > 50 and _int < 0 limit 50;', true).then(function(result){
result.pretty();
})
c1.query('select stddev(_double), stddev(_bigint), stddev(_float) from all_types;', true).then(function(result){
result.pretty();
})
c1.query('select stddev(_double), stddev(_bigint), stddev(_float) from all_types interval(1m) limit 100;', true).then(function(result){
result.pretty();
})
// Binding arguments, and then using promise
var q = c1.query('select _nchar from td_connector_test.all_types where ts >= ? and _int > ? limit 100 offset 40;').bind(new Date(1231), 100)
console.log(q.query);
q.execute().then(function(r) {
r.pretty();
});
// test query null value
c1.execute("create table if not exists td_connector_test.weather(ts timestamp, temperature float, humidity int) tags(location nchar(64))");
c1.execute("insert into t1 using weather tags('北京') values(now, 11.11, 11)");
c1.execute("insert into t1(ts, temperature) values(now, 22.22)");
c1.execute("insert into t1(ts, humidity) values(now, 33)");
c1.query('select * from td_connector_test.t1', true).then(function (result) {
result.pretty();
});
var q = c1.query('select * from td_connector_test.weather');
console.log(q.query);
q.execute().then(function(r) {
r.pretty();
});
function sleep(sleepTime) {
for(var start = +new Date; +new Date - start <= sleepTime; ) { }
}
sleep(10000);
// Raw Async Testing (Callbacks, not promises)
function cb2(param, result, rowCount, rd) {
console.log('CB2 Callbacked!');
console.log("RES *", result);
console.log("Async fetched", rowCount, " rows");
console.log("Passed Param: ", param);
console.log("Fields ", rd.fields);
console.log("Data ", rd.data);
}
function cb1(param,result,code) {
console.log('CB1 Callbacked!');
console.log("RES * ", result);
console.log("Status: ", code);
console.log("Passed Param ", param);
c1.fetchall_a(result, cb2, param);
}
c1.execute_a("describe td_connector_test.all_types;", cb1, {myparam:3.141});
function cb4(param, result, rowCount, rd) {
console.log('CB4 Callbacked!');
console.log("RES *", result);
console.log("Async fetched", rowCount, "rows");
console.log("Passed Param: ", param);
console.log("Fields", rd.fields);
console.log("Data", rd.data);
}
// Without directly calling fetchall_a
var thisRes;
function cb3(param,result,code) {
console.log('CB3 Callbacked!');
console.log("RES *", result);
console.log("Status:", code);
console.log("Passed Param", param);
thisRes = result;
}
//Test calling execute and fetchall seperately and not through callbacks
var param = c1.execute_a("describe td_connector_test.all_types;", cb3, {e:2.718});
console.log("Passed Param outside of callback: ", param);
console.log(param);
setTimeout(function(){
c1.fetchall_a(thisRes, cb4, param);
},100);
// Async through promises
var aq = c1.query('select count(*) from td_connector_test.all_types;',false);
aq.execute_a().then(function(data) {
data.pretty();
});
c1.query('describe td_connector_test.stabletest').execute_a().then(function(r){
r.pretty()
});
setTimeout(function(){
c1.query('drop database td_connector_test;');
},200);
setTimeout(function(){
conn.close();
},2000);
/*
* For a detailed explanation regarding each configuration property, visit:
* https://jestjs.io/docs/configuration
*/
module.exports = {
// All imported modules in your tests should be mocked automatically
// automock: false,
// Stop running tests after `n` failures
// bail: 0,
// The directory where Jest should store its cached dependency information
// cacheDirectory: "/tmp/jest_rt",
// Automatically clear mock calls, instances and results before every test
// clearMocks: true,
// Indicates whether the coverage information should be collected while executing the test
// collectCoverage: true,
// An array of glob patterns indicating a set of files for which coverage information should be collected
// collectCoverageFrom: undefined,
// The directory where Jest should output its coverage files
// coverageDirectory: "coverage",
// An array of regexp pattern strings used to skip coverage collection
// coveragePathIgnorePatterns: [
// "/node_modules/"
// ],
// Indicates which provider should be used to instrument code for coverage
// coverageProvider: "v8",
// A list of reporter names that Jest uses when writing coverage reports
// coverageReporters: [
// "json",
// "text",
// "lcov",
// "clover"
// ],
// An object that configures minimum threshold enforcement for coverage results
// coverageThreshold: undefined,
// A path to a custom dependency extractor
// dependencyExtractor: undefined,
// Make calling deprecated APIs throw helpful error messages
// errorOnDeprecated: false,
// Force coverage collection from ignored files using an array of glob patterns
// forceCoverageMatch: [],
// A path to a module which exports an async function that is triggered once before all test suites
// globalSetup: undefined,
// A path to a module which exports an async function that is triggered once after all test suites
// globalTeardown: undefined,
// A set of global variables that need to be available in all test environments
// globals: {},
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
// maxWorkers: "50%",
// An array of directory names to be searched recursively up from the requiring module's location
// moduleDirectories: [
// "node_modules"
// ],
// An array of file extensions your modules use
// moduleFileExtensions: [
// "js",
// "jsx",
// "ts",
// "tsx",
// "json",
// "node"
// ],
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
// moduleNameMapper: {},
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
// modulePathIgnorePatterns: [],
// Activates notifications for test results
// notify: false,
// An enum that specifies notification mode. Requires { notify: true }
// notifyMode: "failure-change",
// A preset that is used as a base for Jest's configuration
// preset: undefined,
// Run tests from one or more projects
// projects: undefined,
// Use this configuration option to add custom reporters to Jest
// reporters: undefined,
// Automatically reset mock state before every test
// resetMocks: false,
// Reset the module registry before running each individual test
// resetModules: false,
// A path to a custom resolver
// resolver: undefined,
// Automatically restore mock state and implementation before every test
// restoreMocks: false,
// The root directory that Jest should scan for tests and modules within
// rootDir: undefined,
// A list of paths to directories that Jest should use to search for files in
// roots: [
// "<rootDir>"
// ],
// Allows you to use a custom runner instead of Jest's default test runner
// runner: "jest-runner",
// The paths to modules that run some code to configure or set up the testing environment before each test
// setupFiles: [],
// A list of paths to modules that run some code to configure or set up the testing framework before each test
// setupFilesAfterEnv: [],
// The number of seconds after which a test is considered as slow and reported as such in the results.
// slowTestThreshold: 5,
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
// snapshotSerializers: [],
// The test environment that will be used for testing
// testEnvironment: "jest-environment-node",
// Options that will be passed to the testEnvironment
// testEnvironmentOptions: {},
// Adds a location field to test results
// testLocationInResults: false,
// The glob patterns Jest uses to detect test files
// testMatch: [
// "**/__tests__/**/*.[jt]s?(x)",
// "**/?(*.)+(spec|test).[tj]s?(x)"
// ],
testMatch: [
"**/test/cases/*.[jt]s?(x)"
],
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
// testPathIgnorePatterns: [
// "/node_modules/"
// ],
// The regexp pattern or array of patterns that Jest uses to detect test files
// testRegex: [],
// This option allows the use of a custom results processor
// testResultsProcessor: undefined,
// This option allows use of a custom test runner
// testRunner: "jest-circus/runner",
// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
// testURL: "http://localhost",
// Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
// timers: "real",
// A map from regular expressions to paths to transformers
// transform: undefined,
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
// transformIgnorePatterns: [
// "/node_modules/",
// "\\.pnp\\.[^\\/]+$"
// ],
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,
// Indicates whether each individual test should be reported during the run
// verbose: undefined,
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
// watchPathIgnorePatterns: [],
// Whether to use watchman for file crawling
// watchman: true,
};
/**
* C Interface with TDengine Module
* @module CTaosInterface
*/
const ref = require('ref-napi');
const os = require('os');
const ffi = require('ffi-napi');
const ArrayType = require('ref-array-di')(ref);
const Struct = require('ref-struct-di')(ref);
const FieldTypes = require('./constants');
const errors = require('./error');
const _ = require('lodash')
const TaosObjects = require('./taosobjects');
module.exports = CTaosInterface;
const TAOSFIELD = {
NAME_LENGTH: 65,
TYPE_OFFSET: 65,
BYTES_OFFSET: 66,
STRUCT_SIZE: 68,
}
function convertTimestamp(data, num_of_rows, nbytes = 0, offset = 0, precision = 0) {
data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
let res = [];
let currOffset = 0;
while (currOffset < data.length) {
let time = data.readInt64LE(currOffset);
currOffset += nbytes;
res.push(new TaosObjects.TaosTimestamp(time, precision));
}
return res;
}
function convertBool(data, num_of_rows, nbytes = 0, offset = 0, precision = 0) {
data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
let res = new Array(data.length);
for (let i = 0; i < data.length; i++) {
if (data[i] == 0) {
res[i] = false;
}
else if (data[i] == 1) {
res[i] = true;
}
else if (data[i] == FieldTypes.C_BOOL_NULL) {
res[i] = null;
}
}
return res;
}
function convertTinyint(data, num_of_rows, nbytes = 0, offset = 0, precision = 0) {
data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
let res = [];
let currOffset = 0;
while (currOffset < data.length) {
let d = data.readIntLE(currOffset, 1);
res.push(d == FieldTypes.C_TINYINT_NULL ? null : d);
currOffset += nbytes;
}
return res;
}
function convertTinyintUnsigned(data, num_of_rows, nbytes = 0, offset = 0, precision = 0) {
data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
let res = [];
let currOffset = 0;
while (currOffset < data.length) {
let d = data.readUIntLE(currOffset, 1);
res.push(d == FieldTypes.C_TINYINT_UNSIGNED_NULL ? null : d);
currOffset += nbytes;
}
return res;
}
function convertSmallint(data, num_of_rows, nbytes = 0, offset = 0, precision = 0) {
data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
let res = [];
let currOffset = 0;
while (currOffset < data.length) {
let d = data.readIntLE(currOffset, 2);
res.push(d == FieldTypes.C_SMALLINT_NULL ? null : d);
currOffset += nbytes;
}
return res;
}
function convertSmallintUnsigned(data, num_of_rows, nbytes = 0, offset = 0, precision = 0) {
data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
let res = [];
let currOffset = 0;
while (currOffset < data.length) {
let d = data.readUIntLE(currOffset, 2);
res.push(d == FieldTypes.C_SMALLINT_UNSIGNED_NULL ? null : d);
currOffset += nbytes;
}
return res;
}
function convertInt(data, num_of_rows, nbytes = 0, offset = 0, precision = 0) {
data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
let res = [];
let currOffset = 0;
while (currOffset < data.length) {
let d = data.readInt32LE(currOffset);
res.push(d == FieldTypes.C_INT_NULL ? null : d);
currOffset += nbytes;
}
return res;
}
function convertIntUnsigned(data, num_of_rows, nbytes = 0, offset = 0, precision = 0) {
data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
let res = [];
let currOffset = 0;
while (currOffset < data.length) {
let d = data.readUInt32LE(currOffset);
res.push(d == FieldTypes.C_INT_UNSIGNED_NULL ? null : d);
currOffset += nbytes;
}
return res;
}
function convertBigint(data, num_of_rows, nbytes = 0, offset = 0, precision = 0) {
data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
let res = [];
let currOffset = 0;
while (currOffset < data.length) {
let d = data.readInt64LE(currOffset);
res.push(d == FieldTypes.C_BIGINT_NULL ? null : BigInt(d));
currOffset += nbytes;
}
return res;
}
function convertBigintUnsigned(data, num_of_rows, nbytes = 0, offset = 0, precision = 0) {
data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
let res = [];
let currOffset = 0;
while (currOffset < data.length) {
let d = data.readUInt64LE(currOffset);
res.push(d == FieldTypes.C_BIGINT_UNSIGNED_NULL ? null : BigInt(d));
currOffset += nbytes;
}
return res;
}
function convertFloat(data, num_of_rows, nbytes = 0, offset = 0, precision = 0) {
data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
let res = [];
let currOffset = 0;
while (currOffset < data.length) {
let d = parseFloat(data.readFloatLE(currOffset).toFixed(5));
res.push(isNaN(d) ? null : d);
currOffset += nbytes;
}
return res;
}
function convertDouble(data, num_of_rows, nbytes = 0, offset = 0, precision = 0) {
data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
let res = [];
let currOffset = 0;
while (currOffset < data.length) {
let d = parseFloat(data.readDoubleLE(currOffset).toFixed(16));
res.push(isNaN(d) ? null : d);
currOffset += nbytes;
}
return res;
}
function convertBinary(data, num_of_rows, nbytes = 0, offset = 0, precision = 0) {
data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
let res = [];
let currOffset = 0;
while (currOffset < data.length) {
let len = data.readIntLE(currOffset, 2);
let dataEntry = data.slice(currOffset + 2, currOffset + len + 2); //one entry in a row under a column;
if (dataEntry[0] == 255) {
res.push(null)
} else {
res.push(dataEntry.toString("utf-8"));
}
currOffset += nbytes;
}
return res;
}
function convertNchar(data, num_of_rows, nbytes = 0, offset = 0, precision = 0) {
data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
let res = [];
let currOffset = 0;
while (currOffset < data.length) {
let len = data.readIntLE(currOffset, 2);
let dataEntry = data.slice(currOffset + 2, currOffset + len + 2); //one entry in a row under a column;
if (dataEntry[0] == 255 && dataEntry[1] == 255) {
res.push(null)
} else {
res.push(dataEntry.toString("utf-8"));
}
currOffset += nbytes;
}
return res;
}
function convertJsonTag(data, num_of_rows, nbytes = 0, offset = 0, precision = 0) {
data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
let res = [];
let currOffset = 0;
while (currOffset < data.length) {
let len = data.readIntLE(currOffset, 2);
let dataEntry = data.slice(currOffset + 2, currOffset + len + 2); //one entry in a row under a column;
if (dataEntry[0] == 255 && dataEntry[1] == 255) {
res.push(null)
} else {
res.push(dataEntry.toString("utf-8"));
}
currOffset += nbytes;
}
return res;
}
// Object with all the relevant converters from pblock data to javascript readable data
let convertFunctions = {
[FieldTypes.C_BOOL]: convertBool,
[FieldTypes.C_TINYINT]: convertTinyint,
[FieldTypes.C_SMALLINT]: convertSmallint,
[FieldTypes.C_INT]: convertInt,
[FieldTypes.C_BIGINT]: convertBigint,
[FieldTypes.C_FLOAT]: convertFloat,
[FieldTypes.C_DOUBLE]: convertDouble,
[FieldTypes.C_BINARY]: convertBinary,
[FieldTypes.C_TIMESTAMP]: convertTimestamp,
[FieldTypes.C_NCHAR]: convertNchar,
[FieldTypes.C_TINYINT_UNSIGNED]: convertTinyintUnsigned,
[FieldTypes.C_SMALLINT_UNSIGNED]: convertSmallintUnsigned,
[FieldTypes.C_INT_UNSIGNED]: convertIntUnsigned,
[FieldTypes.C_BIGINT_UNSIGNED]: convertBigintUnsigned,
[FieldTypes.C_JSON_TAG]: convertJsonTag,
}
// Define TaosField structure
var char_arr = ArrayType(ref.types.char);
var TaosField = Struct({
'name': char_arr,
});
TaosField.fields.name.type.size = 65;
TaosField.defineProperty('type', ref.types.uint8);
TaosField.defineProperty('bytes', ref.types.int16);
//define schemaless line array
var smlLine = ArrayType(ref.coerceType('char *'))
/**
*
* @param {Object} config - Configuration options for the interface
* @return {CTaosInterface}
* @class CTaosInterface
* @classdesc The CTaosInterface is the interface through which Node.JS communicates data back and forth with TDengine. It is not advised to
* access this class directly and use it unless you understand what these functions do.
*/
function CTaosInterface(config = null, pass = false) {
ref.types.char_ptr = ref.refType(ref.types.char);
ref.types.void_ptr = ref.refType(ref.types.void);
ref.types.void_ptr2 = ref.refType(ref.types.void_ptr);
/*Declare a bunch of functions first*/
/* Note, pointers to TAOS_RES, TAOS, are ref.types.void_ptr. The connection._conn buffer is supplied for pointers to TAOS * */
if ('win32' == os.platform()) {
taoslibname = 'taos';
} else {
taoslibname = 'libtaos';
}
this.libtaos = ffi.Library(taoslibname, {
'taos_options': [ref.types.int, [ref.types.int, ref.types.void_ptr]],
'taos_init': [ref.types.void, []],
//TAOS *taos_connect(char *ip, char *user, char *pass, char *db, int port)
'taos_connect': [ref.types.void_ptr, [ref.types.char_ptr, ref.types.char_ptr, ref.types.char_ptr, ref.types.char_ptr, ref.types.int]],
//void taos_close(TAOS *taos)
'taos_close': [ref.types.void, [ref.types.void_ptr]],
//int *taos_fetch_lengths(TAOS_RES *res);
'taos_fetch_lengths': [ref.types.void_ptr, [ref.types.void_ptr]],
//int taos_query(TAOS *taos, char *sqlstr)
'taos_query': [ref.types.void_ptr, [ref.types.void_ptr, ref.types.char_ptr]],
//int taos_affected_rows(TAOS_RES *res)
'taos_affected_rows': [ref.types.int, [ref.types.void_ptr]],
//int taos_fetch_block(TAOS_RES *res, TAOS_ROW *rows)
'taos_fetch_block': [ref.types.int, [ref.types.void_ptr, ref.types.void_ptr]],
//int taos_num_fields(TAOS_RES *res);
'taos_num_fields': [ref.types.int, [ref.types.void_ptr]],
//TAOS_ROW taos_fetch_row(TAOS_RES *res)
//TAOS_ROW is void **, but we set the return type as a reference instead to get the row
'taos_fetch_row': [ref.refType(ref.types.void_ptr2), [ref.types.void_ptr]],
'taos_print_row': [ref.types.int, [ref.types.char_ptr, ref.types.void_ptr, ref.types.void_ptr, ref.types.int]],
//int taos_result_precision(TAOS_RES *res)
'taos_result_precision': [ref.types.int, [ref.types.void_ptr]],
//void taos_free_result(TAOS_RES *res)
'taos_free_result': [ref.types.void, [ref.types.void_ptr]],
//int taos_field_count(TAOS *taos)
'taos_field_count': [ref.types.int, [ref.types.void_ptr]],
//TAOS_FIELD *taos_fetch_fields(TAOS_RES *res)
'taos_fetch_fields': [ref.refType(TaosField), [ref.types.void_ptr]],
//int taos_errno(TAOS *taos)
'taos_errno': [ref.types.int, [ref.types.void_ptr]],
//char *taos_errstr(TAOS *taos)
'taos_errstr': [ref.types.char_ptr, [ref.types.void_ptr]],
//void taos_stop_query(TAOS_RES *res);
'taos_stop_query': [ref.types.void, [ref.types.void_ptr]],
//char *taos_get_server_info(TAOS *taos);
'taos_get_server_info': [ref.types.char_ptr, [ref.types.void_ptr]],
//char *taos_get_client_info();
'taos_get_client_info': [ref.types.char_ptr, []],
// ASYNC
// void taos_query_a(TAOS *taos, char *sqlstr, void (*fp)(void *, TAOS_RES *, int), void *param)
'taos_query_a': [ref.types.void, [ref.types.void_ptr, ref.types.char_ptr, ref.types.void_ptr, ref.types.void_ptr]],
// void taos_fetch_rows_a(TAOS_RES *res, void (*fp)(void *param, TAOS_RES *, int numOfRows), void *param);
'taos_fetch_rows_a': [ref.types.void, [ref.types.void_ptr, ref.types.void_ptr, ref.types.void_ptr]],
// Subscription
//TAOS_SUB *taos_subscribe(TAOS* taos, int restart, const char* topic, const char *sql, TAOS_SUBSCRIBE_CALLBACK fp, void *param, int interval)
'taos_subscribe': [ref.types.void_ptr, [ref.types.void_ptr, ref.types.int, ref.types.char_ptr, ref.types.char_ptr, ref.types.void_ptr, ref.types.void_ptr, ref.types.int]],
// TAOS_RES *taos_consume(TAOS_SUB *tsub)
'taos_consume': [ref.types.void_ptr, [ref.types.void_ptr]],
//void taos_unsubscribe(TAOS_SUB *tsub);
'taos_unsubscribe': [ref.types.void, [ref.types.void_ptr]],
//Schemaless insert
//TAOS_RES* taos_schemaless_insert(TAOS* taos, char* lines[], int numLines, int protocol,int precision)
// 'taos_schemaless_insert': [ref.types.void_ptr, [ref.types.void_ptr, ref.types.char_ptr, ref.types.int, ref.types.int, ref.types.int]]
'taos_schemaless_insert': [ref.types.void_ptr, [ref.types.void_ptr, smlLine, 'int', 'int', 'int']]
//stmt APIs
// TAOS_STMT* taos_stmt_init(TAOS *taos)
, 'taos_stmt_init': [ref.types.void_ptr, [ref.types.void_ptr]]
// int taos_stmt_prepare(TAOS_STMT *stmt, const char *sql, unsigned long length)
, 'taos_stmt_prepare': [ref.types.int, [ref.types.void_ptr, ref.types.char_ptr, ref.types.ulong]]
// int taos_stmt_set_tbname(TAOS_STMT* stmt, const char* name)
, 'taos_stmt_set_tbname': [ref.types.int, [ref.types.void_ptr, ref.types.char_ptr]]
// int taos_stmt_set_tbname_tags(TAOS_STMT* stmt, const char* name, TAOS_BIND* tags)
, 'taos_stmt_set_tbname_tags': [ref.types.int, [ref.types.void_ptr, ref.types.char_ptr, ref.types.void_ptr]]
// int taos_stmt_set_sub_tbname(TAOS_STMT* stmt, const char* name)
, 'taos_stmt_set_sub_tbname': [ref.types.int, [ref.types.void_ptr, ref.types.char_ptr]]
// int taos_stmt_bind_param(TAOS_STMT *stmt, TAOS_BIND *bind)
// , 'taos_stmt_bind_param': [ref.types.int, [ref.types.void_ptr, ref.refType(TAOS_BIND)]]
, 'taos_stmt_bind_param': [ref.types.int, [ref.types.void_ptr, ref.types.void_ptr]]
// int taos_stmt_bind_single_param_batch(TAOS_STMT* stmt, TAOS_MULTI_BIND* bind, int colIdx)
, 'taos_stmt_bind_single_param_batch': [ref.types.int, [ref.types.void_ptr, ref.types.void_ptr, ref.types.int]]
// int taos_stmt_bind_param_batch(TAOS_STMT* stmt, TAOS_MULTI_BIND* bind)
, 'taos_stmt_bind_param_batch': [ref.types.int, [ref.types.void_ptr, ref.types.void_ptr]]
// int taos_stmt_add_batch(TAOS_STMT *stmt)
, 'taos_stmt_add_batch': [ref.types.int, [ref.types.void_ptr]]
// int taos_stmt_execute(TAOS_STMT *stmt)
, 'taos_stmt_execute': [ref.types.int, [ref.types.void_ptr]]
// TAOS_RES* taos_stmt_use_result(TAOS_STMT *stmt)
, 'taos_stmt_use_result': [ref.types.void_ptr, [ref.types.void_ptr]]
// int taos_stmt_close(TAOS_STMT *stmt)
, 'taos_stmt_close': [ref.types.int, [ref.types.void_ptr]]
// char * taos_stmt_errstr(TAOS_STMT *stmt)
, 'taos_stmt_errstr': [ref.types.char_ptr, [ref.types.void_ptr]]
// int taos_load_table_info(TAOS *taos, const char* tableNameList)
, 'taos_load_table_info': [ref.types.int, [ref.types.void_ptr, ref.types.char_ptr]]
});
if (pass == false) {
if (config == null) {
this._config = ref.alloc(ref.types.char_ptr, ref.NULL);
}
else {
try {
this._config = ref.allocCString(config);
}
catch (err) {
throw "Attribute Error: config is expected as a str";
}
}
if (config != null) {
this.libtaos.taos_options(3, this._config);
}
this.libtaos.taos_init();
}
return this;
}
CTaosInterface.prototype.config = function config() {
return this._config;
}
CTaosInterface.prototype.connect = function connect(host = null, user = "root", password = "taosdata", db = null, port = 0) {
let _host, _user, _password, _db, _port;
try {
_host = host != null ? ref.allocCString(host) : ref.NULL;
}
catch (err) {
throw "Attribute Error: host is expected as a str";
}
try {
_user = ref.allocCString(user)
}
catch (err) {
throw "Attribute Error: user is expected as a str";
}
try {
_password = ref.allocCString(password);
}
catch (err) {
throw "Attribute Error: password is expected as a str";
}
try {
_db = db != null ? ref.allocCString(db) : ref.NULL;
}
catch (err) {
throw "Attribute Error: db is expected as a str";
}
try {
_port = ref.alloc(ref.types.int, port);
}
catch (err) {
throw TypeError("port is expected as an int")
}
let connection = this.libtaos.taos_connect(_host, _user, _password, _db, _port);
if (ref.isNull(connection)) {
throw new errors.TDError('Failed to connect to TDengine');
}
else {
console.log('Successfully connected to TDengine');
}
return connection;
}
CTaosInterface.prototype.close = function close(connection) {
this.libtaos.taos_close(connection);
console.log("Connection is closed");
}
CTaosInterface.prototype.query = function query(connection, sql) {
return this.libtaos.taos_query(connection, ref.allocCString(sql));
}
CTaosInterface.prototype.affectedRows = function affectedRows(result) {
return this.libtaos.taos_affected_rows(result);
}
CTaosInterface.prototype.useResult = function useResult(result) {
let fields = [];
let pfields = this.fetchFields(result);
if (ref.isNull(pfields) == false) {
pfields = ref.reinterpret(pfields, this.fieldsCount(result) * TAOSFIELD.STRUCT_SIZE, 0);
for (let i = 0; i < pfields.length; i += TAOSFIELD.STRUCT_SIZE) {
fields.push({
name: ref.readCString(ref.reinterpret(pfields, TAOSFIELD.NAME_LENGTH, i)),
type: pfields[i + TAOSFIELD.TYPE_OFFSET],
bytes: pfields[i + TAOSFIELD.BYTES_OFFSET] + pfields[i + TAOSFIELD.BYTES_OFFSET + 1] * 256
})
}
}
return fields;
}
CTaosInterface.prototype.fetchBlock = function fetchBlock(result, fields) {
let pblock = ref.NULL_POINTER;
let num_of_rows = this.libtaos.taos_fetch_block(result, pblock);
if (ref.isNull(pblock.deref()) == true) {
return { block: null, num_of_rows: 0 };
}
var fieldL = this.libtaos.taos_fetch_lengths(result);
let precision = this.libtaos.taos_result_precision(result);
var fieldlens = [];
if (ref.isNull(fieldL) == false) {
for (let i = 0; i < fields.length; i++) {
let plen = ref.reinterpret(fieldL, 4, i * 4);
let len = plen.readInt32LE(0);
fieldlens.push(len);
}
}
let blocks = new Array(fields.length);
blocks.fill(null);
num_of_rows = Math.abs(num_of_rows);
let offset = 0;
let ptr = pblock.deref();
for (let i = 0; i < fields.length; i++) {
pdata = ref.reinterpret(ptr, 8, i * 8);
if (ref.isNull(pdata.readPointer())) {
blocks[i] = new Array();
} else {
pdata = ref.ref(pdata.readPointer());
if (!convertFunctions[fields[i]['type']]) {
throw new errors.DatabaseError("Invalid data type returned from database");
}
blocks[i] = convertFunctions[fields[i]['type']](pdata, num_of_rows, fieldlens[i], offset, precision);
}
}
return { blocks: blocks, num_of_rows }
}
CTaosInterface.prototype.fetchRow = function fetchRow(result, fields) {
let row = this.libtaos.taos_fetch_row(result);
return row;
}
CTaosInterface.prototype.freeResult = function freeResult(result) {
this.libtaos.taos_free_result(result);
result = null;
}
/** Number of fields returned in this result handle, must use with async */
CTaosInterface.prototype.numFields = function numFields(result) {
return this.libtaos.taos_num_fields(result);
}
// Fetch fields count by connection, the latest query
CTaosInterface.prototype.fieldsCount = function fieldsCount(result) {
return this.libtaos.taos_field_count(result);
}
CTaosInterface.prototype.fetchFields = function fetchFields(result) {
return this.libtaos.taos_fetch_fields(result);
}
CTaosInterface.prototype.errno = function errno(result) {
return this.libtaos.taos_errno(result);
}
CTaosInterface.prototype.errStr = function errStr(result) {
return ref.readCString(this.libtaos.taos_errstr(result));
}
// Async
CTaosInterface.prototype.query_a = function query_a(connection, sql, callback, param = ref.ref(ref.NULL)) {
// void taos_query_a(TAOS *taos, char *sqlstr, void (*fp)(void *param, TAOS_RES *, int), void *param)
callback = ffi.Callback(ref.types.void, [ref.types.void_ptr, ref.types.void_ptr, ref.types.int], callback);
this.libtaos.taos_query_a(connection, ref.allocCString(sql), callback, param);
return param;
}
/** Asynchrnously fetches the next block of rows. Wraps callback and transfers a 4th argument to the cursor, the row data as blocks in javascript form
* Note: This isn't a recursive function, in order to fetch all data either use the TDengine cursor object, TaosQuery object, or implement a recrusive
* function yourself using the libtaos.taos_fetch_rows_a function
*/
CTaosInterface.prototype.fetch_rows_a = function fetch_rows_a(result, callback, param = ref.ref(ref.NULL)) {
// void taos_fetch_rows_a(TAOS_RES *res, void (*fp)(void *param, TAOS_RES *, int numOfRows), void *param);
var cti = this;
// wrap callback with a function so interface can access the numOfRows value, needed in order to properly process the binary data
let asyncCallbackWrapper = function (param2, result2, numOfRows2) {
// Data preparation to pass to cursor. Could be bottleneck in query execution callback times.
let row = cti.libtaos.taos_fetch_row(result2);
let fields = cti.fetchFields_a(result2);
let precision = cti.libtaos.taos_result_precision(result2);
let blocks = new Array(fields.length);
blocks.fill(null);
numOfRows2 = Math.abs(numOfRows2);
let offset = 0;
var fieldL = cti.libtaos.taos_fetch_lengths(result);
var fieldlens = [];
if (ref.isNull(fieldL) == false) {
for (let i = 0; i < fields.length; i++) {
let plen = ref.reinterpret(fieldL, 8, i * 8);
let len = ref.get(plen, 0, ref.types.int32);
fieldlens.push(len);
}
}
if (numOfRows2 > 0) {
for (let i = 0; i < fields.length; i++) {
if (ref.isNull(pdata.readPointer())) {
blocks[i] = new Array();
} else {
if (!convertFunctions[fields[i]['type']]) {
throw new errors.DatabaseError("Invalid data type returned from database");
}
let prow = ref.reinterpret(row, 8, i * 8);
prow = prow.readPointer();
prow = ref.ref(prow);
blocks[i] = convertFunctions[fields[i]['type']](prow, 1, fieldlens[i], offset, precision);
//offset += fields[i]['bytes'] * numOfRows2;
}
}
}
callback(param2, result2, numOfRows2, blocks);
}
asyncCallbackWrapper = ffi.Callback(ref.types.void, [ref.types.void_ptr, ref.types.void_ptr, ref.types.int], asyncCallbackWrapper);
this.libtaos.taos_fetch_rows_a(result, asyncCallbackWrapper, param);
return param;
}
// Fetch field meta data by result handle
CTaosInterface.prototype.fetchFields_a = function fetchFields_a(result) {
let pfields = this.fetchFields(result);
let pfieldscount = this.numFields(result);
let fields = [];
if (ref.isNull(pfields) == false) {
pfields = ref.reinterpret(pfields, pfieldscount * TAOSFIELD.STRUCT_SIZE, 0);
for (let i = 0; i < pfields.length; i += TAOSFIELD.STRUCT_SIZE) {
fields.push({
name: ref.readCString(ref.reinterpret(pfields, TAOSFIELD.NAME_LENGTH, i)),
type: pfields[i + TAOSFIELD.TYPE_OFFSET],
bytes: pfields[i + TAOSFIELD.BYTES_OFFSET] + pfields[i + TAOSFIELD.BYTES_OFFSET + 1] * 256
})
}
}
return fields;
}
// Stop a query by result handle
CTaosInterface.prototype.stopQuery = function stopQuery(result) {
if (result != null) {
this.libtaos.taos_stop_query(result);
}
else {
throw new errors.ProgrammingError("No result handle passed to stop query");
}
}
CTaosInterface.prototype.getServerInfo = function getServerInfo(connection) {
return ref.readCString(this.libtaos.taos_get_server_info(connection));
}
CTaosInterface.prototype.getClientInfo = function getClientInfo() {
return ref.readCString(this.libtaos.taos_get_client_info());
}
// Subscription
CTaosInterface.prototype.subscribe = function subscribe(connection, restart, topic, sql, interval) {
let topicOrig = topic;
let sqlOrig = sql;
try {
sql = sql != null ? ref.allocCString(sql) : ref.alloc(ref.types.char_ptr, ref.NULL);
}
catch (err) {
throw "Attribute Error: sql is expected as a str";
}
try {
topic = topic != null ? ref.allocCString(topic) : ref.alloc(ref.types.char_ptr, ref.NULL);
}
catch (err) {
throw TypeError("topic is expected as a str");
}
restart = ref.alloc(ref.types.int, restart);
let subscription = this.libtaos.taos_subscribe(connection, restart, topic, sql, null, null, interval);
if (ref.isNull(subscription)) {
throw new errors.TDError('Failed to subscribe to TDengine | Database: ' + dbOrig + ', Table: ' + tableOrig);
}
else {
console.log('Successfully subscribed to TDengine - Topic: ' + topicOrig);
}
return subscription;
}
CTaosInterface.prototype.consume = function consume(subscription) {
let result = this.libtaos.taos_consume(subscription);
let fields = [];
let pfields = this.fetchFields(result);
if (ref.isNull(pfields) == false) {
pfields = ref.reinterpret(pfields, this.numFields(result) * TAOSFIELD.STRUCT_SIZE, 0);
for (let i = 0; i < pfields.length; i += TAOSFIELD.STRUCT_SIZE) {
fields.push({
name: ref.readCString(ref.reinterpret(pfields, TAOSFIELD.NAME_LENGTH, i)),
bytes: pfields[TAOSFIELD.TYPE_OFFSET],
type: pfields[i + TAOSFIELD.BYTES_OFFSET] + pfields[i + TAOSFIELD.BYTES_OFFSET + 1] * 256
})
}
}
let data = [];
while (true) {
let { blocks, num_of_rows } = this.fetchBlock(result, fields);
if (num_of_rows == 0) {
break;
}
for (let i = 0; i < num_of_rows; i++) {
data.push([]);
let rowBlock = new Array(fields.length);
for (let j = 0; j < fields.length; j++) {
rowBlock[j] = blocks[j][i];
}
data[data.length - 1] = (rowBlock);
}
}
return { data: data, fields: fields, result: result };
}
CTaosInterface.prototype.unsubscribe = function unsubscribe(subscription) {
//void taos_unsubscribe(TAOS_SUB *tsub);
this.libtaos.taos_unsubscribe(subscription);
}
//Schemaless insert API
/**
* TAOS* taos, char* lines[], int numLines, int protocol,int precision)
* using taos_errstr get error info, taos_errno get error code. Remember
* to release taos_res, otherwise will lead memory leak.
* TAOS schemaless insert api
* @param {*} connection a valid database connection
* @param {*} lines string data, which satisfied with line protocol
* @param {*} numLines number of rows in param lines.
* @param {*} protocol Line protocol, enum type (0,1,2,3),indicate different line protocol
* @param {*} precision timestamp precision in lines, enum type (0,1,2,3,4,5,6)
* @returns TAOS_RES
*
*/
CTaosInterface.prototype.schemalessInsert = function schemalessInsert(connection, lines, protocal, precision) {
let _numLines = null;
let _lines = null;
if (_.isString(lines)) {
_numLines = 1;
_lines = Buffer.alloc(_numLines * ref.sizeof.pointer);
ref.set(_lines, 0, ref.allocCString(lines), ref.types.char_ptr);
}
else if (_.isArray(lines)) {
_numLines = lines.length;
_lines = Buffer.alloc(_numLines * ref.sizeof.pointer);
for (let i = 0; i < _numLines; i++) {
ref.set(_lines, i * ref.sizeof.pointer, ref.allocCString(lines[i]), ref.types.char_ptr)
}
}
else {
throw new errors.InterfaceError("Unsupport lines input")
}
return this.libtaos.taos_schemaless_insert(connection, _lines, _numLines, protocal, precision);
}
//stmt APIs
/**
* init a TAOS_STMT object for later use.it should be freed with stmtClose.
* @param {*} connection valid taos connection
* @returns Not NULL returned for success, and NULL for failure.
*
*/
CTaosInterface.prototype.stmtInit = function stmtInit(connection) {
return this.libtaos.taos_stmt_init(connection)
}
/**
* prepare a sql statement,'sql' should be a valid INSERT/SELECT statement, 'length' is not used.
* @param {*} stmt
* @param {string} sql a valid INSERT/SELECT statement
* @param {ulong} length not used
* @returns 0 for success, non-zero for failure.
*/
CTaosInterface.prototype.stmtPrepare = function stmtPrepare(stmt, sql, length) {
return this.libtaos.taos_stmt_prepare(stmt, ref.allocCString(sql), 0);
}
/**
* For INSERT only. Used to bind table name as a parmeter for the input stmt object.
* @param {*} stmt could be the value returned by 'stmtInit',
* that may be a valid object or NULL.
* @param {TaosBind} tableName target table name you want to bind
* @returns 0 for success, non-zero for failure.
*/
CTaosInterface.prototype.stmtSetTbname = function stmtSetTbname(stmt, tableName) {
return this.libtaos.taos_stmt_set_tbname(stmt, ref.allocCString(tableName));
}
/**
* For INSERT only.
* Set a table name for binding table name as parameter and tag values for all tag parameters.
* @param {*} stmt could be the value returned by 'stmtInit', that may be a valid object or NULL.
* @param {*} tableName use to set target table name
* @param {TaosMultiBind} tags use to set tag value for target table.
* @returns
*/
CTaosInterface.prototype.stmtSetTbnameTags = function stmtSetTbnameTags(stmt, tableName, tags) {
return this.libtaos.taos_stmt_set_tbname_tags(stmt, ref.allocCString(tableName), tags);
}
/**
* For INSERT only.
* Set a table name for binding table name as parameter. Only used for binding all tables
* in one stable, user application must call 'loadTableInfo' API to load all table
* meta before calling this API. If the table meta is not cached locally, it will return error.
* @param {*} stmt could be the value returned by 'StmtInit', that may be a valid object or NULL.
* @param {*} subTableName table name which is belong to an stable
* @returns 0 for success, non-zero for failure.
*/
CTaosInterface.prototype.stmtSetSubTbname = function stmtSetSubTbname(stmt, subTableName) {
return this.libtaos.taos_stmt_set_sub_tbname(stmt, subTableName);
}
/**
* bind a whole line data, for both INSERT and SELECT. The parameter 'bind' points to an array
* contains the whole line data. Each item in array represents a column's value, the item
* number and sequence should keep consistence with columns in sql statement. The usage of
* structure TAOS_BIND is the same with MYSQL_BIND in MySQL.
* @param {*} stmt could be the value returned by 'stmtInit', that may be a valid object or NULL.
* @param {*} binds points to an array contains the whole line data.
* @returns 0 for success, non-zero for failure.
*/
CTaosInterface.prototype.bindParam = function bindParam(stmt, binds) {
return this.libtaos.taos_stmt_bind_param(stmt, binds);
}
/**
* Bind a single column's data, INTERNAL used and for INSERT only.
* @param {*} stmt could be the value returned by 'stmtInit', that may be a valid object or NULL.
* @param {TaosMultiBind} mbind points to a column's data which could be the one or more lines.
* @param {*} colIndex the column's index in prepared sql statement, it starts from 0.
* @returns 0 for success, non-zero for failure.
*/
CTaosInterface.prototype.stmtBindSingleParamBatch = function stmtBindSingleParamBatch(stmt, mbind, colIndex) {
return this.libtaos.taos_stmt_bind_single_param_batch(stmt, mbind.ref(), colIndex);
}
/**
* For INSERT only.
* Bind one or multiple lines data.
* @param {*} stmt could be the value returned by 'stmtInit',
* that may be a valid object or NULL.
* @param {*} mbinds Points to an array contains one or more lines data.The item
* number and sequence should keep consistence with columns
* n sql statement.
* @returns 0 for success, non-zero for failure.
*/
CTaosInterface.prototype.stmtBindParamBatch = function stmtBindParamBatch(stmt, mbinds) {
return this.libtaos.taos_stmt_bind_param_batch(stmt, mbinds);
}
/**
* add all current bound parameters to batch process, for INSERT only.
* Must be called after each call to bindParam/bindSingleParamBatch,
* or all columns binds for one or more lines with bindSingleParamBatch. User
* application can call any bind parameter API again to bind more data lines after calling
* to this API.
* @param {*} stmt
* @returns 0 for success, non-zero for failure.
*/
CTaosInterface.prototype.addBatch = function addBatch(stmt) {
return this.libtaos.taos_stmt_add_batch(stmt);
}
/**
* actually execute the INSERT/SELECT sql statement. User application can continue
* to bind new data after calling to this API.
* @param {*} stmt
* @returns 0 for success, non-zero for failure.
*/
CTaosInterface.prototype.stmtExecute = function stmtExecute(stmt) {
return this.libtaos.taos_stmt_execute(stmt);
}
/**
* For SELECT only,getting the query result.
* User application should free it with API 'FreeResult' at the end.
* @param {*} stmt could be the value returned by 'stmtInit', that may be a valid object or NULL.
* @returns Not NULL for success, NULL for failure.
*/
CTaosInterface.prototype.stmtUseResult = function stmtUseResult(stmt) {
return this.libtaos.taos_stmt_use_result(stmt);
}
/**
* user application call this API to load all tables meta info.
* This method must be called before stmtSetSubTbname(IntPtr stmt, string name);
* @param {*} taos taos connection
* @param {*} tableList tables need to load meta info are form in an array
* @returns 0 for success, non-zero for failure.
*/
CTaosInterface.prototype.loadTableInfo = function loadTableInfo(taos, tableList) {
let _tableListBuf = Buffer.alloc(ref.sizeof.pointer);
let _listStr = tableList.toString();
if ((_.isString(tableList) )|| (_.isArray(tableList))) {
ref.set(_tableListBuf, 0, ref.allocCString(_listStr), ref.types.char_ptr);
return this.libtaos.taos_load_table_info(taos, _tableListBuf);
} else {
throw new errors.InterfaceError("Unspport tableLis input");
}
}
/**
* Close STMT object and free resources.
* @param {*} stmt could be the value returned by 'stmtInit', that may be a valid object or NULL.
* @returns 0 for success, non-zero for failure.
*/
CTaosInterface.prototype.closeStmt = function closeStmt(stmt) {
return this.libtaos.taos_stmt_close(stmt);
}
/**
* Get detail error message when got failure for any stmt API call.
* If not failure, the result returned by this API is unknown.
* @param {*} stmt Could be the value returned by 'stmtInit', that may be a valid object or NULL.
* @returns error string
*/
CTaosInterface.prototype.stmtErrStr = function stmtErrStr(stmt) {
return ref.readCString(this.libtaos.taos_stmt_errstr(stmt));
}
\ No newline at end of file
const TDengineCursor = require('./cursor')
const CTaosInterface = require('./cinterface')
module.exports = TDengineConnection;
/**
* TDengine Connection Class
* @param {object} options - Options for configuring the connection with TDengine
* @return {TDengineConnection}
* @class TDengineConnection
* @constructor
* @example
* //Initialize a new connection
* var conn = new TDengineConnection({host:"127.0.0.1", user:"root", password:"taosdata", config:"/etc/taos",port:0})
*
*/
function TDengineConnection(options) {
this._conn = null;
this._host = null;
this._user = "root"; //The default user
this._password = "taosdata"; //The default password
this._database = null;
this._port = 0;
this._config = null;
this._chandle = null;
this._configConn(options)
return this;
}
/**
* Configure the connection to TDengine
* @private
* @memberof TDengineConnection
*/
TDengineConnection.prototype._configConn = function _configConn(options) {
if (options['host']) {
this._host = options['host'];
}
if (options['user']) {
this._user = options['user'];
}
if (options['password']) {
this._password = options['password'];
}
if (options['database']) {
this._database = options['database'];
}
if (options['port']) {
this._port = options['port'];
}
if (options['config']) {
this._config = options['config'];
}
this._chandle = new CTaosInterface(this._config);
this._conn = this._chandle.connect(this._host, this._user, this._password, this._database, this._port);
}
/** Close the connection to TDengine */
TDengineConnection.prototype.close = function close() {
this._chandle.close(this._conn);
}
/**
* Initialize a new cursor to interact with TDengine with
* @return {TDengineCursor}
*/
TDengineConnection.prototype.cursor = function cursor() {
//Pass the connection object to the cursor
return new TDengineCursor(this);
}
TDengineConnection.prototype.commit = function commit() {
return this;
}
TDengineConnection.prototype.rollback = function rollback() {
return this;
}
/**
* Clear the results from connector
* @private
*/
/*
TDengineConnection.prototype._clearResultSet = function _clearResultSet() {
var result = this._chandle.useResult(this._conn).result;
if (result) {
this._chandle.freeResult(result)
}
}
*/
const SCHEMALESS_PROTOCOL = {
TSDB_SML_UNKNOWN_PROTOCOL: 0,
TSDB_SML_LINE_PROTOCOL: 1,
TSDB_SML_TELNET_PROTOCOL: 2,
TSDB_SML_JSON_PROTOCOL: 3
}
const SCHEMALESS_PRECISION = {
TSDB_SML_TIMESTAMP_NOT_CONFIGURED: 0,
TSDB_SML_TIMESTAMP_HOURS: 1,
TSDB_SML_TIMESTAMP_MINUTES: 2,
TSDB_SML_TIMESTAMP_SECONDS: 3,
TSDB_SML_TIMESTAMP_MILLI_SECONDS: 4,
TSDB_SML_TIMESTAMP_MICRO_SECONDS: 5,
TSDB_SML_TIMESTAMP_NANO_SECONDS: 6
}
const typeCodesToName = {
0: 'Null',
1: 'Boolean',
2: 'Tiny Int',
3: 'Small Int',
4: 'Int',
5: 'Big Int',
6: 'Float',
7: 'Double',
8: 'Binary',
9: 'Timestamp',
10: 'Nchar',
11: 'Tinyint Unsigned',
12: 'Smallint Unsigned',
13: 'Int Unsigned',
14: 'Bigint Unsigned',
15: 'Json',
}
/**
* @function
* @param {number} typecode - The code to get the name of the type for
* @return {string} Name of the field type
*/
function getType(typecode) {
return typeCodesToName[typecode];
}
/**
* Contains the the definitions/values assigned to various field types
* @module FieldTypes
*/
/**
* TDengine Field Types and their type codes
* @typedef {Object} FieldTypes
* @global
* @property {number} C_NULL - Null
* @property {number} C_BOOL - Boolean. Note, 0x02 is the C_BOOL_NULL value.
* @property {number} C_TINYINT - Tiny Int, values in the range [-2^7+1, 2^7-1]. Note, -2^7 has been used as the C_TINYINT_NULL value
* @property {number} C_SMALLINT - Small Int, values in the range [-2^15+1, 2^15-1]. Note, -2^15 has been used as the C_SMALLINT_NULL value
* @property {number} C_INT - Int, values in the range [-2^31+1, 2^31-1]. Note, -2^31 has been used as the C_INT_NULL value
* @property {number} C_BIGINT - Big Int, values in the range [-2^59, 2^59].
* @property {number} C_FLOAT - Float, values in the range [-3.4E38, 3.4E38], accurate up to 6-7 decimal places.
* @property {number} C_DOUBLE - Double, values in the range [-1.7E308, 1.7E308], accurate up to 15-16 decimal places.
* @property {number} C_BINARY - Binary, encoded in utf-8.
* @property {number} C_TIMESTAMP - Timestamp in format "YYYY:MM:DD HH:MM:SS.MMM". Measured in number of milliseconds passed after
1970-01-01 08:00:00.000 GMT.
* @property {number} C_NCHAR - NChar field type encoded in ASCII, a wide string.
*
* @property {number} C_TIMESTAMP_MILLI - The code for millisecond timestamps, as returned by libtaos.taos_result_precision(result).
* @property {number} C_TIMESTAMP_MICRO - The code for microsecond timestamps, as returned by libtaos.taos_result_precision(result).
*/
module.exports = {
C_NULL: 0,
C_BOOL: 1,
C_TINYINT: 2,
C_SMALLINT: 3,
C_INT: 4,
C_BIGINT: 5,
C_FLOAT: 6,
C_DOUBLE: 7,
C_BINARY: 8,
C_TIMESTAMP: 9,
C_NCHAR: 10,
C_TINYINT_UNSIGNED: 11,
C_SMALLINT_UNSIGNED: 12,
C_INT_UNSIGNED: 13,
C_BIGINT_UNSIGNED: 14,
C_JSON_TAG: 15,
// NULL value definition
// NOTE: These values should change according to C definition in tsdb.h
C_BOOL_NULL: 2,
C_TINYINT_NULL: -128,
C_TINYINT_UNSIGNED_NULL: 255,
C_SMALLINT_NULL: -32768,
C_SMALLINT_UNSIGNED_NULL: 65535,
C_INT_NULL: -2147483648,
C_INT_UNSIGNED_NULL: 4294967295,
C_BIGINT_NULL: -9223372036854775808n,
C_BIGINT_UNSIGNED_NULL: 18446744073709551615n,
C_FLOAT_NULL: 2146435072,
C_DOUBLE_NULL: -9223370937343148032,
C_NCHAR_NULL: 4294967295,
C_BINARY_NULL: 255,
C_TIMESTAMP_MILLI: 0,
C_TIMESTAMP_MICRO: 1,
getType,
SCHEMALESS_PROTOCOL,
SCHEMALESS_PRECISION
}
const ref = require('ref-napi');
require('./globalfunc.js')
const CTaosInterface = require('./cinterface')
const errors = require('./error')
const TaosQuery = require('./taosquery')
const { PerformanceObserver, performance } = require('perf_hooks');
module.exports = TDengineCursor;
/**
* @typedef {Object} Buffer - A Node.js buffer. Please refer to {@link https://nodejs.org/api/buffer.html} for more details
* @global
*/
/**
* @class TDengineCursor
* @classdesc The TDengine Cursor works directly with the C Interface which works with TDengine. It refrains from
* returning parsed data and majority of functions return the raw data such as cursor.fetchall() as compared to the TaosQuery class which
* has functions that "prettify" the data and add more functionality and can be used through cursor.query("your query"). Instead of
* promises, the class and its functions use callbacks.
* @param {TDengineConnection} - The TDengine Connection this cursor uses to interact with TDengine
* @property {data} - Latest retrieved data from query execution. It is an empty array by default
* @property {fields} - Array of the field objects in order from left to right of the latest data retrieved
* @since 1.0.0
*/
function TDengineCursor(connection = null) {
//All parameters are store for sync queries only.
this._rowcount = -1;
this._connection = null;
this._result = null;
this._fields = null;
this.data = [];
this.fields = null;
this._stmt = null;
if (connection != null) {
this._connection = connection
this._chandle = connection._chandle //pass through, just need library loaded.
}
else {
throw new errors.ProgrammingError("A TDengineConnection object is required to be passed to the TDengineCursor");
}
}
/**
* Get the row counts of the latest query
* @since 1.0.0
* @return {number} Rowcount
*/
TDengineCursor.prototype.rowcount = function rowcount() {
return this._rowcount;
}
/**
* Close the cursor by setting its connection to null and freeing results from the connection and resetting the results it has stored
* @return {boolean} Whether or not the cursor was succesfully closed
* @since 1.0.0
*/
TDengineCursor.prototype.close = function close() {
if (this._connection == null) {
return false;
}
// this._connection._clearResultSet();
this._reset_result();
this._connection = null;
return true;
}
/**
* Create a TaosQuery object to perform a query to TDengine and retrieve data.
* @param {string} operation - The operation string to perform a query on
* @param {boolean} execute - Whether or not to immedietely perform the query. Default is false.
* @return {TaosQuery | Promise<TaosResult>} A TaosQuery object
* @example
* var query = cursor.query("select count(*) from meterinfo.meters");
* query.execute();
* @since 1.0.6
*/
TDengineCursor.prototype.query = function query(operation, execute = false) {
return new TaosQuery(operation, this, execute);
}
/**
* Execute a query. Also stores all the field meta data returned from the query into cursor.fields. It is preferable to use cursor.query() to create
* queries and execute them instead of using the cursor object directly.
* @param {string} operation - The query operation to execute in the taos shell
* @param {Object} options - Execution options object. quiet : true turns off logging from queries
* @param {boolean} options.quiet - True if you want to surpress logging such as "Query OK, 1 row(s) ..."
* @param {function} callback - A callback function to execute after the query is made to TDengine
* @return {number | Buffer} Number of affected rows or a Buffer that points to the results of the query
* @since 1.0.0
*/
TDengineCursor.prototype.execute = function execute(operation, options, callback) {
if (operation == undefined) {
throw new errors.ProgrammingError('No operation passed as argument');
return null;
}
if (typeof options == 'function') {
callback = options;
}
if (typeof options != 'object') options = {}
if (this._connection == null) {
throw new errors.ProgrammingError('Cursor is not connected');
}
this._reset_result();
let stmt = operation;
let time = 0;
let res;
if (options['quiet'] != true) {
const obs = new PerformanceObserver((items) => {
time = items.getEntries()[0].duration;
performance.clearMarks();
});
obs.observe({ entryTypes: ['measure'] });
performance.mark('A');
this._result = this._chandle.query(this._connection._conn, stmt);
performance.mark('B');
performance.measure('query', 'A', 'B');
}
else {
this._result = this._chandle.query(this._connection._conn, stmt);
}
res = this._chandle.errno(this._result);
if (res == 0) {
let fieldCount = this._chandle.fieldsCount(this._result);
if (fieldCount == 0) {
let affectedRowCount = this._chandle.affectedRows(this._result);
let response = this._createAffectedResponse(affectedRowCount, time)
if (options['quiet'] != true) {
console.log(response);
}
wrapCB(callback);
return affectedRowCount; //return num of affected rows, common with insert, use statements
}
else {
this._fields = this._chandle.useResult(this._result);
this.fields = this._fields;
wrapCB(callback);
return this._result; //return a pointer to the result
}
}
else {
throw new errors.ProgrammingError(this._chandle.errStr(this._result))
}
}
TDengineCursor.prototype._createAffectedResponse = function (num, time) {
return "Query OK, " + num + " row(s) affected (" + (time * 0.001).toFixed(8) + "s)";
}
TDengineCursor.prototype._createSetResponse = function (num, time) {
return "Query OK, " + num + " row(s) in set (" + (time * 0.001).toFixed(8) + "s)";
}
TDengineCursor.prototype.executemany = function executemany() {
}
TDengineCursor.prototype.fetchone = function fetchone() {
}
TDengineCursor.prototype.fetchmany = function fetchmany() {
}
/**
* Fetches all results from a query and also stores results into cursor.data. It is preferable to use cursor.query() to create
* queries and execute them instead of using the cursor object directly.
* @param {function} callback - callback function executing on the complete fetched data
* @return {Array<Array>} The resultant array, with entries corresponding to each retreived row from the query results, sorted in
* order by the field name ordering in the table.
* @since 1.0.0
* @example
* cursor.execute('select * from db.table');
* var data = cursor.fetchall(function(results) {
* results.forEach(row => console.log(row));
* })
*/
TDengineCursor.prototype.fetchall = function fetchall(options, callback) {
if (this._result == null || this._fields == null) {
throw new errors.OperationalError("Invalid use of fetchall, either result or fields from query are null. First execute a query first");
}
let num_of_rows = this._chandle.affectedRows(this._result);
let data = new Array(num_of_rows);
this._rowcount = 0;
let time = 0;
const obs = new PerformanceObserver((items) => {
time += items.getEntries()[0].duration;
performance.clearMarks();
});
obs.observe({ entryTypes: ['measure'] });
performance.mark('A');
while (true) {
let blockAndRows = this._chandle.fetchBlock(this._result, this._fields);
// console.log(blockAndRows);
// break;
let block = blockAndRows.blocks;
let num_of_rows = blockAndRows.num_of_rows;
if (num_of_rows == 0) {
break;
}
this._rowcount += num_of_rows;
let numoffields = this._fields.length;
for (let i = 0; i < num_of_rows; i++) {
// data.push([]);
let rowBlock = new Array(numoffields);
for (let j = 0; j < numoffields; j++) {
rowBlock[j] = block[j][i];
}
data[this._rowcount - num_of_rows + i] = (rowBlock);
// data.push(rowBlock);
}
}
performance.mark('B');
performance.measure('query', 'A', 'B');
let response = this._createSetResponse(this._rowcount, time)
console.log(response);
// this._connection._clearResultSet();
let fields = this.fields;
this._reset_result();
this.data = data;
this.fields = fields;
wrapCB(callback, data);
return data;
}
/**
* Asynchrnously execute a query to TDengine. NOTE, insertion requests must be done in sync if on the same table.
* @param {string} operation - The query operation to execute in the taos shell
* @param {Object} options - Execution options object. quiet : true turns off logging from queries
* @param {boolean} options.quiet - True if you want to surpress logging such as "Query OK, 1 row(s) ..."
* @param {function} callback - A callback function to execute after the query is made to TDengine
* @return {number | Buffer} Number of affected rows or a Buffer that points to the results of the query
* @since 1.0.0
*/
TDengineCursor.prototype.execute_a = function execute_a(operation, options, callback, param) {
if (operation == undefined) {
throw new errors.ProgrammingError('No operation passed as argument');
return null;
}
if (typeof options == 'function') {
//we expect the parameter after callback to be param
param = callback;
callback = options;
}
if (typeof options != 'object') options = {}
if (this._connection == null) {
throw new errors.ProgrammingError('Cursor is not connected');
}
if (typeof callback != 'function') {
throw new errors.ProgrammingError("No callback function passed to execute_a function");
}
// Async wrapper for callback;
var cr = this;
let asyncCallbackWrapper = function (param2, res2, resCode) {
if (typeof callback == 'function') {
callback(param2, res2, resCode);
}
if (resCode >= 0) {
// let fieldCount = cr._chandle.numFields(res2);
// if (fieldCount == 0) {
// //cr._chandle.freeResult(res2);
// return res2;
// }
// else {
// return res2;
// }
return res2;
}
else {
throw new errors.ProgrammingError("Error occuring with use of execute_a async function. Status code was returned with failure");
}
}
let stmt = operation;
let time = 0;
// Use ref module to write to buffer in cursor.js instead of taosquery to maintain a difference in levels. Have taosquery stay high level
// through letting it pass an object as param
var buf = ref.alloc('Object');
ref.writeObject(buf, 0, param);
const obs = new PerformanceObserver((items) => {
time = items.getEntries()[0].duration;
performance.clearMarks();
});
obs.observe({ entryTypes: ['measure'] });
performance.mark('A');
this._chandle.query_a(this._connection._conn, stmt, asyncCallbackWrapper, buf);
performance.mark('B');
performance.measure('query', 'A', 'B');
return param;
}
/**
* Fetches all results from an async query. It is preferable to use cursor.query_a() to create
* async queries and execute them instead of using the cursor object directly.
* @param {Object} options - An options object containing options for this function
* @param {function} callback - callback function that is callbacked on the COMPLETE fetched data (it is calledback only once!).
* Must be of form function (param, result, rowCount, rowData)
* @param {Object} param - A parameter that is also passed to the main callback function. Important! Param must be an object, and the key "data" cannot be used
* @return {{param:Object, result:Buffer}} An object with the passed parameters object and the buffer instance that is a pointer to the result handle.
* @since 1.2.0
* @example
* cursor.execute('select * from db.table');
* var data = cursor.fetchall(function(results) {
* results.forEach(row => console.log(row));
* })
*/
TDengineCursor.prototype.fetchall_a = function fetchall_a(result, options, callback, param = {}) {
if (typeof options == 'function') {
//we expect the parameter after callback to be param
param = callback;
callback = options;
}
if (typeof options != 'object') options = {}
if (this._connection == null) {
throw new errors.ProgrammingError('Cursor is not connected');
}
if (typeof callback != 'function') {
throw new errors.ProgrammingError('No callback function passed to fetchall_a function')
}
if (param.data) {
throw new errors.ProgrammingError("You aren't allowed to set the key 'data' for the parameters object");
}
let buf = ref.alloc('Object');
param.data = [];
var cr = this;
// This callback wrapper accumulates the data from the fetch_rows_a function from the cinterface. It is accumulated by passing the param2
// object which holds accumulated data in the data key.
let asyncCallbackWrapper = function asyncCallbackWrapper(param2, result2, numOfRows2, rowData) {
param2 = ref.readObject(param2); //return the object back from the pointer
if (numOfRows2 > 0 && rowData.length != 0) {
// Keep fetching until now rows left.
let buf2 = ref.alloc('Object');
param2.data.push(rowData);
ref.writeObject(buf2, 0, param2);
cr._chandle.fetch_rows_a(result2, asyncCallbackWrapper, buf2);
}
else {
let finalData = param2.data;
let fields = cr._chandle.fetchFields_a(result2);
let data = [];
for (let i = 0; i < finalData.length; i++) {
let num_of_rows = finalData[i][0].length; //fetched block number i;
let block = finalData[i];
for (let j = 0; j < num_of_rows; j++) {
data.push([]);
let rowBlock = new Array(fields.length);
for (let k = 0; k < fields.length; k++) {
rowBlock[k] = block[k][j];
}
data[data.length - 1] = rowBlock;
}
}
cr._chandle.freeResult(result2); // free result, avoid seg faults and mem leaks!
callback(param2, result2, numOfRows2, { data: data, fields: fields });
}
}
ref.writeObject(buf, 0, param);
param = this._chandle.fetch_rows_a(result, asyncCallbackWrapper, buf); //returned param
return { param: param, result: result };
}
/**
* Stop a query given the result handle.
* @param {Buffer} result - The buffer that acts as the result handle
* @since 1.3.0
*/
TDengineCursor.prototype.stopQuery = function stopQuery(result) {
this._chandle.stopQuery(result);
}
TDengineCursor.prototype._reset_result = function _reset_result() {
this._rowcount = -1;
if (this._result != null) {
this._chandle.freeResult(this._result);
}
this._result = null;
this._fields = null;
this.data = [];
this.fields = null;
}
/**
* Get server info such as version number
* @return {string}
* @since 1.3.0
*/
TDengineCursor.prototype.getServerInfo = function getServerInfo() {
return this._chandle.getServerInfo(this._connection._conn);
}
/**
* Get client info such as version number
* @return {string}
* @since 1.3.0
*/
TDengineCursor.prototype.getClientInfo = function getClientInfo() {
return this._chandle.getClientInfo();
}
/**
* Subscribe to a table from a database in TDengine.
* @param {Object} config - A configuration object containing the configuration options for the subscription
* @param {string} config.restart - whether or not to continue a subscription if it already exits, otherwise start from beginning
* @param {string} config.topic - The unique identifier of a subscription
* @param {string} config.sql - A sql statement for data query
* @param {string} config.interval - The pulling interval
* @return {Buffer} A buffer pointing to the subscription session handle
* @since 1.3.0
*/
TDengineCursor.prototype.subscribe = function subscribe(config) {
let restart = config.restart ? 1 : 0;
return this._chandle.subscribe(this._connection._conn, restart, config.topic, config.sql, config.interval);
};
/**
* An infinite loop that consumes the latest data and calls a callback function that is provided.
* @param {Buffer} subscription - A buffer object pointing to the subscription session handle
* @param {function} callback - The callback function that takes the row data, field/column meta data, and the subscription session handle as input
* @since 1.3.0
*/
TDengineCursor.prototype.consumeData = async function consumeData(subscription, callback) {
while (true) {
let { data, fields, result } = this._chandle.consume(subscription);
callback(data, fields, result);
}
}
/**
* Unsubscribe the provided buffer object pointing to the subscription session handle
* @param {Buffer} subscription - A buffer object pointing to the subscription session handle that is to be unsubscribed
* @since 1.3.0
*/
TDengineCursor.prototype.unsubscribe = function unsubscribe(subscription) {
this._chandle.unsubscribe(subscription);
}
/**
* schemaless insert
* @param {*} connection a valid database connection
* @param {*} lines string data, which statisfied with line proctocol
* @param {*} protocal Line protocol, enum type (0,1,2,3),indicate different line protocol
* @param {*} precision timestamp precision in lines, enum type (0,1,2,3,4,5,6)
* @returns TAOS_RES
*
*/
TDengineCursor.prototype.schemalessInsert = function schemalessInsert(lines, protocol, precision) {
this._result = this._chandle.schemalessInsert(this._connection._conn, lines, protocol, precision);
let errorNo = this._chandle.errno(this._result);
if (errorNo != 0) {
throw new errors.InterfaceError(errorNo + ":" + this._chandle.errStr(this._result));
}
this._chandle.freeResult(this._result);
}
//STMT
/**
* init a TAOS_STMT object for later use.it should be freed with stmtClose.
* @returns Not NULL returned for success, and NULL for failure.
*
*/
TDengineCursor.prototype.stmtInit = function stmtInit() {
let stmt = null
stmt = this._chandle.stmtInit(this._connection._conn);
if (stmt == null || stmt == undefined) {
throw new errors.DatabaseError(this._chandle.stmtErrStr(stmt));
} else {
this._stmt = stmt;
}
}
/**
* prepare a sql statement,'sql' should be a valid INSERT/SELECT statement
* @param {string} sql a valid INSERT/SELECT statement
* @returns {int} 0 for success, non-zero for failure.
*/
TDengineCursor.prototype.stmtPrepare = function stmtPrepare(sql) {
if (this._stmt == null) {
throw new errors.DatabaseError("stmt is null,init stmt first");
} else {
let stmtPrepare = this._chandle.stmtPrepare(this._stmt, sql, null);
if (stmtPrepare != 0) {
throw new errors.DatabaseError(this._chandle.stmtErrStr(this._stmt));
} else {
console.log("stmtPrepare success.");
}
}
}
/**
* For INSERT only. Used to bind table name as a parmeter for the input stmt object.
* @param {TaosBind} tableName target table name you want to bind
* @returns 0 for success, non-zero for failure.
*/
TDengineCursor.prototype.stmtSetTbname = function stmtSetTbname(tableName) {
if (this._stmt == null) {
throw new errors.DatabaseError("stmt is null,init stmt first");
} else {
let stmtPrepare = this._chandle.stmtSetTbname(this._stmt, tableName);
if (stmtPrepare != 0) {
throw new errors.DatabaseError(this._chandle.stmtErrStr(this._stmt));
} else {
console.log("stmtSetTbname success.");
}
}
}
/**
* For INSERT only.
* Set a table name for binding table name as parameter and tag values for all tag parameters.
* @param {*} tableName use to set target table name
* @param {TaosMultiBind} tags use to set tag value for target table.
* @returns
*/
TDengineCursor.prototype.stmtSetTbnameTags = function stmtSetTbnameTags(tableName, tags) {
if (this._stmt == null) {
throw new errors.DatabaseError("stmt is null,init stmt first");
} else {
let stmtPrepare = this._chandle.stmtSetTbnameTags(this._stmt, tableName, tags);
if (stmtPrepare != 0) {
throw new errors.DatabaseError(this._chandle.stmtErrStr(this._stmt));
} else {
console.log("stmtSetTbnameTags success.");
}
}
}
/**
* For INSERT only.
* Set a table name for binding table name as parameter. Only used for binding all tables
* in one stable, user application must call 'loadTableInfo' API to load all table
* meta before calling this API. If the table meta is not cached locally, it will return error.
* @param {*} subTableName table name which is belong to an stable
* @returns 0 for success, non-zero for failure.
*/
TDengineCursor.prototype.stmtSetSubTbname = function stmtSetSubTbname(subTableName) {
if (this._stmt == null) {
throw new errors.DatabaseError("stmt is null,init stmt first");
} else {
let stmtPrepare = this._chandle.stmtSetSubTbname(this._stmt, subTableName);
if (stmtPrepare != 0) {
throw new errors.DatabaseError(this._chandle.stmtErrStr(this._stmt));
} else {
console.log("stmtSetSubTbname success.");
}
}
}
/**
* bind a whole line data, for both INSERT and SELECT. The parameter 'bind' points to an array
* contains the whole line data. Each item in array represents a column's value, the item
* number and sequence should keep consistence with columns in sql statement. The usage of
* structure TAOS_BIND is the same with MYSQL_BIND in MySQL.
* @param {*} binds points to an array contains the whole line data.
* @returns 0 for success, non-zero for failure.
*/
TDengineCursor.prototype.stmtBindParam = function stmtBindParam(binds) {
if (this._stmt == null) {
throw new errors.DatabaseError("stmt is null,init stmt first");
} else {
let stmtPrepare = this._chandle.bindParam(this._stmt, binds);
if (stmtPrepare != 0) {
throw new errors.DatabaseError(this._chandle.stmtErrStr(this._stmt));
} else {
console.log("bindParam success.");
}
}
}
/**
* Bind a single column's data, INTERNAL used and for INSERT only.
* @param {TaosMultiBind} mbind points to a column's data which could be the one or more lines.
* @param {*} colIndex the column's index in prepared sql statement, it starts from 0.
* @returns 0 for success, non-zero for failure.
*/
TDengineCursor.prototype.stmtBindSingleParamBatch = function stmtBindSingleParamBatch(mbind, colIndex) {
if (this._stmt == null) {
throw new errors.DatabaseError("stmt is null,init stmt first");
} else {
let stmtPrepare = this._chandle.stmtBindSingleParamBatch(this._stmt, mbind, colIndex);
if (stmtPrepare != 0) {
throw new errors.DatabaseError(this._chandle.stmtErrStr(this._stmt));
} else {
console.log("stmtBindSingleParamBatch success.");
}
}
}
/**
* For INSERT only.
* Bind one or multiple lines data.
* @param {*} mbinds Points to an array contains one or more lines data.The item
* number and sequence should keep consistence with columns
* n sql statement.
* @returns 0 for success, non-zero for failure.
*/
TDengineCursor.prototype.stmtBindParamBatch = function stmtBindParamBatch(mbinds) {
if (this._stmt == null) {
throw new errors.DatabaseError("stmt is null,init stmt first");
} else {
let stmtPrepare = this._chandle.stmtBindParamBatch(this._stmt, mbinds);
if (stmtPrepare != 0) {
throw new errors.DatabaseError(this._chandle.stmtErrStr(this._stmt));
} else {
console.log("stmtBindParamBatch success.");
}
}
}
/**
* add all current bound parameters to batch process, for INSERT only.
* Must be called after each call to bindParam/bindSingleParamBatch,
* or all columns binds for one or more lines with bindSingleParamBatch. User
* application can call any bind parameter API again to bind more data lines after calling
* to this API.
* @param {*} stmt
* @returns 0 for success, non-zero for failure.
*/
TDengineCursor.prototype.stmtAddBatch = function stmtAddBatch() {
if (this._stmt == null) {
throw new errors.DatabaseError("stmt is null,init stmt first");
} else {
let addBatchRes = this._chandle.addBatch(this._stmt);
if (addBatchRes != 0) {
throw new errors.DatabaseError(this._chandle.stmtErrStr(this._stmt));
}
else {
console.log("addBatch success.");
}
}
}
/**
* actually execute the INSERT/SELECT sql statement. User application can continue
* to bind new data after calling to this API.
* @param {*} stmt
* @returns 0 for success, non-zero for failure.
*/
TDengineCursor.prototype.stmtExecute = function stmtExecute() {
if (this._stmt != null) {
let stmtExecRes = this._chandle.stmtExecute(this._stmt);
if (stmtExecRes != 0) {
throw new errors.DatabaseError(this._chandle.stmtErrStr(this._stmt));
} else {
console.log("stmtExecute success.")
}
} else {
throw new errors.DatabaseError("stmt is null,init stmt first");
}
}
/**
* For SELECT only,getting the query result.
* User application should free it with API 'FreeResult' at the end.
* @returns Not NULL for success, NULL for failure.
*/
TDengineCursor.prototype.stmtUseResult = function stmtUseResult() {
if (this._stmt != null) {
this._result = this._chandle.stmtUseResult(this._stmt);
let res = this._chandle.errno(this._result);
if (res != 0) {
throw new errors.DatabaseError(this._chandle.errStr(this._stmt));
} else {
console.log("stmtUseResult success.");
let fieldCount = this._chandle.fieldsCount(this._result);
if (fieldCount != 0) {
this._fields = this._chandle.useResult(this._result);
this.fields = this._fields;
}
}
} else {
throw new errors.DatabaseError("stmt is null,init stmt first");
}
}
/**
* user application call this API to load all tables meta info.
* This method must be called before stmtSetSubTbname(IntPtr stmt, string name);
* @param {*} tableList tables need to load meta info are form in an array
* @returns 0 for success, non-zero for failure.
*/
TDengineCursor.prototype.loadTableInfo = function loadTableInfo(tableList) {
if (this._connection._conn != null) {
let stmtExecRes = this._chandle.loadTableInfo(this._connection._conn, tableList);
if (stmtExecRes != 0) {
throw new errors.DatabaseError(`loadTableInfo() failed,code ${stmtExecRes}`);
} else {
console.log("loadTableInfo success.")
}
} else {
throw new errors.DatabaseError("taos connection is null.");
}
}
/**
* close STMT object and free resources.
* @param {*} stmt
* @returns 0 for success, non-zero for failure.
*/
TDengineCursor.prototype.stmtClose = function stmtClose() {
if (this._stmt == null) {
throw new DatabaseError("stmt is null,init stmt first");
} else {
let closeStmtRes = this._chandle.closeStmt(this._stmt);
if (closeStmtRes != 0) {
throw new DatabaseError(this._chandle.stmtErrStr(this._stmt));
}
else {
console.log("closeStmt success.");
}
}
}
\ No newline at end of file
/**
* TDengine Error Class
* @ignore
*/
class TDError extends Error {
constructor(args) {
super(args)
this.name = "TDError";
}
}
/** Exception raised for important warnings like data truncations while inserting.
* @ignore
*/
class Warning extends Error {
constructor(args) {
super(args)
this.name = "Warning";
}
}
/** Exception raised for errors that are related to the database interface rather than the database itself.
* @ignore
*/
class InterfaceError extends TDError {
constructor(args) {
super(args)
this.name = "TDError.InterfaceError";
}
}
/** Exception raised for errors that are related to the database.
* @ignore
*/
class DatabaseError extends TDError {
constructor(args) {
super(args)
this.name = "TDError.DatabaseError";
}
}
/** Exception raised for errors that are due to problems with the processed data like division by zero, numeric value out of range.
* @ignore
*/
class DataError extends DatabaseError {
constructor(args) {
super(args)
this.name = "TDError.DatabaseError.DataError";
}
}
/** Exception raised for errors that are related to the database's operation and not necessarily under the control of the programmer
* @ignore
*/
class OperationalError extends DatabaseError {
constructor(args) {
super(args)
this.name = "TDError.DatabaseError.OperationalError";
}
}
/** Exception raised when the relational integrity of the database is affected.
* @ignore
*/
class IntegrityError extends DatabaseError {
constructor(args) {
super(args)
this.name = "TDError.DatabaseError.IntegrityError";
}
}
/** Exception raised when the database encounters an internal error.
* @ignore
*/
class InternalError extends DatabaseError {
constructor(args) {
super(args)
this.name = "TDError.DatabaseError.InternalError";
}
}
/** Exception raised for programming errors.
* @ignore
*/
class ProgrammingError extends DatabaseError {
constructor(args) {
super(args)
this.name = "TDError.DatabaseError.ProgrammingError";
}
}
/** Exception raised in case a method or database API was used which is not supported by the database.
* @ignore
*/
class NotSupportedError extends DatabaseError {
constructor(args) {
super(args)
this.name = "TDError.DatabaseError.NotSupportedError";
}
}
module.exports = {
TDError, Warning, InterfaceError, DatabaseError, DataError, OperationalError, IntegrityError, InternalError, ProgrammingError, NotSupportedError
};
/* Wrap a callback, reduce code amount */
function wrapCB(callback, input) {
if (typeof callback === 'function') {
callback(input);
}
return;
}
global.wrapCB = wrapCB;
function toTaosTSString(date) {
date = new Date(date);
let tsArr = date.toISOString().split("T")
return tsArr[0] + " " + tsArr[1].substring(0, tsArr[1].length-1);
}
global.toTaosTSString = toTaosTSString;
const ref = require('ref-napi');
const StructType = require('ref-struct-di')(ref);
const taosConst = require('./constants');
const { TDError } = require('./error');
var bufferType = ref.types.int32;
var buffer = ref.refType(ref.types.void);
var bufferLength = ref.types.uint64;
var length = ref.refType(ref.types.uint64);
var isNull = ref.refType(ref.types.int32);
var is_unsigned = ref.types.int;
var error = ref.refType(ref.types.void);
var u = ref.types.int64;
var allocated = ref.types.uint32;
var TAOS_BIND = StructType({
buffer_type: bufferType,
buffer: buffer,
buffer_length: bufferLength,
length: length,
is_null: isNull,
is_unsigned: is_unsigned,
error: error,
u: u,
allocated: allocated,
});
class TaosBind {
constructor(num) {
this.buf = Buffer.alloc(TAOS_BIND.size * num);
this.num = num;
this.index = 0;
}
/**
* Used to bind null value for all data types that tdengine supports.
*/
bindNil() {
if (!this._isOutOfBound()) {
let nil = new TAOS_BIND({
buffer_type: taosConst.C_NULL,
is_null: ref.alloc(ref.types.int32, 1),
});
TAOS_BIND.set(this.buf, this.index * TAOS_BIND.size, nil);
this.index++
} else {
throw new TDError(`bindNil() failed,since index:${this.index} is out of Buffer bound ${this.num}.`);
}
}
/**
*
* @param {bool} val is not null bool value,true or false.
*/
bindBool(val) {
if (!this._isOutOfBound()) {
let bl = new TAOS_BIND({
buffer_type: taosConst.C_BOOL,
buffer: ref.alloc(ref.types.bool, val),
buffer_length: ref.types.bool.size,
length: ref.alloc(ref.types.uint64, ref.types.bool.size),
is_null: ref.alloc(ref.types.int32, 0),
});
TAOS_BIND.set(this.buf, this.index * TAOS_BIND.size, bl);
this.index++
} else {
throw new TDError(`bindBool() failed with ${val},since index:${this.index} is out of Buffer bound ${this.num}.`);
}
}
/**
*
* @param {int8} val is a not null tinyint value.
*/
bindTinyInt(val) {
if (!this._isOutOfBound()) {
let tinnyInt = new TAOS_BIND({
buffer_type: taosConst.C_TINYINT,
buffer: ref.alloc(ref.types.int8, val),
buffer_length: ref.types.int8.size,
length: ref.alloc(ref.types.uint64, ref.types.int8.size),
is_null: ref.alloc(ref.types.int32, 0),
});
TAOS_BIND.set(this.buf, this.index * TAOS_BIND.size, tinnyInt);
this.index++
} else {
throw new TDError(`bindTinyInt() failed with ${val},since index:${this.index} is out of Buffer bound ${this.num}.`);
}
}
/**
*
* @param {short} val is a not null small int value.
*/
bindSmallInt(val) {
if (!this._isOutOfBound()) {
let samllint = new TAOS_BIND({
buffer_type: taosConst.C_SMALLINT,
buffer: ref.alloc(ref.types.int16, val),
buffer_length: ref.types.int16.size,
length: ref.alloc(ref.types.uint64, ref.types.int16.size),
is_null: ref.alloc(ref.types.int32, 0),
});
TAOS_BIND.set(this.buf, this.index * TAOS_BIND.size, samllint);
this.index++
} else {
throw new TDError(`bindSmallInt() failed with ${val},since index:${this.index} is out of Buffer bound ${this.num}.`);
}
}
/**
*
* @param {int} val is a not null int value.
*/
bindInt(val) {
if (!this._isOutOfBound()) {
let int = new TAOS_BIND({
buffer_type: taosConst.C_INT,
buffer: ref.alloc(ref.types.int32, val),
buffer_length: ref.types.int32.size,
length: ref.alloc(ref.types.uint64, ref.types.int32.size),
is_null: ref.alloc(ref.types.int32, 0),
});
TAOS_BIND.set(this.buf, this.index * TAOS_BIND.size, int);
this.index++
} else {
throw new TDError(`bindInt() failed with ${val},since index:${this.index} is out of Buffer bound ${this.num}.`);
}
}
/**
*
* @param {long} val is not null big int value.
*/
bindBigInt(val) {
if (!this._isOutOfBound()) {
let bigint = new TAOS_BIND({
buffer_type: taosConst.C_BIGINT,
buffer: ref.alloc(ref.types.int64, val.toString()),
buffer_length: ref.types.int64.size,
length: ref.alloc(ref.types.uint64, ref.types.int64.size),
is_null: ref.alloc(ref.types.int32, 0),
});
TAOS_BIND.set(this.buf, this.index * TAOS_BIND.size, bigint);
this.index++
} else {
throw new TDError(`bindBigInt() failed with ${val},since index:${this.index} is out of Buffer bound ${this.num}.`);
}
}
/**
*
* @param {float} val is a not null float value
*/
bindFloat(val) {
if (!this._isOutOfBound()) {
let float = new TAOS_BIND({
buffer_type: taosConst.C_FLOAT,
buffer: ref.alloc(ref.types.float, val),
buffer_length: ref.types.float.size,
length: ref.alloc(ref.types.uint64, ref.types.float.size),
is_null: ref.alloc(ref.types.int32, 0),
});
TAOS_BIND.set(this.buf, this.index * TAOS_BIND.size, float);
this.index++
} else {
throw new TDError(`bindFloat() failed with ${val},since index:${this.index} is out of Buffer bound ${this.num}.`);
}
}
/**
*
* @param {double} val is a not null double value
*/
bindDouble(val) {
if (!this._isOutOfBound()) {
let double = new TAOS_BIND({
buffer_type: taosConst.C_DOUBLE,
buffer: ref.alloc(ref.types.double, val),
buffer_length: ref.types.double.size,
length: ref.alloc(ref.types.uint64, ref.types.double.size),
is_null: ref.alloc(ref.types.int32, 0),
});
TAOS_BIND.set(this.buf, this.index * TAOS_BIND.size, double);
this.index++
} else {
throw new TDError(`bindDouble() failed with ${val},since index:${this.index} is out of Buffer bound ${this.num}.`);
}
}
/**
*
* @param {string} val is a string.
*/
bindBinary(val) {
let cstringBuf = ref.allocCString(val, 'utf-8');
if (!this._isOutOfBound()) {
let binary = new TAOS_BIND({
buffer_type: taosConst.C_BINARY,
buffer: cstringBuf,
buffer_length: cstringBuf.length,
length: ref.alloc(ref.types.uint64, cstringBuf.length - 1),
is_null: ref.alloc(ref.types.int32, 0),
});
TAOS_BIND.set(this.buf, this.index * TAOS_BIND.size, binary);
this.index++
} else {
throw new TDError(`bindBinary() failed with ${val},since index:${this.index} is out of Buffer bound ${this.num}.`);
}
}
/**
*
* @param {long} val is a not null timestamp(long) values.
*/
bindTimestamp(val) {
let ts = new TAOS_BIND({
buffer_type: taosConst.C_TIMESTAMP,
buffer: ref.alloc(ref.types.int64, val),
buffer_length: ref.types.int64.size,
length: ref.alloc(ref.types.uint64, ref.types.int64.size),
is_null: ref.alloc(ref.types.int32, 0),
});
TAOS_BIND.set(this.buf, this.index * TAOS_BIND.size, ts);
this.index++
}
/**
*
* @param {string} val is a string.
*/
bindNchar(val) {
let cstringBuf = ref.allocCString(val, 'utf-8');
if (!this._isOutOfBound()) {
let nchar = new TAOS_BIND({
buffer_type: taosConst.C_NCHAR,
buffer: cstringBuf,
buffer_length: cstringBuf.length,
length: ref.alloc(ref.types.uint64, cstringBuf.length - 1),
is_null: ref.alloc(ref.types.int32, 0),
});
TAOS_BIND.set(this.buf, this.index * TAOS_BIND.size, nchar);
this.index++
} else {
throw new TDError(`bindNchar() failed with ${val},since index:${this.index} is out of Buffer bound ${this.num}.`);
}
}
/**
*
* @param {uint8} val is a not null unsinged tinyint value.
*/
bindUTinyInt(val) {
if (!this._isOutOfBound()) {
let uTinyInt = new TAOS_BIND({
buffer_type: taosConst.C_TINYINT_UNSIGNED,
buffer: ref.alloc(ref.types.uint8, val),
buffer_length: ref.types.uint8.size,
length: ref.alloc(ref.types.uint64, ref.types.uint8.size),
is_null: ref.alloc(ref.types.int32, 0),
});
TAOS_BIND.set(this.buf, this.index * TAOS_BIND.size, uTinyInt);
this.index++
} else {
throw new TDError(`bindUTinyInt() failed with ${val},since index:${this.index} is out of Buffer bound ${this.num}.`);
}
}
/**
*
* @param {uint16} val is a not null unsinged smallint value.
*/
bindUSmallInt(val) {
if (!this._isOutOfBound()) {
let uSmallInt = new TAOS_BIND({
buffer_type: taosConst.C_SMALLINT_UNSIGNED,
buffer: ref.alloc(ref.types.uint16, val),
buffer_length: ref.types.uint16.size,
length: ref.alloc(ref.types.uint64, ref.types.uint16.size),
is_null: ref.alloc(ref.types.int32, 0),
});
TAOS_BIND.set(this.buf, this.index * TAOS_BIND.size, uSmallInt);
this.index++
} else {
throw new TDError(`bindUSmallInt() failed with ${val},since index:${this.index} is out of Buffer bound ${this.num}.`);
}
}
/**
*
* @param {uint32} val is a not null unsinged int value.
*/
bindUInt(val) {
if (!this._isOutOfBound()) {
let uInt = new TAOS_BIND({
buffer_type: taosConst.C_INT_UNSIGNED,
buffer: ref.alloc(ref.types.uint32, val),
buffer_length: ref.types.uint32.size,
length: ref.alloc(ref.types.uint64, ref.types.uint32.size),
is_null: ref.alloc(ref.types.int32, 0),
});
TAOS_BIND.set(this.buf, this.index * TAOS_BIND.size, uInt);
this.index++
} else {
throw new TDError(`bindUInt() failed with ${val},since index:${this.index} is out of Buffer bound ${this.num}.`);
}
}
/**
*
* @param {uint64} val is a not null unsinged bigint value.
*/
bindUBigInt(val) {
if (!this._isOutOfBound()) {
let uBigInt = new TAOS_BIND({
buffer_type: taosConst.C_BIGINT_UNSIGNED,
buffer: ref.alloc(ref.types.uint64, val.toString()),
buffer_length: ref.types.uint64.size,
length: ref.alloc(ref.types.uint64, ref.types.uint64.size),
is_null: ref.alloc(ref.types.int32, 0),
});
TAOS_BIND.set(this.buf, this.index * TAOS_BIND.size, uBigInt);
this.index++
} else {
throw new TDError(`bindUBigInt() failed with ${val},since index:${this.index} is out of Buffer bound ${this.num}.`);
}
}
/**
*
* @param {jsonStr} val is a json string. Such as '{\"key1\":\"taosdata\"}'
*/
bindJson(val) {
let cstringBuf = ref.allocCString(val, 'utf-8');
if (!this._isOutOfBound()) {
let jsonType = new TAOS_BIND({
buffer_type: taosConst.C_JSON_TAG,
buffer: cstringBuf,
buffer_length: cstringBuf.length,
length: ref.alloc(ref.types.uint64, cstringBuf.length - 1),
is_null: ref.alloc(ref.types.int32, 0),
});
TAOS_BIND.set(this.buf, this.index * TAOS_BIND.size, jsonType);
this.index++
} else {
throw new TDError(`bindJson() failed with ${val},since index:${this.index} is out of Buffer bound ${this.num}.`);
}
}
/**
*
* @returns binded buffer.
*/
getBind() {
return this.buf;
}
_isOutOfBound() {
if (this.num > this.index) {
return false;
} else {
return true;
}
}
}
module.exports = TaosBind;
const ref = require('ref-napi');
const StructType = require('ref-struct-di')(ref);
const taosConst = require('./constants');
var TAOS_MULTI_BIND = StructType({
'buffer_type': ref.types.int,
'buffer': ref.refType(ref.types.void),
'buffer_length': ref.types.ulong,
'length': ref.refType(ref.types.int),
'is_null': ref.refType(ref.types.char),
'num': ref.types.int,
})
class TaosMultiBind {
constructor() {
}
/**
* To bind bool through an array.
* @param {*} boolArray is an boolean array that stores one column's value.
* @returns A instance of struct TAOS_MULTI_BIND that contains one column's data with bool type.
*/
multiBindBool(boolArray) {
let mbindBufferBuf = Buffer.alloc(ref.types.bool.size * boolArray.length);
let mbindLengBuf = Buffer.alloc(ref.types.int.size * boolArray.length);
let mbindIsNullBuf = Buffer.alloc(ref.types.char.size * boolArray.length);
boolArray.forEach((element, index) => {
ref.set(mbindLengBuf, index * ref.types.int.size, ref.types.bool.size, ref.types.int)
if (element == null || element == undefined) {
// ref.set(mbindBufferBuf,index * ref.types.int64.size,taosConst.C_BIGINT_NULL,ref.types.int64);
ref.set(mbindIsNullBuf, index * ref.types.char.size, 1, ref.types.char);
} else {
ref.set(mbindBufferBuf, index * ref.types.bool.size, element, ref.types.bool);
ref.set(mbindIsNullBuf, index * ref.types.char.size, 0, ref.types.char);
}
});
let mbind = new TAOS_MULTI_BIND({
buffer_type: taosConst.C_BOOL,
buffer: mbindBufferBuf,
buffer_length: ref.types.bool.size,
length: mbindLengBuf,
is_null: mbindIsNullBuf,
num: boolArray.length,
})
return mbind;
}
/**
* to bind tiny int through an array.
* @param {*} tinyIntArray is an array that stores tiny int.
* @returns A instance of struct TAOS_MULTI_BIND that contains one column's data with tiny int.
*/
multiBindTinyInt(tinyIntArray) {
let mbindBufferBuf = Buffer.alloc(ref.types.int8.size * tinyIntArray.length);
let mbindLengBuf = Buffer.alloc(ref.types.int.size * tinyIntArray.length);
let mbindIsNullBuf = Buffer.alloc(ref.types.char.size * tinyIntArray.length);
tinyIntArray.forEach((element, index) => {
ref.set(mbindLengBuf, index * ref.types.int.size, ref.types.int8.size, ref.types.int)
if (element == null || element == undefined) {
// ref.set(mbindBufferBuf,index * ref.types.int64.size,taosConst.C_BIGINT_NULL,ref.types.int64);
ref.set(mbindIsNullBuf, index * ref.types.char.size, 1, ref.types.char);
} else {
ref.set(mbindBufferBuf, index * ref.types.int8.size, element, ref.types.int8);
ref.set(mbindIsNullBuf, index * ref.types.char.size, 0, ref.types.char);
}
});
let mbind = new TAOS_MULTI_BIND({
buffer_type: taosConst.C_TINYINT,
buffer: mbindBufferBuf,
buffer_length: ref.types.int8.size,
length: mbindLengBuf,
is_null: mbindIsNullBuf,
num: tinyIntArray.length,
})
return mbind;
}
/**
* To bind small int through an array.
* @param {*} smallIntArray is an array that stores small int.
* @returns A instance of struct TAOS_MULTI_BIND that contains one column's data with small int.
*/
multiBindSmallInt(smallIntArray) {
let mbindBufferBuf = Buffer.alloc(ref.types.int16.size * smallIntArray.length);
let mbindLengBuf = Buffer.alloc(ref.types.int.size * smallIntArray.length);
let mbindIsNullBuf = Buffer.alloc(ref.types.char.size * smallIntArray.length);
smallIntArray.forEach((element, index) => {
ref.set(mbindLengBuf, index * ref.types.int.size, ref.types.int16.size, ref.types.int)
if (element == null || element == undefined) {
ref.set(mbindIsNullBuf, index * ref.types.char.size, 1, ref.types.char);
} else {
ref.set(mbindBufferBuf, index * ref.types.int16.size, element, ref.types.int16);
ref.set(mbindIsNullBuf, index * ref.types.char.size, 0, ref.types.char);
}
});
let mbind = new TAOS_MULTI_BIND({
buffer_type: taosConst.C_SMALLINT,
buffer: mbindBufferBuf,
buffer_length: ref.types.int16.size,
length: mbindLengBuf,
is_null: mbindIsNullBuf,
num: smallIntArray.length,
})
return mbind;
}
/**
* To bind int through an array.
* @param {*} intArray is an array that stores int.
* @returns A instance of struct TAOS_MULTI_BIND that contains one column's data with int.
*/
multiBindInt(intArray) {
let mbindBufferBuf = Buffer.alloc(ref.types.int.size * intArray.length);
let mbindLengBuf = Buffer.alloc(ref.types.int.size * intArray.length);
let mbindIsNullBuf = Buffer.alloc(ref.types.char.size * intArray.length);
intArray.forEach((element, index) => {
ref.set(mbindLengBuf, index * ref.types.int.size, ref.types.int.size, ref.types.int)
if (element == null || element == undefined) {
ref.set(mbindIsNullBuf, index * ref.types.char.size, 1, ref.types.char);
} else {
ref.set(mbindBufferBuf, index * ref.types.int.size, element, ref.types.int);
ref.set(mbindIsNullBuf, index * ref.types.char.size, 0, ref.types.char);
}
});
let mbind = new TAOS_MULTI_BIND({
buffer_type: taosConst.C_INT,
buffer: mbindBufferBuf,
buffer_length: ref.types.int.size,
length: mbindLengBuf,
is_null: mbindIsNullBuf,
num: intArray.length,
})
return mbind;
}
/**
* To bind big int through an array.
* @param {*} bigIntArray is an array that stores big int.
* @returns A instance of struct TAOS_MULTI_BIND that contains one column's data with big int.
*/
multiBindBigInt(bigIntArray) {
let mbindBufferBuf = Buffer.alloc(ref.types.int64.size * bigIntArray.length);
let mbindLengBuf = Buffer.alloc(ref.types.int.size * bigIntArray.length);
let mbindIsNullBuf = Buffer.alloc(ref.types.char.size * bigIntArray.length);
bigIntArray.forEach((element, index) => {
ref.set(mbindLengBuf, index * ref.types.int.size, ref.types.int64.size, ref.types.int)
if (element == null || element == undefined) {
ref.set(mbindIsNullBuf, index * ref.types.char.size, 1, ref.types.char);
} else {
ref.writeInt64LE(mbindBufferBuf, index * ref.types.int64.size, element.toString())
ref.set(mbindIsNullBuf, index * ref.types.char.size, 0, ref.types.char);
}
});
let mbind = new TAOS_MULTI_BIND({
buffer_type: taosConst.C_BIGINT,
buffer: mbindBufferBuf,
buffer_length: ref.types.int64.size,
length: mbindLengBuf,
is_null: mbindIsNullBuf,
num: bigIntArray.length,
})
return mbind;
}
/**
* To bind float through an array.
* @param {*} floatArray is an array that stores float.
* @returns A instance of struct TAOS_MULTI_BIND that contains one column's data with float.
*/
multiBindFloat(floatArray) {
let mbindBufferBuf = Buffer.alloc(ref.types.float.size * floatArray.length);
let mbindLengBuf = Buffer.alloc(ref.types.int.size * floatArray.length);
let mbindIsNullBuf = Buffer.alloc(ref.types.char.size * floatArray.length);
floatArray.forEach((element, index) => {
ref.set(mbindLengBuf, index * ref.types.int.size, ref.types.float.size, ref.types.int)
if (element == null || element == undefined) {
ref.set(mbindIsNullBuf, index * ref.types.char.size, 1, ref.types.char);
} else {
ref.set(mbindBufferBuf, index * ref.types.float.size, element, ref.types.float);
ref.set(mbindIsNullBuf, index * ref.types.char.size, 0, ref.types.char);
}
});
let mbind = new TAOS_MULTI_BIND({
buffer_type: taosConst.C_FLOAT,
buffer: mbindBufferBuf,
buffer_length: ref.types.float.size,
length: mbindLengBuf,
is_null: mbindIsNullBuf,
num: floatArray.length,
})
return mbind;
}
/**
* To bind double through an array.
* @param {*} doubleArray is an array that stores double.
* @returns A instance of struct TAOS_MULTI_BIND that contains one column's data with double.
*/
multiBindDouble(doubleArray) {
let mbindBufferBuf = Buffer.alloc(ref.types.double.size * doubleArray.length);
let mbindLengBuf = Buffer.alloc(ref.types.int.size * doubleArray.length);
let mbindIsNullBuf = Buffer.alloc(ref.types.char.size * doubleArray.length);
doubleArray.forEach((element, index) => {
ref.set(mbindLengBuf, index * ref.types.int.size, ref.types.double.size, ref.types.int)
if (element == null || element == undefined) {
ref.set(mbindIsNullBuf, index * ref.types.char.size, 1, ref.types.char);
} else {
ref.set(mbindBufferBuf, index * ref.types.double.size, element, ref.types.double);
ref.set(mbindIsNullBuf, index * ref.types.char.size, 0, ref.types.char);
}
});
let mbind = new TAOS_MULTI_BIND({
buffer_type: taosConst.C_DOUBLE,
buffer: mbindBufferBuf,
buffer_length: ref.types.double.size,
length: mbindLengBuf,
is_null: mbindIsNullBuf,
num: doubleArray.length,
})
return mbind;
}
/**
* To bind tdengine's binary through an array.
* @param {*} strArr is an array that stores string.
* (Null string can be defined as undefined or null,notice '' is not null.)
* @returns A instance of struct TAOS_MULTI_BIND that contains one column's data with binary.
*/
multiBindBinary(strArr) {
let maxStrUFT8Length = this._maxUTF8StrArrLength(strArr);
console.log(`maxStrUFT8Length * strArr.length=${maxStrUFT8Length * strArr.length}`);
let mbindBufferBuf = Buffer.alloc(maxStrUFT8Length * strArr.length);
let mbindLengBuf = Buffer.alloc(ref.types.int.size * strArr.length);
let mbindIsNullBuf = Buffer.alloc(ref.types.char.size * strArr.length);
strArr.forEach((element, index) => {
ref.set(mbindLengBuf, index * ref.types.int.size, this._stringUTF8Length(element), ref.types.int)
if (element == null || element == undefined) {
ref.set(mbindIsNullBuf, index * ref.types.char.size, 1, ref.types.char);
} else {
ref.writeCString(mbindBufferBuf, index * maxStrUFT8Length, element, 'utf8');
ref.set(mbindIsNullBuf, index * ref.types.char.size, 0, ref.types.char);
}
});
let mbind = new TAOS_MULTI_BIND({
buffer_type: taosConst.C_BINARY,
buffer: mbindBufferBuf,
buffer_length: maxStrUFT8Length,
length: mbindLengBuf,
is_null: mbindIsNullBuf,
num: strArr.length,
})
return mbind;
}
/**
* To bind timestamp through an array.
* @param {*} timestampArray is an array that stores timestamp.
* @returns A instance of struct TAOS_MULTI_BIND that contains one column's data with timestamp.
*/
multiBindTimestamp(timestampArray) {
let mbindBufferBuf = Buffer.alloc(ref.types.int64.size * timestampArray.length);
let mbindLengBuf = Buffer.alloc(ref.types.int.size * timestampArray.length);
let mbindIsNullBuf = Buffer.alloc(ref.types.char.size * timestampArray.length);
timestampArray.forEach((element, index) => {
ref.set(mbindLengBuf, index * ref.types.int.size, ref.types.int64.size, ref.types.int)
if (element == null || element == undefined) {
ref.set(mbindIsNullBuf, index * ref.types.char.size, 1, ref.types.char);
} else {
ref.writeInt64LE(mbindBufferBuf, index * ref.types.int64.size, element.toString())
ref.set(mbindIsNullBuf, index * ref.types.char.size, 0, ref.types.char);
}
});
let mbind = new TAOS_MULTI_BIND({
buffer_type: taosConst.C_TIMESTAMP,
buffer: mbindBufferBuf,
buffer_length: ref.types.int64.size,
length: mbindLengBuf,
is_null: mbindIsNullBuf,
num: timestampArray.length,
})
return mbind;
}
/**
* To bind tdengine's nchar through an array.
* @param {*} strArr is an array that stores string.
* (Null string can be defined as undefined or null,notice '' is not null.)
* @returns A instance of struct TAOS_MULTI_BIND that contains one nchar column's data with nchar.
*/
multiBindNchar(strArr) {
let maxStrUFT8Length = this._maxUTF8StrArrLength(strArr);
// console.log(`maxStrUFT8Length * strArr.length=${maxStrUFT8Length * strArr.length}`);
let mbindBufferBuf = Buffer.alloc(maxStrUFT8Length * strArr.length);
let mbindLengBuf = Buffer.alloc(ref.types.int.size * strArr.length);
let mbindIsNullBuf = Buffer.alloc(ref.types.char.size * strArr.length);
strArr.forEach((element, index) => {
ref.set(mbindLengBuf, index * ref.types.int.size, this._stringUTF8Length(element), ref.types.int)
if (element == null || element == undefined) {
ref.set(mbindIsNullBuf, index * ref.types.char.size, 1, ref.types.char);
} else {
ref.writeCString(mbindBufferBuf, index * maxStrUFT8Length, element, 'utf8');
ref.set(mbindIsNullBuf, index * ref.types.char.size, 0, ref.types.char);
}
});
let mbind = new TAOS_MULTI_BIND({
buffer_type: taosConst.C_NCHAR,
buffer: mbindBufferBuf,
buffer_length: maxStrUFT8Length,
length: mbindLengBuf,
is_null: mbindIsNullBuf,
num: strArr.length,
})
return mbind;
}
/**
* to bind unsigned tiny int through an array.
* @param {*} uTinyIntArray is an array that stores unsigned tiny int.
* @returns A instance of struct TAOS_MULTI_BIND that contains one column's data with unsigned tiny int.
*/
multiBindUTinyInt(uTinyIntArray) {
let mbindBufferBuf = Buffer.alloc(ref.types.uint8.size * uTinyIntArray.length);
let mbindLengBuf = Buffer.alloc(ref.types.int.size * uTinyIntArray.length);
let mbindIsNullBuf = Buffer.alloc(ref.types.char.size * uTinyIntArray.length);
uTinyIntArray.forEach((element, index) => {
ref.set(mbindLengBuf, index * ref.types.int.size, ref.types.uint8.size, ref.types.int)
if (element == null || element == undefined) {
ref.set(mbindIsNullBuf, index * ref.types.char.size, 1, ref.types.char);
} else {
ref.set(mbindBufferBuf, index * ref.types.uint8.size, element, ref.types.uint8);
ref.set(mbindIsNullBuf, index * ref.types.char.size, 0, ref.types.char);
}
});
let mbind = new TAOS_MULTI_BIND({
buffer_type: taosConst.C_TINYINT_UNSIGNED,
buffer: mbindBufferBuf,
buffer_length: ref.types.uint8.size,
length: mbindLengBuf,
is_null: mbindIsNullBuf,
num: uTinyIntArray.length,
})
return mbind;
}
/**
* To bind unsigned small int through an array.
* @param {*} uSmallIntArray is an array that stores unsigned small int.
* @returns A instance of struct TAOS_MULTI_BIND that contains one column's data with unsigned small int.
*/
multiBindUSmallInt(uSmallIntArray) {
let mbindBufferBuf = Buffer.alloc(ref.types.uint16.size * uSmallIntArray.length);
let mbindLengBuf = Buffer.alloc(ref.types.int.size * uSmallIntArray.length);
let mbindIsNullBuf = Buffer.alloc(ref.types.char.size * uSmallIntArray.length);
uSmallIntArray.forEach((element, index) => {
ref.set(mbindLengBuf, index * ref.types.int.size, ref.types.uint16.size, ref.types.int)
if (element == null || element == undefined) {
ref.set(mbindIsNullBuf, index * ref.types.char.size, 1, ref.types.char);
} else {
ref.set(mbindBufferBuf, index * ref.types.uint16.size, element, ref.types.uint16);
ref.set(mbindIsNullBuf, index * ref.types.char.size, 0, ref.types.char);
}
});
let mbind = new TAOS_MULTI_BIND({
buffer_type: taosConst.C_SMALLINT_UNSIGNED,
buffer: mbindBufferBuf,
buffer_length: ref.types.uint16.size,
length: mbindLengBuf,
is_null: mbindIsNullBuf,
num: uSmallIntArray.length,
})
return mbind;
}
/**
* To bind unsigned int through an array.
* @param {*} uIntArray is an array that stores unsigned int.
* @returns A instance of struct TAOS_MULTI_BIND that contains one column's data with unsigned int.
*/
multiBindUInt(uIntArray) {
let mbindBufferBuf = Buffer.alloc(ref.types.uint.size * uIntArray.length);
let mbindLengBuf = Buffer.alloc(ref.types.int.size * uIntArray.length);
let mbindIsNullBuf = Buffer.alloc(ref.types.char.size * uIntArray.length);
uIntArray.forEach((element, index) => {
ref.set(mbindLengBuf, index * ref.types.int.size, ref.types.uint.size, ref.types.int)
if (element == null || element == undefined) {
ref.set(mbindIsNullBuf, index * ref.types.char.size, 1, ref.types.char);
} else {
ref.set(mbindBufferBuf, index * ref.types.uint.size, element, ref.types.uint);
ref.set(mbindIsNullBuf, index * ref.types.char.size, 0, ref.types.char);
}
});
let mbind = new TAOS_MULTI_BIND({
buffer_type: taosConst.C_INT_UNSIGNED,
buffer: mbindBufferBuf,
buffer_length: ref.types.uint.size,
length: mbindLengBuf,
is_null: mbindIsNullBuf,
num: uIntArray.length,
})
return mbind;
}
/**
* To bind unsigned big int through an array.
* @param {*} uBigIntArray is an array that stores unsigned big int.
* @returns A instance of struct TAOS_MULTI_BIND that contains one column's data with unsigned big int.
*/
multiBindUBigInt(uBigIntArray) {
let mbindBufferBuf = Buffer.alloc(ref.types.uint64.size * uBigIntArray.length);
let mbindLengBuf = Buffer.alloc(ref.types.int.size * uBigIntArray.length);
let mbindIsNullBuf = Buffer.alloc(ref.types.char.size * uBigIntArray.length);
uBigIntArray.forEach((element, index) => {
ref.set(mbindLengBuf, index * ref.types.int.size, ref.types.uint64.size, ref.types.int)
if (element == null || element == undefined) {
ref.set(mbindIsNullBuf, index * ref.types.char.size, 1, ref.types.char);
} else {
ref.writeUInt64LE(mbindBufferBuf, index * ref.types.uint64.size, element.toString())
ref.set(mbindIsNullBuf, index * ref.types.char.size, 0, ref.types.char);
}
});
let mbind = new TAOS_MULTI_BIND({
buffer_type: taosConst.C_BIGINT_UNSIGNED,
buffer: mbindBufferBuf,
buffer_length: ref.types.uint64.size,
length: mbindLengBuf,
is_null: mbindIsNullBuf,
num: uBigIntArray.length,
})
return mbind;
}
// multiBJson(jsonArray) no need to support.Since till now TDengine only support json tag
// and there is no need to support bind json tag in TAOS_MULTI_BIND.
/**
*
* @param {*} strArr an string array
* @returns return the max length of the element in strArr in "UFT-8" encoding.
*/
_maxUTF8StrArrLength(strArr) {
let max = 0;
strArr.forEach((item) => {
let realLeng = 0;
let itemLength = -1;
if (item == null || item == undefined) {
itemLength = 0;
} else {
itemLength = item.length;
}
let charCode = -1;
for (let i = 0; i < itemLength; i++) {
charCode = item.charCodeAt(i);
if (charCode >= 0 && charCode <= 128) {
realLeng += 1;
} else {
realLeng += 3;
}
}
if (max < realLeng) {
max = realLeng
};
});
return max;
}
/**
*
* @param {*} str a string.
* @returns return the length of the input string encoding with utf-8.
*/
_stringUTF8Length(str) {
let leng = 0;
if (str == null || str == undefined) {
leng = 0;
} else {
for (let i = 0; i < str.length; i++) {
if (str.charCodeAt(i) >= 0 && str.charCodeAt(i) <= 128) {
leng += 1;
} else {
leng += 3;
}
}
}
return leng;
}
}
// console.log(TAOS_MULTI_BIND.size)
module.exports = { TaosMultiBind, TAOS_MULTI_BIND };
\ No newline at end of file
const ref = require('ref-napi');
const { TDError } = require('./error');
const { TAOS_MULTI_BIND, TaosMultiBind } = require('./taosMultiBind');
const TAOS_MULTI_BIND_SIZE = TAOS_MULTI_BIND.size;
class TaosMultiBindArr extends TaosMultiBind {
/**
* The constructor,initial basic parameters and alloc buffer.
* @param {*} numOfColumns the number of column that you want to bind parameters.
*/
constructor(numOfColumns) {
super();
this.taosMBindArrBuf = Buffer.alloc(numOfColumns * TAOS_MULTI_BIND_SIZE);
this.index = 0;
this.bound = numOfColumns;
}
/**
* Used to bind boolean column's values.
* @param {*} boolArray An array of bool value,
* represents the bool values you want to bind.
*/
multiBindBool(boolArray) {
if (this.bound > this.index) {
let mBindBool = super.multiBindBool(boolArray);
TAOS_MULTI_BIND.set(this.taosMBindArrBuf, this.index * TAOS_MULTI_BIND_SIZE, mBindBool);
this.index += 1;
} else {
throw new TDError(`multiBindArrBool() failed,since index:${this.index} is out of Buffer bound ${this.bound}.`)
}
}
/**
* Used to bind tiny int column's values.
* @param {*} tinyIntArray An array of tiny int value.
* represents the tiny int values you want to bind.
*/
multiBindTinyInt(tinyIntArray) {
if (this.bound > this.index) {
let mBindTinyInt = super.multiBindTinyInt(tinyIntArray);
TAOS_MULTI_BIND.set(this.taosMBindArrBuf, this.index * TAOS_MULTI_BIND_SIZE, mBindTinyInt);
this.index += 1;
} else {
throw new TDError(`multiBindArrTinyInt() failed,since index:${this.index} is out of Buffer bound ${this.bound}.`)
}
}
/**
* Used to bind small int column's value.
* @param {*} smallIntArray An array of small int values,
* represents the small int values you want to bind.
*/
multiBindSmallInt(smallIntArray) {
if (this.bound > this.index) {
let mBindSmallInt = super.multiBindSmallInt(smallIntArray);
TAOS_MULTI_BIND.set(this.taosMBindArrBuf, this.index * TAOS_MULTI_BIND_SIZE, mBindSmallInt);
this.index += 1;
} else {
throw new TDError(`multiBindSmallInt() failed,since index:${this.index} is out of Buffer bound ${this.bound}.`)
}
}
/**
* Used to bind int column's value.
* @param {*} intArray An array of int values,
* represents the int values you want to bind.
*/
multiBindInt(intArray) {
if (this.bound > this.index) {
let mBindInt = super.multiBindInt(intArray);
TAOS_MULTI_BIND.set(this.taosMBindArrBuf, this.index * TAOS_MULTI_BIND_SIZE, mBindInt);
this.index += 1;
} else {
throw new TDError(`multiBindInt() failed,since index:${this.index} is out of Buffer bound ${this.bound}.`)
}
}
/**
* Used to bind big int column's value.
* @param {*} bigIntArray An array of big int values,
* represents the big int values you want to bind.
*/
multiBindBigInt(bigIntArray) {
if (this.bound > this.index) {
let mBindBigInt = super.multiBindBigInt(bigIntArray);
TAOS_MULTI_BIND.set(this.taosMBindArrBuf, this.index * TAOS_MULTI_BIND_SIZE, mBindBigInt);
this.index += 1;
} else {
throw new TDError(`multiBindBigInt() failed,since index:${this.index} is out of Buffer bound ${this.bound}.`)
}
}
/**
* Used to bind float column's value.
* @param {*} floatArray An array of float values,
* represents the float values you want to bind.
*/
multiBindFloat(floatArray) {
if (this.bound > this.index) {
let mBindFloat = super.multiBindFloat(floatArray);
TAOS_MULTI_BIND.set(this.taosMBindArrBuf, this.index * TAOS_MULTI_BIND_SIZE, mBindFloat);
this.index += 1;
} else {
throw new TDError(`multiBindFloat() failed,since index:${this.index} is out of Buffer bound ${this.bound}.`)
}
}
/**
* Used to bind double column's value.
* @param {*} doubleArray An array of double values,
* represents the double values you want to bind.
*/
multiBindDouble(doubleArray) {
if (this.bound > this.index) {
let mBindDouble = super.multiBindDouble(doubleArray);
TAOS_MULTI_BIND.set(this.taosMBindArrBuf, this.index * TAOS_MULTI_BIND_SIZE, mBindDouble);
this.index += 1;
} else {
throw new TDError(`multiBindDouble() failed,since index:${this.index} is out of Buffer bound ${this.bound}.`)
}
}
/**
* Used to bind binary column's value.
* @param {*} strArr An array of binary(string) values,
* represents the binary values you want to bind.
* Notice '' is not equal to TDengine's "null" value.
*/
multiBindBinary(strArr) {
if (this.bound > this.index) {
let mBindBinary = super.multiBindBinary(strArr);
TAOS_MULTI_BIND.set(this.taosMBindArrBuf, this.index * TAOS_MULTI_BIND_SIZE, mBindBinary);
this.index += 1;
} else {
throw new TDError(`multiBindBinary() failed,since index:${this.index} is out of Buffer bound ${this.bound}.`)
}
}
/**
* Used to bind timestamp column's values.
* @param {*} timestampArray An array of timestamp values,
* represents the timestamp values you want to bind.
*/
multiBindTimestamp(timestampArray) {
if (this.bound > this.index) {
let mBindTimestamp = super.multiBindTimestamp(timestampArray);
TAOS_MULTI_BIND.set(this.taosMBindArrBuf, this.index * TAOS_MULTI_BIND_SIZE, mBindTimestamp);
this.index += 1;
} else {
throw new TDError(`multiBindArrTimestamp() failed,since index:${this.index} is out of Buffer bound ${this.bound}.`)
}
}
/**
* Used to bind nchar column's value.
* @param {*} strArr An array of nchar(string) values,
* represents the nchar values you want to bind.
* Notice '' is not equal to TDengine's "null" value.
*/
multiBindNchar(strArr) {
if (this.bound > this.index) {
let mBindNchar = super.multiBindNchar(strArr);
TAOS_MULTI_BIND.set(this.taosMBindArrBuf, this.index * TAOS_MULTI_BIND_SIZE, mBindNchar);
this.index += 1;
} else {
throw new TDError(`multiBindNchar() failed,since index:${this.index} is out of Buffer bound ${this.bound}.`)
}
}
/**
* Used to bind unsigned tiny int column's value.
* @param {*} uTinyIntArray An array of unsigned tiny int values,
* represents the unsigned tiny int values you want to bind.
*/
multiBindUTinyInt(uTinyIntArray) {
if (this.bound > this.index) {
let mBindNchar = super.multiBindUTinyInt(uTinyIntArray);
TAOS_MULTI_BIND.set(this.taosMBindArrBuf, this.index * TAOS_MULTI_BIND_SIZE, mBindNchar);
this.index += 1;
} else {
throw new TDError(`multiBindUTinyInt() failed,since index:${this.index} is out of Buffer bound ${this.bound}.`)
}
}
/**
* Used to bind unsigned small int column's value.
* @param {*} uSmallIntArray An array of unsigned small int value,
* represents the unsigned small int values you want to bind.
*/
multiBindUSmallInt(uSmallIntArray) {
if (this.bound > this.index) {
let mBindUSmallInt = super.multiBindUSmallInt(uSmallIntArray);
TAOS_MULTI_BIND.set(this.taosMBindArrBuf, this.index * TAOS_MULTI_BIND_SIZE, mBindUSmallInt);
this.index += 1;
} else {
throw new TDError(`multiBindUSmallInt() failed,since index:${this.index} is out of Buffer bound ${this.bound}.`)
}
}
/**
* Used to bind unsigned int column's value.
* @param {*} uIntArray An array of unsigned int column's value,
* represents the unsigned int values you want to bind.
*/
multiBindUInt(uIntArray) {
if (this.bound > this.index) {
let mBindUInt = super.multiBindUInt(uIntArray);
TAOS_MULTI_BIND.set(this.taosMBindArrBuf, this.index * TAOS_MULTI_BIND_SIZE, mBindUInt);
this.index += 1;
} else {
throw new TDError(`multiBindUInt() failed,since index:${this.index} is out of Buffer bound ${this.bound}.`)
}
}
/**
* Used to bind unsigned big int column's value.
* @param {*} uBigIntArray An array of unsigned big int column's value,
* represents the unsigned big int values you want to bind.
*/
multiBindUBigInt(uBigIntArray) {
if (this.bound > this.index) {
let mBindUBigInt = super.multiBindUBigInt(uBigIntArray);
TAOS_MULTI_BIND.set(this.taosMBindArrBuf, this.index * TAOS_MULTI_BIND_SIZE, mBindUBigInt);
this.index += 1;
} else {
throw new TDError(`multiBindUBigInt() failed,since index:${this.index} is out of Buffer bound ${this.bound}.`)
}
}
// multiBJson(jsonArray) no need to support.Since till now TDengine only support json tag
// and there is no need to support bind json tag in TAOS_MULTI_BIND.
/**
* After all the parameters have been prepared and stored
* in the buffer, Call this method to get the buffer.
* @returns return the buffer which stores all the parameters.
*/
getMultiBindArr() {
return this.taosMBindArrBuf;
}
}
module.exports = TaosMultiBindArr;
\ No newline at end of file
const FieldTypes = require('./constants');
const util = require('util');
/**
* Various objects such as TaosRow and TaosColumn that help make parsing data easier
* @module TaosObjects
*
*/
/**
* The TaosRow object. Contains the data from a retrieved row from a database and functions that parse the data.
* @typedef {Object} TaosRow - A row of data retrieved from a table.
* @global
* @example
* var trow = new TaosRow(row);
* console.log(trow.data);
*/
function TaosRow(row) {
this.data = row;
this.length = row.length;
return this;
}
/**
* @typedef {Object} TaosField - A field/column's metadata from a table.
* @global
* @example
* var tfield = new TaosField(field);
* console.log(tfield.name);
*/
function TaosField(field) {
this._field = field;
this.name = field.name;
this.type = FieldTypes.getType(field.type);
return this;
}
/**
* A TaosTimestamp object, which is the standard date object with added functionality
* @global
* @memberof TaosObjects
* @param {Date} date - A Javascript date time object or the time in milliseconds past 1970-1-1 00:00:00.000
*/
class TaosTimestamp extends Date {
constructor(date, precision = 0) {
if (precision === 1) {
super(Math.floor(date / 1000));
this.precisionExtras = date % 1000;
} else if (precision === 2) {
// use BigInt to fix: 1623254400999999999 / 1000000 = 1623254401000 which not expected
super(parseInt(BigInt(date) / 1000000n));
// use BigInt to fix: 1625801548423914405 % 1000000 = 914496 which not expected (914405)
this.precisionExtras = parseInt(BigInt(date) % 1000000n);
} else {
super(parseInt(date));
}
this.precision = precision;
}
/**
* TDengine raw timestamp.
* @returns raw taos timestamp (int64)
*/
taosTimestamp() {
if (this.precision == 1) {
return (this * 1000 + this.precisionExtras);
} else if (this.precision == 2) {
return (this * 1000000 + this.precisionExtras);
} else {
return Math.floor(this);
}
}
/**
* Gets the microseconds of a Date.
* @return {Int} A microseconds integer
*/
getMicroseconds() {
if (this.precision == 1) {
return this.getMilliseconds() * 1000 + this.precisionExtras;
} else if (this.precision == 2) {
return this.getMilliseconds() * 1000 + this.precisionExtras / 1000;
} else {
return 0;
}
}
/**
* Gets the nanoseconds of a TaosTimestamp.
* @return {Int} A nanoseconds integer
*/
getNanoseconds() {
if (this.precision == 1) {
return this.getMilliseconds() * 1000000 + this.precisionExtras * 1000;
} else if (this.precision == 2) {
return this.getMilliseconds() * 1000000 + this.precisionExtras;
} else {
return 0;
}
}
/**
* @returns {String} a string for timestamp string format
*/
_precisionExtra() {
if (this.precision == 1) {
return String(this.precisionExtras).padStart(3, '0');
} else if (this.precision == 2) {
return String(this.precisionExtras).padStart(6, '0');
} else {
return '';
}
}
/**
* @function Returns the date into a string usable by TDengine
* @return {string} A Taos Timestamp String
*/
toTaosString() {
var tzo = -this.getTimezoneOffset(),
dif = tzo >= 0 ? '+' : '-',
pad = function (num) {
var norm = Math.floor(Math.abs(num));
return (norm < 10 ? '0' : '') + norm;
},
pad2 = function (num) {
var norm = Math.floor(Math.abs(num));
if (norm < 10) return '00' + norm;
if (norm < 100) return '0' + norm;
if (norm < 1000) return norm;
};
return this.getFullYear() +
'-' + pad(this.getMonth() + 1) +
'-' + pad(this.getDate()) +
' ' + pad(this.getHours()) +
':' + pad(this.getMinutes()) +
':' + pad(this.getSeconds()) +
'.' + pad2(this.getMilliseconds()) +
'' + this._precisionExtra();
}
/**
* Custom console.log
* @returns {String} string format for debug
*/
[util.inspect.custom](depth, opts) {
return this.toTaosString() + JSON.stringify({ precision: this.precision, precisionExtras: this.precisionExtras }, opts);
}
toString() {
return this.toTaosString();
}
}
module.exports = { TaosRow, TaosField, TaosTimestamp }
var TaosResult = require('./taosresult')
require('./globalfunc.js')
module.exports = TaosQuery;
/**
* @class TaosQuery
* @classdesc The TaosQuery class is one level above the TDengine Cursor in that it makes sure to generally return promises from functions, and wrap
* all data with objects such as wrapping a row of data with Taos Row. This is meant to enable an higher level API that allows additional
* functionality and save time whilst also making it easier to debug and enter less problems with the use of promises.
* @param {string} query - Query to construct object from
* @param {TDengineCursor} cursor - The cursor from which this query will execute from
* @param {boolean} execute - Whether or not to immedietely execute the query synchronously and fetch all results. Default is false.
* @property {string} query - The current query in string format the TaosQuery object represents
* @return {TaosQuery}
* @since 1.0.6
*/
function TaosQuery(query = "", cursor = null, execute = false) {
this.query = query;
this._cursor = cursor;
if (execute == true) {
return this.execute();
}
return this;
}
/**
* Executes the query object and returns a Promise
* @memberof TaosQuery
* @return {Promise<TaosResult>} A promise that resolves with a TaosResult object, or rejects with an error
* @since 1.0.6
*/
TaosQuery.prototype.execute = async function execute() {
var taosQuery = this; //store the current instance of taosQuery to avoid async issues?
var executionPromise = new Promise(function(resolve, reject) {
let data = [];
let fields = [];
let result;
try {
taosQuery._cursor.execute(taosQuery.query);
if (taosQuery._cursor._fields) fields = taosQuery._cursor._fields;
if (taosQuery._cursor._result != null) data = taosQuery._cursor.fetchall();
result = new TaosResult(data, fields)
}
catch(err) {
reject(err);
}
resolve(result)
});
return executionPromise;
}
/**
* Executes the query object asynchronously and returns a Promise. Completes query to completion.
* @memberof TaosQuery
* @param {Object} options - Execution options
* @return {Promise<TaosResult>} A promise that resolves with a TaosResult object, or rejects with an error
* @since 1.2.0
*/
TaosQuery.prototype.execute_a = async function execute_a(options = {}) {
var executionPromise = new Promise( (resolve, reject) => {
});
var fres;
var frej;
var fetchPromise = new Promise( (resolve, reject) => {
fres = resolve;
frej = reject;
});
let asyncCallbackFetchall = async function(param, res, numOfRows, blocks) {
if (numOfRows > 0) {
// Likely a query like insert
fres();
}
else {
fres(new TaosResult(blocks.data, blocks.fields));
}
}
let asyncCallback = async function(param, res, code) {
//upon success, we fetchall results
this._cursor.fetchall_a(res, options, asyncCallbackFetchall, {});
}
this._cursor.execute_a(this.query, asyncCallback.bind(this), {});
return fetchPromise;
}
/**
* Bind arguments to the query and automatically parses them into the right format
* @param {array | ...args} args - A number of arguments to bind to each ? in the query
* @return {TaosQuery}
* @example
* // An example of binding a javascript date and a number to a query
* var query = cursor.query("select count(*) from meterinfo.meters where ts <= ? and areaid = ?").bind(new Date(), 3);
* var promise1 = query.execute();
* promise1.then(function(result) {
* result.pretty(); // Log the prettified version of the results.
* });
* @since 1.0.6
*/
TaosQuery.prototype.bind = function bind(f, ...args) {
if (typeof f == 'object' && f.constructor.name != 'Array') args.unshift(f); //param is not an array object
else if (typeof f != 'object') args.unshift(f);
else { args = f; }
args.forEach(function(arg) {
if (arg.constructor.name == 'TaosTimestamp') arg = "\"" + arg.toTaosString() + "\"";
else if (arg.constructor.name == 'Date') arg = "\"" + toTaosTSString(arg) + "\"";
else if (typeof arg == 'string') arg = "\"" + arg + "\"";
this.query = this.query.replace(/\?/,arg);
}, this);
return this;
}
require('./globalfunc.js')
const TaosObjects = require('./taosobjects');
const TaosRow = TaosObjects.TaosRow;
const TaosField = TaosObjects.TaosField;
module.exports = TaosResult;
/**
* @class TaosResult
* @classdesc A TaosResult class consts of the row data and the fields metadata, all wrapped under various objects for higher functionality.
* @param {Array<TaosRow>} data - Array of result rows
* @param {Array<TaosField>} fields - Array of field meta data
* @property {Array<TaosRow>} data - Array of TaosRows forming the result data (this does not include field meta data)
* @property {Array<TaosField>} fields - Array of TaosFields forming the fields meta data array.
* @return {TaosResult}
* @since 1.0.6
*/
function TaosResult(data, fields) {
this.data = data.map(row => new TaosRow(row));
this.rowcount = this.data.length;
this.fields = fields.map(field => new TaosField(field));
}
/**
* Pretty print data and the fields meta data as if you were using the taos shell
* @memberof TaosResult
* @function pretty
* @since 1.0.6
*/
TaosResult.prototype.pretty = function pretty() {
let fieldsStr = "";
let sizing = [];
this.fields.forEach((field, i) => {
if (field._field.type == 8 || field._field.type == 10 ) {
sizing.push(Math.max(field.name.length, field._field.bytes));
}
else {
sizing.push(Math.max(field.name.length, suggestedMinWidths[field._field.type]));
}
fieldsStr += fillEmpty(Math.floor(sizing[i] / 2 - field.name.length / 2)) + field.name + fillEmpty(Math.ceil(sizing[i] / 2 - field.name.length / 2)) + " | ";
});
var sumLengths = sizing.reduce((a, b) => a += b, (0)) + sizing.length * 3;
console.log("\n" + fieldsStr);
console.log(printN("=", sumLengths));
this.data.forEach(row => {
let rowStr = "";
row.data.forEach((entry, i) => {
if (this.fields[i]._field.type == 9) {
entry = entry.toTaosString();
} else {
entry = entry == null ? 'null' : entry.toString();
}
rowStr += entry
rowStr += fillEmpty(sizing[i] - entry.length) + " | ";
});
console.log(rowStr);
});
}
const suggestedMinWidths = {
0: 4,
1: 4,
2: 4,
3: 6,
4: 11,
5: 20,
6: 24,
7: 24,
8: 10,
9: 25,
10: 10,
11: 4,
12: 6,
13: 11,
14: 20,
15: 20,
}
function printN(s, n) {
let f = "";
for (let i = 0; i < n; i++) {
f += s;
}
return f;
}
function fillEmpty(n) {
let str = "";
for (let i = 0; i < n; i++) {
str += " ";
}
return str;
}
{
"name": "td2.0-connector",
"version": "2.0.11",
"description": "A Node.js connector for TDengine.",
"main": "tdengine.js",
"directories": {
"example": "examples",
"test": "test"
},
"scripts": {
"test": "jest",
"catalog": "jest --json"
},
"repository": {
"type": "git",
"url": "git+https://github.com/taosdata/tdengine.git"
},
"keywords": [
"TDengine",
"TAOS Data",
"Time Series Database",
"Connector"
],
"author": "TaosData Inc.",
"license": "AGPL-3.0-or-later",
"bugs": {
"url": "https://github.com/taosdata/tdengine/issues"
},
"homepage": "https://github.com/taosdata/tdengine#readme",
"dependencies": {
"ffi-napi": "^3.1.0",
"lodash": "^4.17.21",
"ref-array-di": "^1.2.1",
"ref-napi": "^3.0.2",
"ref-struct-di": "^1.1.1"
},
"devDependencies": {
"jest": "^27.4.7"
}
}
var TDengineConnection = require('./nodetaos/connection.js')
const TDengineConstant = require('./nodetaos/constants.js')
const TaosBind = require('./nodetaos/taosBind')
const { TaosMultiBind } = require('./nodetaos/taosMultiBind')
const TaosMultiBindArr = require('./nodetaos/taosMultiBindArr')
module.exports = {
connect: function (connection = {}) {
return new TDengineConnection(connection);
},
SCHEMALESS_PROTOCOL: TDengineConstant.SCHEMALESS_PROTOCOL,
SCHEMALESS_PRECISION: TDengineConstant.SCHEMALESS_PRECISION,
TaosBind,
TaosMultiBind,
TaosMultiBindArr,
}
\ No newline at end of file
// const { testMatch } = require('../../jest.config');
const taos = require('../../tdengine');
const { getFeildsFromDll, buildInsertSql, getFieldArr, getResData } = require('../utils/utilTools')
const author = 'xiaolei';
const result = 'passed';
const fileName = __filename.slice(__dirname.length + 1);
// This is a taos connection
let conn;
// This is a Cursor
let c1;
function executeUpdate(sql) {
console.log(sql);
c1.execute(sql);
}
function executeQuery(sql) {
c1.execute(sql, { quiet: true })
var data = c1.fetchall();
let fields = c1.fields;
let resArr = [];
data.forEach(row => {
row.forEach(data => {
if (data instanceof Date) {
// console.log("date obejct:"+data.valueOf());
resArr.push(data.taosTimestamp());
} else {
// console.log("not date:"+data);
resArr.push(data);
}
// console.log(data instanceof Date)
})
})
return { resData: resArr, resFeilds: fields };
}
beforeAll(() => {
conn = taos.connect({ host: "127.0.0.1", user: "root", password: "taosdata", config: "/etc/taos", port: 10 });
c1 = conn.cursor();
executeUpdate("create database if not exists node_test_db keep 3650;");
executeUpdate("use node_test_db;");
});
// Clears the database and adds some testing data.
// Jest will wait for this promise to resolve before running tests.
afterAll(() => {
executeUpdate("drop database if exists node_test_db;");
c1.close();
conn.close();
});
describe("test unsigned type", () => {
test(`name:test unsinged tinnyint ntable;` +
`author:${author};` +
`desc:create,insert,query with unsigned tinnyint;` +
`filename:${fileName};` +
`result:${result}`, () => {
let createSql = "create table if not exists utinnytest(ts timestamp,ut tinyint unsigned,i4 int,rownum nchar(20));";
executeUpdate(createSql);
let expectResField = getFieldArr(getFeildsFromDll(createSql));
let expectResData = [1641827743305, 254, 124, 'row1'
, 1641827743306, 0, -123, 'row2'
, 1641827743307, 54, 0, 'row3'];
let insertSql = buildInsertSql('utinnytest', '', expectResData, [], 4);
executeUpdate(insertSql);
let result = executeQuery("select * from utinnytest;");
let actualResData = result.resData;
let actualResFields = result.resFeilds;
//assert result data length
expect(expectResData.length).toEqual(actualResData.length);
//assert result data
expectResData.forEach((item, index) => {
expect(item).toEqual(actualResData[index]);
});
//assert result meta data
expectResField.forEach((item, index) => {
expect(item).toEqual(actualResFields[index])
})
});
test(`name:test unsinged smallint ntable;` +
`author:${author};` +
`desc:create,insert,query with unsigned smallint;` +
`filename:${fileName};` +
`result:${result}`, () => {
let createSql = "create table if not exists usmalltest(ts timestamp,ut smallint unsigned,i4 int,rownum nchar(20));";
executeUpdate(createSql);
let expectResField = getFieldArr(getFeildsFromDll(createSql));
let expectResData = [1641827743305, 65534, 124, 'row1', 1641827743306, 0, -123, 'row2', 1641827743307, 79, 0, 'row3'];
let insertSql = buildInsertSql('usmalltest', '', expectResData, [], 4);
executeUpdate(insertSql);
let result = executeQuery("select * from usmalltest;");
let actualResData = result.resData;
let actualResFields = result.resFeilds;
//assert result data length
expect(expectResData.length).toEqual(actualResData.length);
//assert result data
expectResData.forEach((item, index) => {
expect(item).toEqual(actualResData[index]);
});
//assert result meta data
expectResField.forEach((item, index) => {
expect(item).toEqual(actualResFields[index])
})
});
test(`name:test unsinged int ntable;` +
`author:${author};` +
`desc:create,insert,query with unsigned int;` +
`filename:${fileName};` +
`result:${result}`, () => {
let createSql = "create table if not exists uinttest(ts timestamp,ui int unsigned,i4 int,rownum nchar(20));";
executeUpdate(createSql);
let expectResField = getFieldArr(getFeildsFromDll(createSql));
let expectResData = [1641827743305, 4294967294, 2147483647, 'row1', 1641827743306, 0, -2147483647, 'row2', 1641827743307, 105, 0, 'row3'];
let insertSql = buildInsertSql('uinttest', '', expectResData, [], 4);
executeUpdate(insertSql);
let result = executeQuery("select * from uinttest;");
let actualResData = result.resData;
let actualResFields = result.resFeilds;
//assert result data length
expect(expectResData.length).toEqual(actualResData.length);
//assert result data
expectResData.forEach((item, index) => {
expect(item).toEqual(actualResData[index]);
});
//assert result meta data
expectResField.forEach((item, index) => {
expect(item).toEqual(actualResFields[index])
})
});
test(`name:test unsinged bigint ntable;` +
`author:${author};` +
`desc:create,insert,query with unsigned bigint;` +
`filename:${fileName};` +
`result:${result}`, () => {
let createSql = "create table if not exists ubigtest(ts timestamp,ui bigint unsigned,i8 bigint,rownum nchar(20));";
executeUpdate(createSql);
let expectResField = getFieldArr(getFeildsFromDll(createSql));
let expectResData = [1641827743305, 18446744073709551614n, 9223372036854775807n, 'row1',
1641827743306, 0n, -9223372036854775807n, 'row2',
1641827743307, 130n, 0n, 'row3'];
let insertSql = buildInsertSql('ubigtest', '', expectResData, [], 4);
executeUpdate(insertSql);
let result = executeQuery("select * from ubigtest;");
let actualResData = result.resData;
let actualResFields = result.resFeilds;
//assert result data length
expect(expectResData.length).toEqual(actualResData.length);
//assert result data
expectResData.forEach((item, index) => {
expect(item).toEqual(actualResData[index]);
});
//assert result meta data
expectResField.forEach((item, index) => {
expect(item).toEqual(actualResFields[index])
})
});
test(`name:test unsinged type ntable;` +
`author:${author};` +
`desc:create,insert,query with mutiple unsinged type;` +
`filename:${fileName};` +
`result:${result}`, () => {
let createSql = "create table if not exists unsigntest(ts timestamp,ut tinyint unsigned,us smallint unsigned,ui int unsigned,ub bigint unsigned,bi bigint);";
executeUpdate(createSql);
let expectResField = getFieldArr(getFeildsFromDll(createSql));
let expectResData = [1641827743305, 254, 65534, 4294967294, 18446744073709551614n, 9223372036854775807n,
1641827743306, 0, 0, 0, 0n, -9223372036854775807n];
let insertSql = buildInsertSql('unsigntest', '', expectResData, [], 6);
executeUpdate(insertSql);
let result = executeQuery("select * from unsigntest;");
// console.log(`result.data:${result.resData}`);
// console.log(`result.feilds:${result.resFeilds}`);
let actualResData = result.resData;
let actualResFields = result.resFeilds;
//assert result data length
expect(expectResData.length).toEqual(actualResData.length);
//assert result data
expectResData.forEach((item, index) => {
expect(item).toEqual(actualResData[index]);
});
//assert result meta data
expectResField.forEach((item, index) => {
expect(item).toEqual(actualResFields[index])
})
});
test(`name:test unsinged type stable max value;` +
`author:${author};` +
`desc:this is a description;` +
`filename:${fileName};` +
`result:${result}`, () => {
let createSql = "create table if not exists max_unsigned_tag_test" +
"(ts timestamp" +
",ut tinyint unsigned" +
",us smallint unsigned" +
",ui int unsigned" +
",ub bigint unsigned" +
",bi bigint)" +
"tags(" +
"ut1 tinyint unsigned" +
",us2 smallint unsigned" +
",ui4 int unsigned" +
",ubi8 bigint unsigned" +
",desc_nchr nchar(200)" +
");";
executeUpdate(createSql);
let expectResField = getFieldArr(getFeildsFromDll(createSql));
let colData = [1641827743305, 254, 65534, 4294967294, 18446744073709551614n, 9223372036854775807n,
1641827743306, 0, 0, 0, 0n, -9223372036854775807n,
1641827743307, 201, 44, 2, 8n, 1531n];
let tagData = [254, 65534, 4294967294, 18446744073709551614n, 'max value of unsinged type tag']
let insertSql = buildInsertSql('max_unsigned_tag_test_sub1', 'max_unsigned_tag_test', colData, tagData, 6);
let expectResData = getResData(colData, tagData, 6);
executeUpdate(insertSql);
let result = executeQuery("select * from max_unsigned_tag_test;");
let actualResData = result.resData;
let actualResFields = result.resFeilds;
//assert result data length
expect(expectResData.length).toEqual(actualResData.length);
//assert result data
expectResData.forEach((item, index) => {
expect(item).toEqual(actualResData[index]);
});
//assert result meta data
expectResField.forEach((item, index) => {
expect(item).toEqual(actualResFields[index])
})
});
test(`name:test unsinged type stable minimal value;` +
`author:${author};` +
`desc:this is a description;` +
`filename:${fileName};` +
`result:${result}`, () => {
let createSql = "create table if not exists min_unsigned_tag_test" +
"(ts timestamp" +
",ut tinyint unsigned" +
",us smallint unsigned" +
",ui int unsigned" +
",ub bigint unsigned" +
",bi bigint)" +
"tags(" +
"ut1 tinyint unsigned" +
",us2 smallint unsigned" +
",ui4 int unsigned" +
",ubi8 bigint unsigned" +
",desc_nchr nchar(200)" +
");";
executeUpdate(createSql);
let expectResField = getFieldArr(getFeildsFromDll(createSql));
let colData = [1641827743305, 254, 65534, 4294967294, 18446744073709551614n, 9223372036854775807n,
1641827743306, 0, 0, 0, 0n, -9223372036854775807n,
1641827743307, 201, 44, 2, 8n, 1531n];
let tagData = [0, 0, 0, 0n, 'minimal value of unsinged type tag']
let insertSql = buildInsertSql('min_unsigned_tag_test_sub1', 'min_unsigned_tag_test', colData, tagData, 6);
let expectResData = getResData(colData, tagData, 6);
executeUpdate(insertSql);
let result = executeQuery("select * from min_unsigned_tag_test;");
let actualResData = result.resData;
let actualResFields = result.resFeilds;
//assert result data length
console.log("expectResData.length:" + expectResData.length + " actualResData.length:" + actualResData.length);
expect(expectResData.length).toEqual(actualResData.length);
//assert result data
expectResData.forEach((item, index) => {
expect(item).toEqual(actualResData[index]);
});
//assert result meta data
expectResField.forEach((item, index) => {
expect(item).toEqual(actualResFields[index])
})
});
test(`name:test unsinged type stable mixed value;` +
`author:${author};` +
`desc:this is a description;` +
`filename:${fileName};` +
`result:${result}`, () => {
let createSql = "create table if not exists mix_unsigned_tag_test" +
"(ts timestamp" +
",ut tinyint unsigned" +
",us smallint unsigned" +
",ui int unsigned" +
",ub bigint unsigned" +
",bi bigint)" +
"tags(" +
"ut1 tinyint unsigned" +
",us2 smallint unsigned" +
",ui4 int unsigned" +
",ubi8 bigint unsigned" +
",desc_nchr nchar(200)" +
");";
executeUpdate(createSql);
let expectResField = getFieldArr(getFeildsFromDll(createSql));
let colData = [1641827743305, 254, 65534, 4294967294, 18446744073709551614n, 9223372036854775807n,
1641827743306, 0, 0, 0, 0n, -9223372036854775807n,
1641827743307, 201, 44, 2, 8n, 1531n];
let tagData = [1, 20, 300, 4000n, 'mixed value of unsinged type tag']
let insertSql = buildInsertSql('mix_unsigned_tag_test_sub1', 'mix_unsigned_tag_test', colData, tagData, 6);
let expectResData = getResData(colData, tagData, 6);
executeUpdate(insertSql);
let result = executeQuery("select * from mix_unsigned_tag_test;");
let actualResData = result.resData;
let actualResFields = result.resFeilds;
//assert result data length
expect(expectResData.length).toEqual(actualResData.length);
//assert result data
expectResData.forEach((item, index) => {
expect(item).toEqual(actualResData[index]);
});
//assert result meta data
expectResField.forEach((item, index) => {
expect(item).toEqual(actualResFields[index])
})
});
})
describe("test cn character", () => {
test(`name:test cn ntable;` +
`author:${author};` +
`desc:create,insert,query with cn characters;` +
`filename:${fileName};` +
`result:${result}`, () => {
createSql = "create table if not exists nchartest(ts timestamp,value int,text binary(200),detail nchar(200));"
executeUpdate(createSql);
let expectResField = getFieldArr(getFeildsFromDll(createSql));
let expectResData = [1641827743305, 1, 'taosdata', 'tdengine'
, 1641827743306, 2, 'tasdata', '涛思数据'
, 1641827743307, 3, '涛思数据', 'tdengine'
, 1641827743308, 4, '涛思数据taosdata', 'tdengine'
, 1641827743309, 5, '涛思数据taosdata', 'tdengine涛思数据'];
let insertSql = buildInsertSql('nchartest', '', expectResData, [], 4);
executeUpdate(insertSql);
let result = executeQuery("select * from nchartest;");
let actualResData = result.resData;
let actualResFields = result.resFeilds;
//assert result data length
expect(expectResData.length).toEqual(actualResData.length);
//assert result data
expectResData.forEach((item, index) => {
expect(item).toEqual(actualResData[index]);
});
//assert result meta data
expectResField.forEach((item, index) => {
expect(item).toEqual(actualResFields[index])
})
})
test(`name:test cn stable;` +
`author:${author};` +
`desc:create,insert,query with cn characters;` +
`filename:${fileName};` +
`result:${result}`, () => {
createSql = "create table if not exists nchartest_s(ts timestamp,value int,text binary(200),detail nchar(200))tags(tag_bi binary(50),tag_nchr nchar(50));"
executeUpdate(createSql);
let expectResField = getFieldArr(getFeildsFromDll(createSql));
let colData = [1641827743305, 1, 'taosdata', 'tdengine'
, 1641827743306, 2, 'tasdata', '涛思数据'
, 1641827743307, 3, '涛思数据', 'tdengine'
, 1641827743308, 4, '涛思数据taosdata', 'tdengine'
, 1641827743309, 5, '涛思数据taosdata', 'tdengine涛思数据'];
let tagData = ['tags涛思', '数据tags'];
let insertSql = buildInsertSql('sb_1', 'nchartest_s', colData, tagData, 4);
let expectResData = getResData(colData, tagData, 4);
executeUpdate(insertSql);
let result = executeQuery("select * from nchartest_s;");
let actualResData = result.resData;
let actualResFields = result.resFeilds;
//assert result data length
expect(expectResData.length).toEqual(actualResData.length);
//assert result data
expectResData.forEach((item, index) => {
expect(item).toEqual(actualResData[index]);
});
//assert result meta data
expectResField.forEach((item, index) => {
expect(item).toEqual(actualResFields[index])
})
})
})
describe("test schemaless", () => {
test(`name:sml line protocal using string;` +
`author:${author};` +
`desc:using line protocal to schemaless insert with a string;` +
`filename:${fileName};` +
`result:${result}`, () => {
let stablename = 'line_protocal_string';
createSql = `create table if not exists ${stablename}(ts timestamp,c1 bigint,c3 nchar(6),c2 bool,c4 double)`
+ `tags(t1 nchar(4),t2 nchar(4),t3 nchar(4));`
executeUpdate(createSql);
let expectResField = getFieldArr(getFeildsFromDll(createSql));
let colData = [1626006833639, 3n, 'passit', false, 4.000000000];
let tagData = ['3i64', '4f64', '\"t3\"'];
let expectResData = getResData(colData, tagData, 5);
let lineStr = stablename + ",t1=3i64,t2=4f64,t3=\"t3\" c1=3i64,c3=L\"passit\",c2=false,c4=4f64 1626006833639";
c1.schemalessInsert(lineStr, taos.SCHEMALESS_PROTOCOL.TSDB_SML_LINE_PROTOCOL, taos.SCHEMALESS_PRECISION.TSDB_SML_TIMESTAMP_MILLI_SECONDS);
let result = executeQuery(`select * from ${stablename};`);
let actualResData = result.resData;
let actualResFields = result.resFeilds;
//assert result data length
expect(expectResData.length).toEqual(actualResData.length);
//assert result data
expectResData.forEach((item, index) => {
expect(item).toEqual(actualResData[index]);
});
//assert result meta data
expectResField.forEach((item, index) => {
expect(item).toEqual(actualResFields[index])
})
})
test(`name:sml line protocal using Array;` +
`author:${author};` +
`desc:using line protocal to schemaless insert with an Array;` +
`filename:${fileName};` +
`result:${result}`, () => {
let stablename = 'line_protocol_arr';
createSql = `create table if not exists ${stablename}(ts timestamp,c1 bigint,c3 nchar(10),c2 bool,c4 double,c5 double)`
+ `tags(t1 nchar(4),t2 nchar(4),t3 nchar(4),t4 nchar(4));`
executeUpdate(createSql);
let expectResField = getFieldArr(getFeildsFromDll(createSql));
let colData1 = [1626006833641, 3n, 'passitagin', true, 5, 5]
let colData2 = [1626006833639, 3n, 'passit', false, 4, null];
let tagData1 = ['4i64', '5f64', '\"t4\"', '5f64'];
let tagData2 = ['3i64', '4f64', '\"t3\"', null];
let expectResDataTable1 = getResData(colData1, tagData1, 6);
let expectResDataTable2 = getResData(colData2, tagData2, 6);
let expectResData = expectResDataTable1.concat(expectResDataTable2);
let lineStr = [stablename + ",t1=3i64,t2=4f64,t3=\"t3\" c1=3i64,c3=L\"passit\",c2=false,c4=4f64 1626006833639000000",
stablename + ",t1=4i64,t3=\"t4\",t2=5f64,t4=5f64 c1=3i64,c3=L\"passitagin\",c2=true,c4=5f64,c5=5f64 1626006833641000000"
];
c1.schemalessInsert(lineStr, taos.SCHEMALESS_PROTOCOL.TSDB_SML_LINE_PROTOCOL, taos.SCHEMALESS_PRECISION.TSDB_SML_TIMESTAMP_NANO_SECONDS);
let result = executeQuery(`select * from ${stablename};`);
let actualResData = result.resData;
let actualResFields = result.resFeilds;
//assert result data length
expect(expectResData.length).toEqual(actualResData.length);
//assert result data
expectResData.forEach((item, index) => {
expect(item).toEqual(actualResData[index]);
});
//assert result meta data
expectResField.forEach((item, index) => {
expect(item).toEqual(actualResFields[index])
})
})
test(`name:sml json protocal using string;` +
`author:${author};` +
`desc:using json protocal to schemaless insert with a json string;` +
`filename:${fileName};` +
`result:${result}`, () => {
let stablename = 'json_protocol_str';
createSql = `create table if not exists ${stablename}(ts timestamp,value double)`
+ `tags(t1 bool,t2 bool,t3 double,t4 nchar(35));`
executeUpdate(createSql);
let expectResField = getFieldArr(getFeildsFromDll(createSql));
let colData1 = [1626006833000, 10]
let tagData1 = [true, false, 10, '123_abc_.!@#$%^&*:;,./?|+-=()[]{}<>'];
let expectResData = getResData(colData1, tagData1, 2);
let jsonStr = "{"
+ "\"metric\": \"" + stablename + "\","
+ "\"timestamp\": 1626006833000,"
+ "\"value\": 10,"
+ "\"tags\": {"
+ " \"t1\": true,"
+ "\"t2\": false,"
+ "\"t3\": 10,"
+ "\"t4\": \"123_abc_.!@#$%^&*:;,./?|+-=()[]{}<>\""
+ "}"
+ "}";
c1.schemalessInsert(jsonStr, taos.SCHEMALESS_PROTOCOL.TSDB_SML_JSON_PROTOCOL, taos.SCHEMALESS_PRECISION.TSDB_SML_TIMESTAMP_MILLI_SECONDS);
let result = executeQuery(`select * from ${stablename};`);
let actualResData = result.resData;
let actualResFields = result.resFeilds;
//assert result data length
expect(expectResData.length).toEqual(actualResData.length);
//assert result data
expectResData.forEach((item, index) => {
expect(item).toEqual(actualResData[index]);
});
//assert result meta data
expectResField.forEach((item, index) => {
expect(item).toEqual(actualResFields[index])
})
})
test(`name:sml json protocal using Array;` +
`author:${author};` +
`desc:using json protocal to schemaless insert with a json array;` +
`filename:${fileName};` +
`result:${result}`, () => {
let stablename = 'json_protocol_arr';
createSql = `create table if not exists ${stablename}(ts timestamp,value double)`
+ `tags(t1 bool,t2 bool,t3 double,t4 nchar(35));`
executeUpdate(createSql);
let expectResField = getFieldArr(getFeildsFromDll(createSql));
let colData1 = [1626006833000, 10]
let tagData1 = [true, false, 10, '123_abc_.!@#$%^&*:;,./?|+-=()[]{}<>'];
let expectResData = getResData(colData1, tagData1, 2);
let jsonArr = ["{"
+ "\"metric\": \"" + stablename + "\","
+ "\"timestamp\": 1626006833,"
+ "\"value\": 10,"
+ "\"tags\": {"
+ " \"t1\": true,"
+ "\"t2\": false,"
+ "\"t3\": 10,"
+ "\"t4\": \"123_abc_.!@#$%^&*:;,./?|+-=()[]{}<>\""
+ "}"
+ "}"
];
c1.schemalessInsert(jsonArr, taos.SCHEMALESS_PROTOCOL.TSDB_SML_JSON_PROTOCOL, taos.SCHEMALESS_PRECISION.TSDB_SML_TIMESTAMP_SECONDS);
let result = executeQuery(`select * from ${stablename};`);
let actualResData = result.resData;
let actualResFields = result.resFeilds;
//assert result data length
expect(expectResData.length).toEqual(actualResData.length);
//assert result data
expectResData.forEach((item, index) => {
expect(item).toEqual(actualResData[index]);
});
//assert result meta data
expectResField.forEach((item, index) => {
expect(item).toEqual(actualResFields[index])
})
})
})
describe("test support microsecond", () => {
test(`name:ms support ntable;` +
`author:${author};` +
`desc:test normal table supports microseconds;` +
`filename:${fileName};` +
`result:${result}`, () => {
let db = 'nodejs_support_ms_ntable';
let table = 'us_test_ntable';
let expectResData = [1625801548423914, 1, 1625801548423914,
1625801548423915, 2, 1625801548423914,
1625801548423916, 3, 1625801548423914,
1625801548423917, 4, 1625801548423914];
let createDB = `create database if not exists ${db} keep 3650 precision \'us\';`;
let createSql = `create table if not exists ${db}.${table} (ts timestamp, seq int,record_date timestamp);`;
let dropDB = `drop database if exists ${db};`;
let insertSql = buildInsertSql(db + '.' + table, '',expectResData, [], 3);
let querySql = `select * from ${db}.${table};`;
let expectResField = getFieldArr(getFeildsFromDll(createSql));
executeUpdate(dropDB);
executeUpdate(createDB);
executeUpdate(createSql);
executeUpdate(insertSql);
let result = executeQuery(querySql);
let actualResData = result.resData;
let actualResFields = result.resFeilds;
//assert result data length
expect(expectResData.length).toEqual(actualResData.length);
//assert result data
expectResData.forEach((item, index) => {
expect(item).toEqual(actualResData[index]);
});
//assert result meta data
expectResField.forEach((item, index) => {
expect(item).toEqual(actualResFields[index])
})
executeUpdate(dropDB);
});
test(`name:ms support stable;` +
`author:${author};` +
`desc:test stable supports microseconds;` +
`filename:${fileName};` +
`result:${result}`, () => {
let db = 'nodejs_support_ms_stable';
let stable = 'us_test_stable';
let table = "sub_1";
let colData = [1625801548423914, 1, 1625801548423914,
1625801548423915, 2, 1625801548423914,
1625801548423916, 3, 1625801548423914,
1625801548423917, 4, 1625801548423914];
let tagData = [1,1625801548423914];
let createDB = `create database if not exists ${db} keep 3650 precision \'us\';`;
let createSql = `create table if not exists ${db}.${stable} (ts timestamp,seq int,`+
`record_date timestamp)tags(id int,htime timestamp);`;
let dropDB = `drop database if exists ${db};`;
let insertSql = buildInsertSql(db + '.' + table, db + '.' + stable, colData,tagData, 3);
let querySql = `select * from ${db}.${stable};`;
let expectResField = getFieldArr(getFeildsFromDll(createSql));
let expectResData = getResData(colData, tagData, 3);
executeUpdate(dropDB);
executeUpdate(createDB);
executeUpdate(createSql);
executeUpdate(insertSql);
let result = executeQuery(querySql);
let actualResData = result.resData;
let actualResFields = result.resFeilds;
//assert result data length
expect(expectResData.length).toEqual(actualResData.length);
//assert result data
expectResData.forEach((item, index) => {
expect(item).toEqual(actualResData[index]);
});
//assert result meta data
expectResField.forEach((item, index) => {
expect(item).toEqual(actualResFields[index])
})
executeUpdate(dropDB);
})
})
describe("test support nanosecond", () => {
test(`name:ns support ntable;` +
`author:${author};` +
`desc:test normal table supports nanoseconds;` +
`filename:${fileName};` +
`result:${result}`, () => {
let db = 'nodejs_support_ns_ntable';
let table = 'ns_test_ntable';
let expectResData = [1625801548423914100, 1, 1625801548423914
,1625801548423914200, 2, 1625801548423914
];
let createDB = `create database if not exists ${db} keep 3650 precision \'ns\';`;
let createSql = `create table if not exists ${db}.${table} (ts timestamp, seq int,record_date timestamp);`;
let dropDB = `drop database if exists ${db};`;
let insertSql = buildInsertSql(db + '.' + table, '',expectResData, [], 3);
let querySql = `select * from ${db}.${table};`;
let expectResField = getFieldArr(getFeildsFromDll(createSql));
executeUpdate(dropDB);
executeUpdate(createDB);
executeUpdate(createSql);
executeUpdate(insertSql);
console.log(querySql);
let result = executeQuery(querySql);
let actualResData = result.resData;
let actualResFields = result.resFeilds;
//assert result data length
expect(expectResData.length).toEqual(actualResData.length);
//assert result data
expectResData.forEach((item, index) => {
console.log((index));
expect(item).toEqual(actualResData[index]);
});
//assert result meta data
expectResField.forEach((item, index) => {
expect(item).toEqual(actualResFields[index])
})
executeUpdate(dropDB);
});
test(`name:ns support stable;` +
`author:${author};` +
`desc:test stable supports nanoseconds;` +
`filename:${fileName};` +
`result:${result}`, () => {
let db = 'nodejs_support_ns_stable';
let stable = 'ns_test_stable';
let table = "sub_1";
let colData = [1625801548423914100, 1, 1625801548423914,
1625801548423914200, 2, 1625801548423914];
let tagData = [1,1625801548423914100];
let createDB = `create database if not exists ${db} keep 3650 precision \'ns\';`;
let createSql = `create table if not exists ${db}.${stable} (ts timestamp,seq int,`+
`record_date timestamp)tags(id int,htime timestamp);`;
let dropDB = `drop database if exists ${db};`;
let insertSql = buildInsertSql(db + '.' + table, db + '.' + stable, colData,tagData, 3);
let querySql = `select * from ${db}.${stable};`;
let expectResField = getFieldArr(getFeildsFromDll(createSql));
let expectResData = getResData(colData, tagData, 3);
executeUpdate(dropDB);
executeUpdate(createDB);
executeUpdate(createSql);
executeUpdate(insertSql);
let result = executeQuery(querySql);
let actualResData = result.resData;
let actualResFields = result.resFeilds;
//assert result data length
expect(expectResData.length).toEqual(actualResData.length);
//assert result data
expectResData.forEach((item, index) => {
expect(item).toEqual(actualResData[index]);
});
//assert result meta data
expectResField.forEach((item, index) => {
expect(item).toEqual(actualResFields[index])
})
executeUpdate(dropDB);
})
})
describe("test json tag", () => {
test(`name:json tag;` +
`author:${author};` +
`desc:create,insert,query with json tag;` +
`filename:${fileName};` +
`result:${result}`, () => {
let tableName = 'jsons1';
let createSql = `create table if not exists ${tableName}(ts timestamp, dataInt int, dataBool bool, dataStr nchar(50), dataStrBin binary(150)) tags(jtag json);`;
executeUpdate(createSql);
let expectResField = getFieldArr(getFeildsFromDll(createSql));
let colData = [1591060618000, 1, false, 'json1', '涛思数据'
, 1591060628000, 23, true, '涛思数据', 'json'
, 1591060638000, 54, false, 'tdengine', 'taosdata'];
let tagData = ['{\"tag1\":\"fff\",\"tag2\":5,\"tag3\":true}']
let insertSql = buildInsertSql('json_sub_1', tableName, colData, tagData, 5);
let expectResData = getResData(colData, tagData, 5);
executeUpdate(insertSql);
let result = executeQuery(`select * from ${tableName};`);
let actualResData = result.resData;
let actualResFields = result.resFeilds;
//assert result data length
expect(expectResData.length).toEqual(actualResData.length);
//assert result data
expectResData.forEach((item, index) => {
expect(item).toEqual(actualResData[index]);
});
//assert result meta data
expectResField.forEach((item, index) => {
expect(item).toEqual(actualResFields[index])
})
})
})
\ No newline at end of file
const taos = require('../../tdengine');
const { getFeildsFromDll, buildInsertSql, getFieldArr, getResData } = require('../utils/utilTools')
const author = 'xiaolei';
const result = 'passed';
const fileName = __filename.slice(__dirname.length + 1);
// This is a taos connection
let conn;
// This is a Cursor
let c1;
// prepare data
let dbName = 'node_test_stmt_db';
let tsArr = [1642435200000, 1642435300000, 1642435400000, 1642435500000, 1642435600000];
let boolArr = [true, false, true, false, null];
let tinyIntArr = [-127, 3, 127, 0, null];
let smallIntArr = [-32767, 16, 32767, 0, null];
let intArr = [-2147483647, 17, 2147483647, 0, null];
let bigIntArr = [-9223372036854775807n, 9223372036854775807n, 18n, 0n, null];
let floatArr = [3.4028234663852886e+38, -3.4028234663852886e+38, 19, 0, null];
let doubleArr = [1.7976931348623157e+308, -1.7976931348623157e+308, 20, 0, null];
let binaryArr = ['TDengine_Binary', 'taosdata涛思数据', '~!@#$%^&*()', '', null];
let ncharArr = ['TDengine_Nchar', 'taosdata涛思数据', '~!@#$$%^&*()', '', null];
let uTinyIntArr = [0, 127, 254, 23, null];
let uSmallIntArr = [0, 256, 65534, 24, null];
let uIntArr = [0, 1233, 4294967294, 25, null];
let uBigIntArr = [0n, 36424354000001111n, 18446744073709551614n, 26n, null];
//prepare tag data.
let tagData1 = [true, 1, 32767, 1234555, -164243520000011111n, 214.02, 2.01, 'taosdata涛思数据', 'TDengine数据', 254, 65534, 4294967290 / 2, 164243520000011111n];
let tagData2 = [true, 2, 32767, 1234555, -164243520000011111n, 214.02, 2.01, 'taosdata涛思数据', 'TDengine数据', 254, 65534, 4294967290 / 2, 164243520000011111n];
let tagData3 = [true, 3, 32767, 1234555, -164243520000011111n, 214.02, 2.01, 'taosdata涛思数据', 'TDengine数据', 254, 65534, 4294967290 / 2, 164243520000011111n];
/**
* Combine individual array of every tdengine type that
* has been declared and then return a new array.
* @returns return data array.
*/
function getBindData() {
let bindDataArr = [];
for (let i = 0; i < 5; i++) {
bindDataArr.push(tsArr[i]);
bindDataArr.push(boolArr[i]);
bindDataArr.push(tinyIntArr[i]);
bindDataArr.push(smallIntArr[i]);
bindDataArr.push(intArr[i]);
bindDataArr.push(bigIntArr[i]);
bindDataArr.push(floatArr[i]);
bindDataArr.push(doubleArr[i]);
bindDataArr.push(binaryArr[i]);
bindDataArr.push(ncharArr[i]);
bindDataArr.push(uTinyIntArr[i]);
bindDataArr.push(uSmallIntArr[i]);
bindDataArr.push(uIntArr[i]);
bindDataArr.push(uBigIntArr[i]);
}
return bindDataArr;
}
function executeUpdate(sql) {
console.log(sql);
c1.execute(sql);
}
function executeQuery(sql) {
c1.execute(sql, { quiet: true })
var data = c1.fetchall();
let fields = c1.fields;
let resArr = [];
data.forEach(row => {
row.forEach(data => {
if (data instanceof Date) {
// console.log("date obejct:"+data.valueOf());
resArr.push(data.taosTimestamp());
} else {
// console.log("not date:"+data);
resArr.push(data);
}
// console.log(data instanceof Date)
})
})
return { resData: resArr, resFeilds: fields };
}
beforeAll(() => {
conn = taos.connect({ host: "127.0.0.1", user: "root", password: "taosdata", config: "/etc/taos", port: 10 });
c1 = conn.cursor();
executeUpdate(`create database if not exists ${dbName} keep 3650;`);
executeUpdate(`use ${dbName};`);
});
// Clears the database and adds some testing data.
// Jest will wait for this promise to resolve before running tests.
afterAll(() => {
executeUpdate(`drop database if exists ${dbName};`);
c1.close();
conn.close();
});
describe("stmt_bind_single_param", () => {
test(`name:bindSingleParamWithOneTable;` +
`author:${author};` +
`desc:Using stmtBindSingleParam() bind one table in a batch;` +
`filename:${fileName};` +
`result:${result}`, () => {
let table = 'bindsingleparambatch_121';
let createSql = `create table if not exists ${table} ` +
`(ts timestamp,` +
`bl bool,` +
`i8 tinyint,` +
`i16 smallint,` +
`i32 int,` +
`i64 bigint,` +
`f32 float,` +
`d64 double,` +
`bnr binary(20),` +
`nchr nchar(20),` +
`u8 tinyint unsigned,` +
`u16 smallint unsigned,` +
`u32 int unsigned,` +
`u64 bigint unsigned` +
`)tags(` +
`t_bl bool,` +
`t_i8 tinyint,` +
`t_i16 smallint,` +
`t_i32 int,` +
`t_i64 bigint,` +
`t_f32 float,` +
`t_d64 double,` +
`t_bnr binary(20),` +
`t_nchr nchar(20),` +
`t_u8 tinyint unsigned,` +
`t_u16 smallint unsigned,` +
`t_u32 int unsigned,` +
`t_u64 bigint unsigned` +
`);`;
let insertSql = `insert into ? using ${table} tags(?,?,?,?,?,?,?,?,?,?,?,?,?) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?);`;
let querySql = `select * from ${table}`;
let expectResField = getFieldArr(getFeildsFromDll(createSql));
let expectResData = getResData(getBindData(), tagData1, 14);
// prepare tag TAOS_BIND
let tagBind1 = new taos.TaosBind(14);
tagBind1.bindBool(true);
tagBind1.bindTinyInt(1);
tagBind1.bindSmallInt(32767);
tagBind1.bindInt(1234555);
tagBind1.bindBigInt(-164243520000011111n);
tagBind1.bindFloat(214.02);
tagBind1.bindDouble(2.01);
tagBind1.bindBinary('taosdata涛思数据');
tagBind1.bindNchar('TDengine数据');
tagBind1.bindUTinyInt(254);
tagBind1.bindUSmallInt(65534);
tagBind1.bindUInt(4294967290 / 2);
tagBind1.bindUBigInt(164243520000011111n);
//Prepare TAOS_MULTI_BIND data
let mBind1 = new taos.TaosMultiBind();
executeUpdate(createSql);
c1.stmtInit();
c1.stmtPrepare(insertSql);
c1.stmtSetTbnameTags(`${table}_s01`, tagBind1.getBind());
c1.stmtBindSingleParamBatch(mBind1.multiBindTimestamp(tsArr), 0);
c1.stmtBindSingleParamBatch(mBind1.multiBindBool(boolArr), 1);
c1.stmtBindSingleParamBatch(mBind1.multiBindTinyInt(tinyIntArr), 2);
c1.stmtBindSingleParamBatch(mBind1.multiBindSmallInt(smallIntArr), 3);
c1.stmtBindSingleParamBatch(mBind1.multiBindInt(intArr), 4);
c1.stmtBindSingleParamBatch(mBind1.multiBindBigInt(bigIntArr), 5);
c1.stmtBindSingleParamBatch(mBind1.multiBindFloat(floatArr), 6);
c1.stmtBindSingleParamBatch(mBind1.multiBindDouble(doubleArr), 7);
c1.stmtBindSingleParamBatch(mBind1.multiBindBinary(binaryArr), 8);
c1.stmtBindSingleParamBatch(mBind1.multiBindNchar(ncharArr), 9);
c1.stmtBindSingleParamBatch(mBind1.multiBindUTinyInt(uTinyIntArr), 10);
c1.stmtBindSingleParamBatch(mBind1.multiBindUSmallInt(uSmallIntArr), 11);
c1.stmtBindSingleParamBatch(mBind1.multiBindUInt(uIntArr), 12);
c1.stmtBindSingleParamBatch(mBind1.multiBindUBigInt(uBigIntArr), 13);
c1.stmtAddBatch();
c1.stmtExecute();
c1.stmtClose();
let result = executeQuery(querySql);
let actualResData = result.resData;
let actualResFields = result.resFeilds;
//assert result data length
expect(expectResData.length).toEqual(actualResData.length);
//assert result data
expectResData.forEach((item, index) => {
expect(item).toEqual(actualResData[index]);
});
//assert result meta data
expectResField.forEach((item, index) => {
expect(item).toEqual(actualResFields[index])
})
});
test(`name:bindSingleParamWithMultiTable;` +
`author:${author};` +
`desc:Using stmtBindSingleParam() bind multiple tables in a batch;` +
`filename:${fileName};` +
`result:${result}`, () => {
let table = 'bindsingleparambatch_m21';//bind multiple table to one batch
let createSql = `create table if not exists ${table} ` +
`(ts timestamp,` +
`bl bool,` +
`i8 tinyint,` +
`i16 smallint,` +
`i32 int,` +
`i64 bigint,` +
`f32 float,` +
`d64 double,` +
`bnr binary(20),` +
`nchr nchar(20),` +
`u8 tinyint unsigned,` +
`u16 smallint unsigned,` +
`u32 int unsigned,` +
`u64 bigint unsigned` +
`)tags(` +
`t_bl bool,` +
`t_i8 tinyint,` +
`t_i16 smallint,` +
`t_i32 int,` +
`t_i64 bigint,` +
`t_f32 float,` +
`t_d64 double,` +
`t_bnr binary(20),` +
`t_nchr nchar(20),` +
`t_u8 tinyint unsigned,` +
`t_u16 smallint unsigned,` +
`t_u32 int unsigned,` +
`t_u64 bigint unsigned` +
`);`;
let insertSql = `insert into ? using ${table} tags(?,?,?,?,?,?,?,?,?,?,?,?,?) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?);`;
let querySql = `select * from ${table}`;
let expectResField = getFieldArr(getFeildsFromDll(createSql));
let expectResData = getResData(getBindData(), tagData1, 14).concat(getResData(getBindData(), tagData2, 14)).concat(getResData(getBindData(), tagData3, 14));
// prepare tag TAOS_BIND
let tagBind1 = new taos.TaosBind(14);
tagBind1.bindBool(true);
tagBind1.bindTinyInt(1);
tagBind1.bindSmallInt(32767);
tagBind1.bindInt(1234555);
tagBind1.bindBigInt(-164243520000011111n);
tagBind1.bindFloat(214.02);
tagBind1.bindDouble(2.01);
tagBind1.bindBinary('taosdata涛思数据');
tagBind1.bindNchar('TDengine数据');
tagBind1.bindUTinyInt(254);
tagBind1.bindUSmallInt(65534);
tagBind1.bindUInt(4294967290 / 2);
tagBind1.bindUBigInt(164243520000011111n);
let tagBind2 = new taos.TaosBind(14);
tagBind2.bindBool(true);
tagBind2.bindTinyInt(2);
tagBind2.bindSmallInt(32767);
tagBind2.bindInt(1234555);
tagBind2.bindBigInt(-164243520000011111n);
tagBind2.bindFloat(214.02);
tagBind2.bindDouble(2.01);
tagBind2.bindBinary('taosdata涛思数据');
tagBind2.bindNchar('TDengine数据');
tagBind2.bindUTinyInt(254);
tagBind2.bindUSmallInt(65534);
tagBind2.bindUInt(4294967290 / 2);
tagBind2.bindUBigInt(164243520000011111n);
let tagBind3 = new taos.TaosBind(14);
tagBind3.bindBool(true);
tagBind3.bindTinyInt(3);
tagBind3.bindSmallInt(32767);
tagBind3.bindInt(1234555);
tagBind3.bindBigInt(-164243520000011111n);
tagBind3.bindFloat(214.02);
tagBind3.bindDouble(2.01);
tagBind3.bindBinary('taosdata涛思数据');
tagBind3.bindNchar('TDengine数据');
tagBind3.bindUTinyInt(254);
tagBind3.bindUSmallInt(65534);
tagBind3.bindUInt(4294967290 / 2);
tagBind3.bindUBigInt(164243520000011111n);
//Prepare TAOS_MULTI_BIND data
let mBind = new taos.TaosMultiBind();
executeUpdate(createSql);
c1.stmtInit();
c1.stmtPrepare(insertSql);
// ========bind for 1st table =============
c1.stmtSetTbnameTags(`${table}_s01`, tagBind1.getBind());
c1.stmtBindSingleParamBatch(mBind.multiBindTimestamp(tsArr), 0);
c1.stmtBindSingleParamBatch(mBind.multiBindBool(boolArr), 1);
c1.stmtBindSingleParamBatch(mBind.multiBindTinyInt(tinyIntArr), 2);
c1.stmtBindSingleParamBatch(mBind.multiBindSmallInt(smallIntArr), 3);
c1.stmtBindSingleParamBatch(mBind.multiBindInt(intArr), 4);
c1.stmtBindSingleParamBatch(mBind.multiBindBigInt(bigIntArr), 5);
c1.stmtBindSingleParamBatch(mBind.multiBindFloat(floatArr), 6);
c1.stmtBindSingleParamBatch(mBind.multiBindDouble(doubleArr), 7);
c1.stmtBindSingleParamBatch(mBind.multiBindBinary(binaryArr), 8);
c1.stmtBindSingleParamBatch(mBind.multiBindNchar(ncharArr), 9);
c1.stmtBindSingleParamBatch(mBind.multiBindUTinyInt(uTinyIntArr), 10);
c1.stmtBindSingleParamBatch(mBind.multiBindUSmallInt(uSmallIntArr), 11);
c1.stmtBindSingleParamBatch(mBind.multiBindUInt(uIntArr), 12);
c1.stmtBindSingleParamBatch(mBind.multiBindUBigInt(uBigIntArr), 13);
c1.stmtAddBatch();
// c1.stmtExecute();
// ========bind for 2nd table =============
c1.stmtSetTbnameTags(`${table}_s02`, tagBind2.getBind());
c1.stmtBindSingleParamBatch(mBind.multiBindTimestamp(tsArr), 0);
c1.stmtBindSingleParamBatch(mBind.multiBindBool(boolArr), 1);
c1.stmtBindSingleParamBatch(mBind.multiBindTinyInt(tinyIntArr), 2);
c1.stmtBindSingleParamBatch(mBind.multiBindSmallInt(smallIntArr), 3);
c1.stmtBindSingleParamBatch(mBind.multiBindInt(intArr), 4);
c1.stmtBindSingleParamBatch(mBind.multiBindBigInt(bigIntArr), 5);
c1.stmtBindSingleParamBatch(mBind.multiBindFloat(floatArr), 6);
c1.stmtBindSingleParamBatch(mBind.multiBindDouble(doubleArr), 7);
c1.stmtBindSingleParamBatch(mBind.multiBindBinary(binaryArr), 8);
c1.stmtBindSingleParamBatch(mBind.multiBindNchar(ncharArr), 9);
c1.stmtBindSingleParamBatch(mBind.multiBindUTinyInt(uTinyIntArr), 10);
c1.stmtBindSingleParamBatch(mBind.multiBindUSmallInt(uSmallIntArr), 11);
c1.stmtBindSingleParamBatch(mBind.multiBindUInt(uIntArr), 12);
c1.stmtBindSingleParamBatch(mBind.multiBindUBigInt(uBigIntArr), 13);
c1.stmtAddBatch();
// c1.stmtExecute();
// ========bind for 3rd table =============
c1.stmtSetTbnameTags(`${table}_s0`, tagBind3.getBind());
c1.stmtBindSingleParamBatch(mBind.multiBindTimestamp(tsArr), 0);
c1.stmtBindSingleParamBatch(mBind.multiBindBool(boolArr), 1);
c1.stmtBindSingleParamBatch(mBind.multiBindTinyInt(tinyIntArr), 2);
c1.stmtBindSingleParamBatch(mBind.multiBindSmallInt(smallIntArr), 3);
c1.stmtBindSingleParamBatch(mBind.multiBindInt(intArr), 4);
c1.stmtBindSingleParamBatch(mBind.multiBindBigInt(bigIntArr), 5);
c1.stmtBindSingleParamBatch(mBind.multiBindFloat(floatArr), 6);
c1.stmtBindSingleParamBatch(mBind.multiBindDouble(doubleArr), 7);
c1.stmtBindSingleParamBatch(mBind.multiBindBinary(binaryArr), 8);
c1.stmtBindSingleParamBatch(mBind.multiBindNchar(ncharArr), 9);
c1.stmtBindSingleParamBatch(mBind.multiBindUTinyInt(uTinyIntArr), 10);
c1.stmtBindSingleParamBatch(mBind.multiBindUSmallInt(uSmallIntArr), 11);
c1.stmtBindSingleParamBatch(mBind.multiBindUInt(uIntArr), 12);
c1.stmtBindSingleParamBatch(mBind.multiBindUBigInt(uBigIntArr), 13);
c1.stmtAddBatch();
c1.stmtExecute();
c1.stmtClose();
let result = executeQuery(querySql);
let actualResData = result.resData;
let actualResFields = result.resFeilds;
//assert result data length
expect(expectResData.length).toEqual(actualResData.length);
//assert result data
expectResData.forEach((item, index) => {
expect(item).toEqual(actualResData[index]);
});
//assert result meta data
expectResField.forEach((item, index) => {
expect(item).toEqual(actualResFields[index])
})
});
})
describe("stmt_bind_para_batch", () => {
test(`name:bindParamBatchWithOneTable;` +
`author:${author};` +
`desc:Using stmtBindParamBatch() bind one table in a batch;` +
`filename:${fileName};` +
`result:${result}`, () => {
let table = 'bindparambatch_121';//bind one table to one batch
let createSql = `create table if not exists ${table} ` +
`(ts timestamp,` +
`bl bool,` +
`i8 tinyint,` +
`i16 smallint,` +
`i32 int,` +
`i64 bigint,` +
`f32 float,` +
`d64 double,` +
`bnr binary(20),` +
`nchr nchar(20),` +
`u8 tinyint unsigned,` +
`u16 smallint unsigned,` +
`u32 int unsigned,` +
`u64 bigint unsigned` +
`)tags(` +
`t_bl bool,` +
`t_i8 tinyint,` +
`t_i16 smallint,` +
`t_i32 int,` +
`t_i64 bigint,` +
`t_f32 float,` +
`t_d64 double,` +
`t_bnr binary(20),` +
`t_nchr nchar(20),` +
`t_u8 tinyint unsigned,` +
`t_u16 smallint unsigned,` +
`t_u32 int unsigned,` +
`t_u64 bigint unsigned` +
`);`;
let insertSql = `insert into ? using ${table} tags(?,?,?,?,?,?,?,?,?,?,?,?,?) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?);`;
let querySql = `select * from ${table}`;
let expectResField = getFieldArr(getFeildsFromDll(createSql));
let expectResData = getResData(getBindData(), tagData1, 14);
//prepare tag TAO_BIND
let tagBind = new taos.TaosBind(14);
tagBind.bindBool(true);
tagBind.bindTinyInt(1);
tagBind.bindSmallInt(32767);
tagBind.bindInt(1234555);
tagBind.bindBigInt(-164243520000011111n);
tagBind.bindFloat(214.02);
tagBind.bindDouble(2.01);
tagBind.bindBinary('taosdata涛思数据');
tagBind.bindNchar('TDengine数据');
tagBind.bindUTinyInt(254);
tagBind.bindUSmallInt(65534);
tagBind.bindUInt(4294967290 / 2);
tagBind.bindUBigInt(164243520000011111n);
//Prepare TAOS_MULTI_BIND data array
let mBinds = new taos.TaosMultiBindArr(14);
mBinds.multiBindTimestamp(tsArr);
mBinds.multiBindBool(boolArr);
mBinds.multiBindTinyInt(tinyIntArr);
mBinds.multiBindSmallInt(smallIntArr);
mBinds.multiBindInt(intArr);
mBinds.multiBindBigInt(bigIntArr);
mBinds.multiBindFloat(floatArr);
mBinds.multiBindDouble(doubleArr);
mBinds.multiBindBinary(binaryArr);
mBinds.multiBindNchar(ncharArr);
mBinds.multiBindUTinyInt(uTinyIntArr);
mBinds.multiBindUSmallInt(uSmallIntArr);
mBinds.multiBindUInt(uIntArr);
mBinds.multiBindUBigInt(uBigIntArr);
executeUpdate(createSql);
c1.stmtInit();
c1.stmtPrepare(insertSql);
c1.stmtSetTbnameTags(`${table}_s01`, tagBind.getBind());
c1.stmtBindParamBatch(mBinds.getMultiBindArr());
c1.stmtAddBatch();
c1.stmtExecute();
c1.stmtClose();
let result = executeQuery(querySql);
let actualResData = result.resData;
let actualResFields = result.resFeilds;
//assert result data length
expect(expectResData.length).toEqual(actualResData.length);
//assert result data
expectResData.forEach((item, index) => {
expect(item).toEqual(actualResData[index]);
});
//assert result meta data
expectResField.forEach((item, index) => {
expect(item).toEqual(actualResFields[index])
})
});
test(`name:bindParamBatchWithMultiTable;` +
`author:${author};` +
`desc:Using stmtBindParamBatch() bind multiple tables in a batch;` +
`filename:${fileName};` +
`result:${result}`, () => {
let table = 'bindparambatch_m21';//bind multiple tables to one batch
let createSql = `create table if not exists ${table} ` +
`(ts timestamp,` +
`bl bool,` +
`i8 tinyint,` +
`i16 smallint,` +
`i32 int,` +
`i64 bigint,` +
`f32 float,` +
`d64 double,` +
`bnr binary(20),` +
`nchr nchar(20),` +
`u8 tinyint unsigned,` +
`u16 smallint unsigned,` +
`u32 int unsigned,` +
`u64 bigint unsigned` +
`)tags(` +
`t_bl bool,` +
`t_i8 tinyint,` +
`t_i16 smallint,` +
`t_i32 int,` +
`t_i64 bigint,` +
`t_f32 float,` +
`t_d64 double,` +
`t_bnr binary(20),` +
`t_nchr nchar(20),` +
`t_u8 tinyint unsigned,` +
`t_u16 smallint unsigned,` +
`t_u32 int unsigned,` +
`t_u64 bigint unsigned` +
`);`;
let insertSql = `insert into ? using ${table} tags(?,?,?,?,?,?,?,?,?,?,?,?,?) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?);`;
let querySql = `select * from ${table}`;
let expectResField = getFieldArr(getFeildsFromDll(createSql));
let expectResData = getResData(getBindData(), tagData1, 14).concat(getResData(getBindData(), tagData2, 14)).concat(getResData(getBindData(), tagData3, 14));
// prepare tag TAOS_BIND
let tagBind1 = new taos.TaosBind(14);
tagBind1.bindBool(true);
tagBind1.bindTinyInt(1);
tagBind1.bindSmallInt(32767);
tagBind1.bindInt(1234555);
tagBind1.bindBigInt(-164243520000011111n);
tagBind1.bindFloat(214.02);
tagBind1.bindDouble(2.01);
tagBind1.bindBinary('taosdata涛思数据');
tagBind1.bindNchar('TDengine数据');
tagBind1.bindUTinyInt(254);
tagBind1.bindUSmallInt(65534);
tagBind1.bindUInt(4294967290 / 2);
tagBind1.bindUBigInt(164243520000011111n);
let tagBind2 = new taos.TaosBind(14);
tagBind2.bindBool(true);
tagBind2.bindTinyInt(2);
tagBind2.bindSmallInt(32767);
tagBind2.bindInt(1234555);
tagBind2.bindBigInt(-164243520000011111n);
tagBind2.bindFloat(214.02);
tagBind2.bindDouble(2.01);
tagBind2.bindBinary('taosdata涛思数据');
tagBind2.bindNchar('TDengine数据');
tagBind2.bindUTinyInt(254);
tagBind2.bindUSmallInt(65534);
tagBind2.bindUInt(4294967290 / 2);
tagBind2.bindUBigInt(164243520000011111n);
let tagBind3 = new taos.TaosBind(14);
tagBind3.bindBool(true);
tagBind3.bindTinyInt(3);
tagBind3.bindSmallInt(32767);
tagBind3.bindInt(1234555);
tagBind3.bindBigInt(-164243520000011111n);
tagBind3.bindFloat(214.02);
tagBind3.bindDouble(2.01);
tagBind3.bindBinary('taosdata涛思数据');
tagBind3.bindNchar('TDengine数据');
tagBind3.bindUTinyInt(254);
tagBind3.bindUSmallInt(65534);
tagBind3.bindUInt(4294967290 / 2);
tagBind3.bindUBigInt(164243520000011111n);
//Prepare TAOS_MULTI_BIND data array
let mBinds = new taos.TaosMultiBindArr(14);
mBinds.multiBindTimestamp(tsArr);
mBinds.multiBindBool(boolArr);
mBinds.multiBindTinyInt(tinyIntArr);
mBinds.multiBindSmallInt(smallIntArr);
mBinds.multiBindInt(intArr);
mBinds.multiBindBigInt(bigIntArr);
mBinds.multiBindFloat(floatArr);
mBinds.multiBindDouble(doubleArr);
mBinds.multiBindBinary(binaryArr);
mBinds.multiBindNchar(ncharArr);
mBinds.multiBindUTinyInt(uTinyIntArr);
mBinds.multiBindUSmallInt(uSmallIntArr);
mBinds.multiBindUInt(uIntArr);
mBinds.multiBindUBigInt(uBigIntArr);
executeUpdate(createSql);
c1.stmtInit();
c1.stmtPrepare(insertSql);
// ===========bind for 1st table ==========
c1.stmtSetTbnameTags(`${table}_s01`, tagBind1.getBind());
c1.stmtBindParamBatch(mBinds.getMultiBindArr());
c1.stmtAddBatch();
// c1.stmtExecute();
// ===========bind for 2nd table ==========
c1.stmtSetTbnameTags(`${table}_s02`, tagBind2.getBind());
c1.stmtBindParamBatch(mBinds.getMultiBindArr());
c1.stmtAddBatch();
// c1.stmtExecute();
// ===========bind for 3rd table ==========
c1.stmtSetTbnameTags(`${table}_s03`, tagBind3.getBind());
c1.stmtBindParamBatch(mBinds.getMultiBindArr());
c1.stmtAddBatch();
c1.stmtExecute();
c1.stmtClose();
let result = executeQuery(querySql);
let actualResData = result.resData;
let actualResFields = result.resFeilds;
//assert result data length
expect(expectResData.length).toEqual(actualResData.length);
//assert result data
expectResData.forEach((item, index) => {
expect(item).toEqual(actualResData[index]);
});
//assert result meta data
expectResField.forEach((item, index) => {
expect(item).toEqual(actualResFields[index])
})
});
})
describe("stmt_bind_param", () => {
test(`name:bindParamWithOneTable;` +
`author:${author};` +
`desc:using stmtBindParam() bind one table in a batch;` +
`filename:${fileName};` +
`result:${result}`, () => {
let table = 'bindparam_121';//bind one table to one batch
let createSql = `create table if not exists ${table} ` +
`(ts timestamp,` +
`bl bool,` +
`i8 tinyint,` +
`i16 smallint,` +
`i32 int,` +
`i64 bigint,` +
`f32 float,` +
`d64 double,` +
`bnr binary(20),` +
`nchr nchar(20),` +
`u8 tinyint unsigned,` +
`u16 smallint unsigned,` +
`u32 int unsigned,` +
`u64 bigint unsigned` +
`)tags(` +
`t_bl bool,` +
`t_i8 tinyint,` +
`t_i16 smallint,` +
`t_i32 int,` +
`t_i64 bigint,` +
`t_f32 float,` +
`t_d64 double,` +
`t_bnr binary(20),` +
`t_nchr nchar(20),` +
`t_u8 tinyint unsigned,` +
`t_u16 smallint unsigned,` +
`t_u32 int unsigned,` +
`t_u64 bigint unsigned` +
`);`;
let insertSql = `insert into ? using ${table} tags(?,?,?,?,?,?,?,?,?,?,?,?,?) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?);`;
let querySql = `select * from ${table}`;
let expectResField = getFieldArr(getFeildsFromDll(createSql));
let data = getBindData();
let expectResData = getResData(data, tagData1, 14);
//prepare tag data
let tags = new taos.TaosBind(13);
tags.bindBool(true);
tags.bindTinyInt(1);
tags.bindSmallInt(32767);
tags.bindInt(1234555);
tags.bindBigInt(-164243520000011111n);
tags.bindFloat(214.02);
tags.bindDouble(2.01);
tags.bindBinary('taosdata涛思数据');
tags.bindNchar('TDengine数据');
tags.bindUTinyInt(254);
tags.bindUSmallInt(65534);
tags.bindUInt(4294967290 / 2);
tags.bindUBigInt(164243520000011111n);
executeUpdate(createSql);
c1.stmtInit();
c1.stmtPrepare(insertSql);
c1.stmtSetTbnameTags(`${table}_s01`, tags.getBind());
for (let i = 0; i < data.length - 14; i += 14) {
let bind = new taos.TaosBind(14);
bind.bindTimestamp(data[i]);
bind.bindBool(data[i + 1]);
bind.bindTinyInt(data[i + 2]);
bind.bindSmallInt(data[i + 3]);
bind.bindInt(data[i + 4]);
bind.bindBigInt(data[i + 5]);
bind.bindFloat(data[i + 6]);
bind.bindDouble(data[i + 7]);
bind.bindBinary(data[i + 8]);
bind.bindNchar(data[i + 9]);
bind.bindUTinyInt(data[i + 10]);
bind.bindUSmallInt(data[i + 11]);
bind.bindUInt(data[i + 12]);
bind.bindUBigInt(data[i + 13]);
c1.stmtBindParam(bind.getBind());
c1.stmtAddBatch();
}
let bind2 = new taos.TaosBind(14);
bind2.bindTimestamp(data[14 * 4]);
for (let j = 0; j < 13; j++) {
bind2.bindNil();
}
c1.stmtBindParam(bind2.getBind());
c1.stmtAddBatch();
c1.stmtExecute();
c1.stmtClose();
let result = executeQuery(querySql);
let actualResData = result.resData;
let actualResFields = result.resFeilds;
//assert result data length
expect(expectResData.length).toEqual(actualResData.length);
//assert result data
expectResData.forEach((item, index) => {
expect(item).toEqual(actualResData[index]);
});
//assert result meta data
expectResField.forEach((item, index) => {
expect(item).toEqual(actualResFields[index])
})
});
test(`name:bindParamWithMultiTable;` +
`author:${author};` +
`desc:using stmtBindParam() bind multiple tables in a batch;` +
`filename:${fileName};` +
`result:${result}`, () => {
let table = 'bindparam_m21';//bind one table to one batch
let createSql = `create table if not exists ${table} ` +
`(ts timestamp,` +
`bl bool,` +
`i8 tinyint,` +
`i16 smallint,` +
`i32 int,` +
`i64 bigint,` +
`f32 float,` +
`d64 double,` +
`bnr binary(20),` +
`nchr nchar(20),` +
`u8 tinyint unsigned,` +
`u16 smallint unsigned,` +
`u32 int unsigned,` +
`u64 bigint unsigned` +
`)tags(` +
`t_bl bool,` +
`t_i8 tinyint,` +
`t_i16 smallint,` +
`t_i32 int,` +
`t_i64 bigint,` +
`t_f32 float,` +
`t_d64 double,` +
`t_bnr binary(20),` +
`t_nchr nchar(20),` +
`t_u8 tinyint unsigned,` +
`t_u16 smallint unsigned,` +
`t_u32 int unsigned,` +
`t_u64 bigint unsigned` +
`);`;
let insertSql = `insert into ? using ${table} tags(?,?,?,?,?,?,?,?,?,?,?,?,?) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?);`;
let querySql = `select * from ${table}`;
let expectResField = getFieldArr(getFeildsFromDll(createSql));
let data = getBindData();
let expectResData = getResData(data, tagData1, 14).concat(getResData(data, tagData2, 14)).concat(getResData(data, tagData3, 14));
// prepare tag TAOS_BIND
let tagBind1 = new taos.TaosBind(14);
tagBind1.bindBool(true);
tagBind1.bindTinyInt(1);
tagBind1.bindSmallInt(32767);
tagBind1.bindInt(1234555);
tagBind1.bindBigInt(-164243520000011111n);
tagBind1.bindFloat(214.02);
tagBind1.bindDouble(2.01);
tagBind1.bindBinary('taosdata涛思数据');
tagBind1.bindNchar('TDengine数据');
tagBind1.bindUTinyInt(254);
tagBind1.bindUSmallInt(65534);
tagBind1.bindUInt(4294967290 / 2);
tagBind1.bindUBigInt(164243520000011111n);
let tagBind2 = new taos.TaosBind(14);
tagBind2.bindBool(true);
tagBind2.bindTinyInt(2);
tagBind2.bindSmallInt(32767);
tagBind2.bindInt(1234555);
tagBind2.bindBigInt(-164243520000011111n);
tagBind2.bindFloat(214.02);
tagBind2.bindDouble(2.01);
tagBind2.bindBinary('taosdata涛思数据');
tagBind2.bindNchar('TDengine数据');
tagBind2.bindUTinyInt(254);
tagBind2.bindUSmallInt(65534);
tagBind2.bindUInt(4294967290 / 2);
tagBind2.bindUBigInt(164243520000011111n);
let tagBind3 = new taos.TaosBind(14);
tagBind3.bindBool(true);
tagBind3.bindTinyInt(3);
tagBind3.bindSmallInt(32767);
tagBind3.bindInt(1234555);
tagBind3.bindBigInt(-164243520000011111n);
tagBind3.bindFloat(214.02);
tagBind3.bindDouble(2.01);
tagBind3.bindBinary('taosdata涛思数据');
tagBind3.bindNchar('TDengine数据');
tagBind3.bindUTinyInt(254);
tagBind3.bindUSmallInt(65534);
tagBind3.bindUInt(4294967290 / 2);
tagBind3.bindUBigInt(164243520000011111n);
executeUpdate(createSql);
c1.stmtInit();
c1.stmtPrepare(insertSql);
// ========= bind for 1st table =================
c1.stmtSetTbnameTags(`${table}_s01`, tagBind1.getBind());
for (let i = 0; i < data.length - 14; i += 14) {
let bind = new taos.TaosBind(14);
bind.bindTimestamp(data[i]);
bind.bindBool(data[i + 1]);
bind.bindTinyInt(data[i + 2]);
bind.bindSmallInt(data[i + 3]);
bind.bindInt(data[i + 4]);
bind.bindBigInt(data[i + 5]);
bind.bindFloat(data[i + 6]);
bind.bindDouble(data[i + 7]);
bind.bindBinary(data[i + 8]);
bind.bindNchar(data[i + 9]);
bind.bindUTinyInt(data[i + 10]);
bind.bindUSmallInt(data[i + 11]);
bind.bindUInt(data[i + 12]);
bind.bindUBigInt(data[i + 13]);
c1.stmtBindParam(bind.getBind());
c1.stmtAddBatch();
}
let bind2 = new taos.TaosBind(14);
bind2.bindTimestamp(data[14 * 4]);
for (let j = 0; j < 13; j++) {
bind2.bindNil();
}
c1.stmtBindParam(bind2.getBind());
c1.stmtAddBatch();
// c1.stmtExecute();
// ========= bind for 2nd table =================
c1.stmtSetTbnameTags(`${table}_s02`, tagBind2.getBind());
for (let i = 0; i < data.length - 14; i += 14) {
let bind = new taos.TaosBind(14);
bind.bindTimestamp(data[i]);
bind.bindBool(data[i + 1]);
bind.bindTinyInt(data[i + 2]);
bind.bindSmallInt(data[i + 3]);
bind.bindInt(data[i + 4]);
bind.bindBigInt(data[i + 5]);
bind.bindFloat(data[i + 6]);
bind.bindDouble(data[i + 7]);
bind.bindBinary(data[i + 8]);
bind.bindNchar(data[i + 9]);
bind.bindUTinyInt(data[i + 10]);
bind.bindUSmallInt(data[i + 11]);
bind.bindUInt(data[i + 12]);
bind.bindUBigInt(data[i + 13]);
c1.stmtBindParam(bind.getBind());
c1.stmtAddBatch();
}
c1.stmtBindParam(bind2.getBind());
c1.stmtAddBatch();
// c1.stmtExecute();
// ========= bind for 3rd table =================
c1.stmtSetTbnameTags(`${table}_s03`, tagBind3.getBind());
for (let i = 0; i < data.length - 14; i += 14) {
let bind = new taos.TaosBind(14);
bind.bindTimestamp(data[i]);
bind.bindBool(data[i + 1]);
bind.bindTinyInt(data[i + 2]);
bind.bindSmallInt(data[i + 3]);
bind.bindInt(data[i + 4]);
bind.bindBigInt(data[i + 5]);
bind.bindFloat(data[i + 6]);
bind.bindDouble(data[i + 7]);
bind.bindBinary(data[i + 8]);
bind.bindNchar(data[i + 9]);
bind.bindUTinyInt(data[i + 10]);
bind.bindUSmallInt(data[i + 11]);
bind.bindUInt(data[i + 12]);
bind.bindUBigInt(data[i + 13]);
c1.stmtBindParam(bind.getBind());
c1.stmtAddBatch();
}
c1.stmtBindParam(bind2.getBind());
c1.stmtAddBatch();
c1.stmtExecute();
c1.stmtClose();
let result = executeQuery(querySql);
let actualResData = result.resData;
let actualResFields = result.resFeilds;
//assert result data length
expect(expectResData.length).toEqual(actualResData.length);
//assert result data
expectResData.forEach((item, index) => {
expect(item).toEqual(actualResData[index]);
});
//assert result meta data
expectResField.forEach((item, index) => {
expect(item).toEqual(actualResFields[index])
})
});
})
/**
* This is an util function will return the column info based on the create sql.
* @param {*} sql
* @returns Return an Array about the column names and column type.
*
*/
function getFeildsFromDll(sql) {
let fields = [];
let firstBracket = sql.indexOf('(');
let lastBracket = sql.lastIndexOf(')')
let metaStr = sql.slice(firstBracket, lastBracket + 1);
let splitTags = metaStr.split("tags");
splitTags.forEach((item, index, arr) => {
arr[index] = item.slice(1, item.length - 1)
})
splitTags.forEach((item) => {
let tmp = item.split(",");
tmp.forEach((item) => {
let newItem = item.trim();
let spaceInd = newItem.indexOf(' ', 1)
fields.push(newItem.slice(0, spaceInd));
fields.push(newItem.slice(spaceInd + 1, newItem.length))
})
})
return fields;
}
/**
* Based on the input array, it will generate sql that could be used to insert the data of array into the db.
* @param {*} tableName It could be the table name that you want to insert data.
* @param {*} stable If you want to using stable as template to create table automatically,
* set this to your stable name. Deault if '';
* @param {*} dataArr An Array of data that you want insert (it could be mutilple lines)
* @param {*} tagArr An Array used to store one sub table's tag info
* @param {*} numOfColumn The number of columns that the target table has.
* @returns Return an insert sql string.
*/
function buildInsertSql(tableName, stable = '', dataArr, tagArr = [], numOfColumn) {
let insertSql = "";
let dataPartial = "(";
let tagPart = "(";
dataArr.forEach((item, index) => {
// let item = dataArr[index];
if (typeof item == "string") {
dataPartial += '\'' + item + '\'';
} else {
dataPartial += item;
}
if ((index + 1) % numOfColumn == 0 && (index + 1) != dataArr.length) {
dataPartial += ")("
} else if ((index + 1) % numOfColumn == 0 && (index + 1) == dataArr.length) {
dataPartial += ")"
} else {
dataPartial += ","
}
})
if (stable != '') {
tagArr.forEach((item, index) => {
if (typeof item == "string") {
tagPart += '\'' + item + '\'';
} else {
tagPart += item;
}
if (index != tagArr.length - 1) {
tagPart += ",";
} else {
tagPart += ")";
}
})
}
if (stable == '') {
insertSql += `insert into ${tableName} values ${dataPartial};`
} else {
insertSql += `insert into ${tableName} using ${stable} tags ${tagPart} values ${dataPartial};`
}
return insertSql;
}
/**
* used to mapping the data type of an create clause into TDengine's datatype code
*/
const TDengineTypeCode = {
'null': 0,
'bool': 1,
'tinyint': 2,
'smallint': 3,
'int': 4,
'bigint': 5,
'float': 6,
'double': 7,
'binary': 8,
'timestamp': 9,
'nchar': 10,
'tinyint unsigned': 11,
'smallint unsigned': 12,
'int unsigned': 13,
'bigint unsigned': 14,
'json': 15,
}
/**
* Mapping the data type with corresponing size that has defined in tdengine
*/
const TDengineTypeBytes = {
'null': 0,
'bool': 1,
'tinyint': 1,
'smallint': 2,
'int': 4,
'bigint': 8,
'float': 4,
'double': 8,
'timestamp': 8,
'tinyint unsigned': 1,
'smallint unsigned': 2,
'int unsigned': 4,
'bigint unsigned': 8,
'json': 4096,
}
/**
* Used to create an array of taos feilds object.
* @param {*} arr This should be the return array from the method getFeildsFromDll()
* @returns Return an array of taosFeild Object
*/
function getFieldArr(arr) {
let feild = [];
for (let i = 0; i < arr.length;) {
let bracetPosi = arr[i + 1].indexOf('(');
let type = '';
let size = -1;
if (bracetPosi == -1) {
type = TDengineTypeCode[arr[i + 1]];
size = TDengineTypeBytes[arr[i + 1]];
}else{
type = TDengineTypeCode[arr[i + 1].slice(0, bracetPosi)];
size = Number(arr[i + 1].slice(bracetPosi + 1, arr[i + 1].indexOf(')')));
}
let fieldObj = {
name: arr[i].toLowerCase(),
type: type,
bytes: size
}
feild.push(fieldObj);
i = i + 2;
}
return feild;
}
/**
* Conbine arrays of data info and tag info together, and return a new array. This array construction is simmilar with query result
* from the tdengine by taos shell.This method only can be used by a subtable.
* @param {*} dataArr An array holds columns' data that will be insert into the db.
* @param {*} tagArr An array holds tags' data that is belong to a sub table.
* @param {*} numOfcolumn
* @returns return the an array of column data and tag data.
*/
function getResData(dataArr, tagArr, numOfcolumn) {
let resData = [];
dataArr.forEach((item, index) => {
resData.push(item);
if ((index + 1) % numOfcolumn == 0) {
tagArr.forEach((element) => {
resData.push(element);
}) ;
}
});
return resData;
}
module.exports = { getFeildsFromDll, buildInsertSql, getFieldArr, getResData };
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册