未验证 提交 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,
};
此差异已折叠。
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
}
此差异已折叠。
/**
* 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 { 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 }
此差异已折叠。
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;
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册