提交 555208eb 编写于 作者: W wenzhouwww

[TD-5661]<test>: let all connector test case for nano seconds running

上级 bb3b0a6f
......@@ -235,11 +235,18 @@ pipeline {
npm install td2.0-connector > /dev/null 2>&1
node nodejsChecker.js host=localhost
node test1970.js
cd ${WKC}/tests/connectorTest/nodejsTest/nanosupport
npm install td2.0-connector > /dev/null 2>&1
node nanosecondTest.js
'''
sh '''
cd ${WKC}/tests/examples/C#/taosdemo
mcs -out:taosdemo *.cs > /dev/null 2>&1
echo '' |./taosdemo -c /etc/taos
cd ${WKC}/tests/connectorTest/C#Test/nanosupport
mcs -out:nano *.cs > /dev/null 2>&1
echo '' |./nano
'''
sh '''
cd ${WKC}/tests/gotest
......
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace TDengineDriver
{
enum TDengineDataType
{
TSDB_DATA_TYPE_NULL = 0, // 1 bytes
TSDB_DATA_TYPE_BOOL = 1, // 1 bytes
TSDB_DATA_TYPE_TINYINT = 2, // 1 bytes
TSDB_DATA_TYPE_SMALLINT = 3, // 2 bytes
TSDB_DATA_TYPE_INT = 4, // 4 bytes
TSDB_DATA_TYPE_BIGINT = 5, // 8 bytes
TSDB_DATA_TYPE_FLOAT = 6, // 4 bytes
TSDB_DATA_TYPE_DOUBLE = 7, // 8 bytes
TSDB_DATA_TYPE_BINARY = 8, // string
TSDB_DATA_TYPE_TIMESTAMP = 9,// 8 bytes
TSDB_DATA_TYPE_NCHAR = 10, // unicode string
TSDB_DATA_TYPE_UTINYINT = 11,// 1 byte
TSDB_DATA_TYPE_USMALLINT= 12,// 2 bytes
TSDB_DATA_TYPE_UINT = 13, // 4 bytes
TSDB_DATA_TYPE_UBIGINT= 14 // 8 bytes
}
enum TDengineInitOption
{
TSDB_OPTION_LOCALE = 0,
TSDB_OPTION_CHARSET = 1,
TSDB_OPTION_TIMEZONE = 2,
TDDB_OPTION_CONFIGDIR = 3,
TDDB_OPTION_SHELL_ACTIVITY_TIMER = 4
}
class TDengineMeta
{
public string name;
public short size;
public byte type;
public string TypeName()
{
switch ((TDengineDataType)type)
{
case TDengineDataType.TSDB_DATA_TYPE_BOOL:
return "BOOL";
case TDengineDataType.TSDB_DATA_TYPE_TINYINT:
return "TINYINT";
case TDengineDataType.TSDB_DATA_TYPE_SMALLINT:
return "SMALLINT";
case TDengineDataType.TSDB_DATA_TYPE_INT:
return "INT";
case TDengineDataType.TSDB_DATA_TYPE_BIGINT:
return "BIGINT";
case TDengineDataType.TSDB_DATA_TYPE_UTINYINT:
return "TINYINT UNSIGNED";
case TDengineDataType.TSDB_DATA_TYPE_USMALLINT:
return "SMALLINT UNSIGNED";
case TDengineDataType.TSDB_DATA_TYPE_UINT:
return "INT UNSIGNED";
case TDengineDataType.TSDB_DATA_TYPE_UBIGINT:
return "BIGINT UNSIGNED";
case TDengineDataType.TSDB_DATA_TYPE_FLOAT:
return "FLOAT";
case TDengineDataType.TSDB_DATA_TYPE_DOUBLE:
return "DOUBLE";
case TDengineDataType.TSDB_DATA_TYPE_BINARY:
return "STRING";
case TDengineDataType.TSDB_DATA_TYPE_TIMESTAMP:
return "TIMESTAMP";
case TDengineDataType.TSDB_DATA_TYPE_NCHAR:
return "NCHAR";
default:
return "undefine";
}
}
}
class TDengine
{
public const int TSDB_CODE_SUCCESS = 0;
[DllImport("taos", EntryPoint = "taos_init", CallingConvention = CallingConvention.Cdecl)]
static extern public void Init();
[DllImport("taos", EntryPoint = "taos_cleanup", CallingConvention = CallingConvention.Cdecl)]
static extern public void Cleanup();
[DllImport("taos", EntryPoint = "taos_options", CallingConvention = CallingConvention.Cdecl)]
static extern public void Options(int option, string value);
[DllImport("taos", EntryPoint = "taos_connect", CallingConvention = CallingConvention.Cdecl)]
static extern public IntPtr Connect(string ip, string user, string password, string db, short port);
[DllImport("taos", EntryPoint = "taos_errstr", CallingConvention = CallingConvention.Cdecl)]
static extern private IntPtr taos_errstr(IntPtr res);
static public string Error(IntPtr res)
{
IntPtr errPtr = taos_errstr(res);
return Marshal.PtrToStringAnsi(errPtr);
}
[DllImport("taos", EntryPoint = "taos_errno", CallingConvention = CallingConvention.Cdecl)]
static extern public int ErrorNo(IntPtr res);
[DllImport("taos", EntryPoint = "taos_query", CallingConvention = CallingConvention.Cdecl)]
static extern public IntPtr Query(IntPtr conn, string sqlstr);
[DllImport("taos", EntryPoint = "taos_affected_rows", CallingConvention = CallingConvention.Cdecl)]
static extern public int AffectRows(IntPtr res);
[DllImport("taos", EntryPoint = "taos_field_count", CallingConvention = CallingConvention.Cdecl)]
static extern public int FieldCount(IntPtr res);
[DllImport("taos", EntryPoint = "taos_fetch_fields", CallingConvention = CallingConvention.Cdecl)]
static extern private IntPtr taos_fetch_fields(IntPtr res);
static public List<TDengineMeta> FetchFields(IntPtr res)
{
const int fieldSize = 68;
List<TDengineMeta> metas = new List<TDengineMeta>();
if (res == IntPtr.Zero)
{
return metas;
}
int fieldCount = FieldCount(res);
IntPtr fieldsPtr = taos_fetch_fields(res);
for (int i = 0; i < fieldCount; ++i)
{
int offset = i * fieldSize;
TDengineMeta meta = new TDengineMeta();
meta.name = Marshal.PtrToStringAnsi(fieldsPtr + offset);
meta.type = Marshal.ReadByte(fieldsPtr + offset + 65);
meta.size = Marshal.ReadInt16(fieldsPtr + offset + 66);
metas.Add(meta);
}
return metas;
}
[DllImport("taos", EntryPoint = "taos_fetch_row", CallingConvention = CallingConvention.Cdecl)]
static extern public IntPtr FetchRows(IntPtr res);
[DllImport("taos", EntryPoint = "taos_free_result", CallingConvention = CallingConvention.Cdecl)]
static extern public IntPtr FreeResult(IntPtr res);
[DllImport("taos", EntryPoint = "taos_close", CallingConvention = CallingConvention.Cdecl)]
static extern public int Close(IntPtr taos);
//get precisionin parameter restultset
[DllImport("taos", EntryPoint = "taos_result_precision", CallingConvention = CallingConvention.Cdecl)]
static extern public int ResultPrecision(IntPtr taos);
}
}
此差异已折叠。
const taos = require('td2.0-connector');
var conn = taos.connect({host:"localhost", user:"root", password:"taosdata", config:"/etc/taos",port:6030})
var c1 = conn.cursor();
function checkData(sql,row,col,data){
console.log(sql)
c1.execute(sql)
var d = c1.fetchall();
let checkdata = d[row][col];
if (checkdata == data) {
console.log('check pass')
}
else{
console.log('check failed')
console.log('checked is :',checkdata)
console.log("expected is :",data)
}
}
// nano basic case
c1.execute('reset query cache')
c1.execute('drop database if exists db')
c1.execute('create database db precision "ns";')
c1.execute('use db');
c1.execute('create table tb (ts timestamp, speed int)')
c1.execute('insert into tb values(\'2021-06-10 00:00:00.100000001\', 1);')
c1.execute('insert into tb values(1623254400150000000, 2);')
c1.execute('import into tb values(1623254400300000000, 3);')
c1.execute('import into tb values(1623254400299999999, 4);')
c1.execute('insert into tb values(1623254400300000001, 5);')
c1.execute('insert into tb values(1623254400999999999, 7);')
c1.execute('insert into tb values(1623254400123456789, 8);')
sql = 'select * from tb;'
console.log('*******************************************')
console.log('this is area about checkdata result')
//check data about insert data
checkData(sql,0,0,'2021-06-10 00:00:00.100000001')
checkData(sql,1,0,'2021-06-10 00:00:00.123456789')
checkData(sql,2,0,'2021-06-10 00:00:00.150000000')
checkData(sql,3,0,'2021-06-10 00:00:00.299999999')
checkData(sql,4,0,'2021-06-10 00:00:00.300000000')
checkData(sql,5,0,'2021-06-10 00:00:00.300000001')
checkData(sql,6,0,'2021-06-10 00:00:00.999999999')
checkData(sql,0,1,1)
checkData(sql,1,1,8)
checkData(sql,2,1,2)
checkData(sql,5,1,5)
// us basic case
c1.execute('reset query cache')
c1.execute('drop database if exists usdb')
c1.execute('create database usdb precision "us";')
c1.execute('use usdb');
c1.execute('create table tb (ts timestamp, speed int)')
c1.execute('insert into tb values(\'2021-06-10 00:00:00.100001\', 1);')
c1.execute('insert into tb values(1623254400150000, 2);')
c1.execute('import into tb values(1623254400300000, 3);')
c1.execute('import into tb values(1623254400299999, 4);')
c1.execute('insert into tb values(1623254400300001, 5);')
c1.execute('insert into tb values(1623254400999999, 7);')
c1.execute('insert into tb values(1623254400123789, 8);')
sql = 'select * from tb;'
console.log('*******************************************')
//check data about insert data
checkData(sql,0,0,'2021-06-10 00:00:00.100001')
checkData(sql,1,0,'2021-06-10 00:00:00.123789')
checkData(sql,2,0,'2021-06-10 00:00:00.150000')
checkData(sql,3,0,'2021-06-10 00:00:00.299999')
checkData(sql,4,0,'2021-06-10 00:00:00.300000')
checkData(sql,5,0,'2021-06-10 00:00:00.300001')
checkData(sql,6,0,'2021-06-10 00:00:00.999999')
checkData(sql,0,1,1)
checkData(sql,1,1,8)
checkData(sql,2,1,2)
checkData(sql,5,1,5)
console.log('*******************************************')
// ms basic case
c1.execute('reset query cache')
c1.execute('drop database if exists msdb')
c1.execute('create database msdb precision "ms";')
c1.execute('use msdb');
c1.execute('create table tb (ts timestamp, speed int)')
c1.execute('insert into tb values(\'2021-06-10 00:00:00.101\', 1);')
c1.execute('insert into tb values(1623254400150, 2);')
c1.execute('import into tb values(1623254400300, 3);')
c1.execute('import into tb values(1623254400299, 4);')
c1.execute('insert into tb values(1623254400301, 5);')
c1.execute('insert into tb values(1623254400789, 7);')
c1.execute('insert into tb values(1623254400999, 8);')
sql = 'select * from tb;'
console.log('*******************************************')
console.log('this is area about checkdata result')
//check data about insert data
checkData(sql,0,0,'2021-06-10 00:00:00.101')
checkData(sql,1,0,'2021-06-10 00:00:00.150')
checkData(sql,2,0,'2021-06-10 00:00:00.299')
checkData(sql,3,0,'2021-06-10 00:00:00.300')
checkData(sql,4,0,'2021-06-10 00:00:00.301')
checkData(sql,5,0,'2021-06-10 00:00:00.789')
checkData(sql,6,0,'2021-06-10 00:00:00.999')
checkData(sql,0,1,1)
checkData(sql,1,1,2)
checkData(sql,2,1,4)
checkData(sql,5,1,7)
console.log('*******************************************')
// offfical query result to show
// console.log('this is area about fetch all data')
// var query = c1.query(sql)
// var promise = query.execute();
// promise.then(function(result) {
// result.pretty();
// });
console.log('*******************************************')
c1.execute('use db')
sql2 = 'select count(*) from tb where ts > 1623254400100000000 and ts < 1623254400100000002;'
checkData(sql2,0,0,1)
sql3 = 'select count(*) from tb where ts > \'2021-06-10 0:00:00.100000001\' and ts < \'2021-06-10 0:00:00.160000000\';'
checkData(sql3,0,0,2)
sql4 = 'select count(*) from tb where ts > 1623254400100000000 and ts < 1623254400150000000;'
checkData(sql4,0,0,2)
sql5 = 'select count(*) from tb where ts > \'2021-06-10 0:00:00.100000000\' and ts < \'2021-06-10 0:00:00.150000000\';'
checkData(sql5,0,0,2)
sql6 = 'select count(*) from tb where ts > 1623254400400000000;'
checkData(sql6,0,0,1)
sql7 = 'select count(*) from tb where ts < \'2021-06-10 00:00:00.400000000\';'
checkData(sql7,0,0,6)
sql8 = 'select count(*) from tb where ts > now + 400000000b;'
c1.execute(sql8)
sql9 = 'select count(*) from tb where ts >= \'2021-06-10 0:00:00.100000001\';'
checkData(sql9,0,0,7)
sql10 = 'select count(*) from tb where ts <= 1623254400300000000;'
checkData(sql10,0,0,5)
sql11 = 'select count(*) from tb where ts = \'2021-06-10 0:00:00.000000000\';'
c1.execute(sql11)
sql12 = 'select count(*) from tb where ts = 1623254400150000000;'
checkData(sql12,0,0,1)
sql13 = 'select count(*) from tb where ts = \'2021-06-10 0:00:00.100000001\';'
checkData(sql13,0,0,1)
sql14 = 'select count(*) from tb where ts between 1623254400000000000 and 1623254400400000000;'
checkData(sql14,0,0,6)
sql15 = 'select count(*) from tb where ts between \'2021-06-10 0:00:00.299999999\' and \'2021-06-10 0:00:00.300000001\';'
checkData(sql15,0,0,3)
sql16 = 'select avg(speed) from tb interval(5000000000b);'
checkData(sql16,0,0,'2021-06-10 00:00:00.000000000')
sql17 = 'select avg(speed) from tb interval(100000000b)'
checkData(sql17,0,1,3.6666666666666665)
checkData(sql17,1,1,4.000000000)
checkData(sql17,2,0,'2021-06-10 00:00:00.300000000')
checkData(sql17,3,0,'2021-06-10 00:00:00.900000000')
console.log("print break ")
// sql18 = 'select avg(speed) from tb interval(999b)'
// c1.execute(sql18)
console.log("print break2 ")
sql19 = 'select avg(speed) from tb interval(1u);'
checkData(sql19,2,1,2.000000000)
checkData(sql19,3,0,'2021-06-10 00:00:00.299999000')
sql20 = 'select avg(speed) from tb interval(100000000b) sliding (100000000b);'
checkData(sql20,2,1,4.000000000)
checkData(sql20,3,0,'2021-06-10 00:00:00.900000000')
sql21 = 'select last(*) from tb;'
checkData(sql21,0,0,'2021-06-10 00:00:00.999999999')
sql22 = 'select first(*) from tb;'
checkData(sql22,0,0,'2021-06-10 00:00:00.100000001')
// timezone support
console.log('testing nanosecond support in other timestamps')
c1.execute('create table tb2 (ts timestamp, speed int, ts2 timestamp);')
c1.execute('insert into tb2 values(\'2021-06-10 0:00:00.100000001\', 1, \'2021-06-11 0:00:00.100000001\');')
c1.execute('insert into tb2 values(1623254400150000000, 2, 1623340800150000000);')
c1.execute('import into tb2 values(1623254400300000000, 3, 1623340800300000000);')
c1.execute('import into tb2 values(1623254400299999999, 4, 1623340800299999999);')
c1.execute('insert into tb2 values(1623254400300000001, 5, 1623340800300000001);')
c1.execute('insert into tb2 values(1623254400999999999, 7, 1623513600999999999);')
sql23 = 'select * from tb2;'
checkData(sql23,0,0,'2021-06-10 00:00:00.100000001')
checkData(sql23,1,0,'2021-06-10 00:00:00.150000000')
checkData(sql23,2,1,4)
checkData(sql23,3,1,3)
checkData(sql23,4,2,'2021-06-11 00:00:00.300000001')
checkData(sql23,5,2,'2021-06-13 00:00:00.999999999')
sql24 = 'select count(*) from tb2 where ts2 >= \'2021-06-11 0:00:00.100000001\';'
checkData(sql24,0,0,6)
sql25 = 'select count(*) from tb2 where ts2 <= 1623340800400000000;'
checkData(sql25,0,0,5)
sql26 = 'select count(*) from tb2 where ts2 = \'2021-06-11 0:00:00.300000001\';'
checkData(sql26,0,0,1)
sql27 = 'select count(*) from tb2 where ts2 = 1623340800300000001;'
checkData(sql27,0,0,1)
sql28 = 'select count(*) from tb2 where ts2 between 1623340800000000000 and 1623340800450000000;'
checkData(sql28,0,0,5)
sql29 = 'select count(*) from tb2 where ts2 between \'2021-06-11 0:00:00.299999999\' and \'2021-06-11 0:00:00.300000001\';'
checkData(sql29,0,0,3)
sql30 = 'select count(*) from tb2 where ts2 <> 1623513600999999999;'
checkData(sql30,0,0,5)
sql31 = 'select count(*) from tb2 where ts2 <> \'2021-06-11 0:00:00.100000001\';'
checkData(sql31,0,0,5)
sql32 = 'select count(*) from tb2 where ts2 != 1623513600999999999;'
checkData(sql32,0,0,5)
sql33 = 'select count(*) from tb2 where ts2 != \'2021-06-11 0:00:00.100000001\';'
checkData(sql33,0,0,5)
c1.execute('insert into tb2 values(now + 500000000b, 6, now +2d);')
sql34 = 'select count(*) from tb2;'
checkData(sql34,0,0,7)
// check timezone support
c1.execute('use db;')
c1.execute('create stable st (ts timestamp ,speed float ) tags(time timestamp ,id int);')
c1.execute('insert into stb1 using st tags("2021-06-10 0:00:00.123456789" , 1 ) values("2021-06-10T0:00:00.123456789+07:00" , 1.0);' )
sql35 = 'select first(*) from stb1;'
checkData(sql35,0,0,'2021-06-10 01:00:00.123456789')
c1.execute('use usdb;')
c1.execute('create stable st (ts timestamp ,speed float ) tags(time timestamp ,id int);')
c1.execute('insert into stb1 using st tags("2021-06-10 0:00:00.123456" , 1 ) values("2021-06-10T0:00:00.123456+07:00" , 1.0);' )
sql36 = 'select first(*) from stb1;'
checkData(sql36,0,0,'2021-06-10 01:00:00.123456')
c1.execute('use msdb;')
c1.execute('create stable st (ts timestamp ,speed float ) tags(time timestamp ,id int);')
c1.execute('insert into stb1 using st tags("2021-06-10 0:00:00.123456" , 1 ) values("2021-06-10T0:00:00.123456+07:00" , 1.0);' )
sql36 = 'select first(*) from stb1;'
checkData(sql36,0,0,'2021-06-10 01:00:00.123')
此差异已折叠。
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)
}
}
*/
/**
* 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,
// NULL value definition
// NOTE: These values should change according to C definition in tsdb.h
C_BOOL_NULL : 2,
C_TINYINT_NULL : -128,
C_SMALLINT_NULL : -32768,
C_INT_NULL : -2147483648,
C_BIGINT_NULL : -9223372036854775808,
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,
}
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',
}
/**
* @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];
}
const ref = require('ref-napi');
require('./globalfunc.js')
const CTaosInterface = require('./cinterface')
const errors = require('./error')
const TaosQuery = require('./taosquery')
const { PerformanceObserver, performance } = require('perf_hooks');
module.exports = TDengineCursor;
/**
* @typedef {Object} Buffer - A Node.js buffer. Please refer to {@link https://nodejs.org/api/buffer.html} for more details
* @global
*/
/**
* @class TDengineCursor
* @classdesc The TDengine Cursor works directly with the C Interface which works with TDengine. It refrains from
* returning parsed data and majority of functions return the raw data such as cursor.fetchall() as compared to the TaosQuery class which
* has functions that "prettify" the data and add more functionality and can be used through cursor.query("your query"). Instead of
* promises, the class and its functions use callbacks.
* @param {TDengineConnection} - The TDengine Connection this cursor uses to interact with TDengine
* @property {data} - Latest retrieved data from query execution. It is an empty array by default
* @property {fields} - Array of the field objects in order from left to right of the latest data retrieved
* @since 1.0.0
*/
function TDengineCursor(connection = null) {
//All parameters are store for sync queries only.
this._rowcount = -1;
this._connection = null;
this._result = null;
this._fields = null;
this.data = [];
this.fields = null;
if (connection != null) {
this._connection = connection
this._chandle = connection._chandle //pass through, just need library loaded.
}
else {
throw new errors.ProgrammingError("A TDengineConnection object is required to be passed to the TDengineCursor");
}
}
/**
* Get the row counts of the latest query
* @since 1.0.0
* @return {number} Rowcount
*/
TDengineCursor.prototype.rowcount = function rowcount() {
return this._rowcount;
}
/**
* Close the cursor by setting its connection to null and freeing results from the connection and resetting the results it has stored
* @return {boolean} Whether or not the cursor was succesfully closed
* @since 1.0.0
*/
TDengineCursor.prototype.close = function close() {
if (this._connection == null) {
return false;
}
this._connection._clearResultSet();
this._reset_result();
this._connection = null;
return true;
}
/**
* Create a TaosQuery object to perform a query to TDengine and retrieve data.
* @param {string} operation - The operation string to perform a query on
* @param {boolean} execute - Whether or not to immedietely perform the query. Default is false.
* @return {TaosQuery | Promise<TaosResult>} A TaosQuery object
* @example
* var query = cursor.query("select count(*) from meterinfo.meters");
* query.execute();
* @since 1.0.6
*/
TDengineCursor.prototype.query = function query(operation, execute = false) {
return new TaosQuery(operation, this, execute);
}
/**
* Execute a query. Also stores all the field meta data returned from the query into cursor.fields. It is preferable to use cursor.query() to create
* queries and execute them instead of using the cursor object directly.
* @param {string} operation - The query operation to execute in the taos shell
* @param {Object} options - Execution options object. quiet : true turns off logging from queries
* @param {boolean} options.quiet - True if you want to surpress logging such as "Query OK, 1 row(s) ..."
* @param {function} callback - A callback function to execute after the query is made to TDengine
* @return {number | Buffer} Number of affected rows or a Buffer that points to the results of the query
* @since 1.0.0
*/
TDengineCursor.prototype.execute = function execute(operation, options, callback) {
if (operation == undefined) {
throw new errors.ProgrammingError('No operation passed as argument');
return null;
}
if (typeof options == 'function') {
callback = options;
}
if (typeof options != 'object') options = {}
if (this._connection == null) {
throw new errors.ProgrammingError('Cursor is not connected');
}
this._reset_result();
let stmt = operation;
let time = 0;
let res;
if (options['quiet'] != true) {
const obs = new PerformanceObserver((items) => {
time = items.getEntries()[0].duration;
performance.clearMarks();
});
obs.observe({ entryTypes: ['measure'] });
performance.mark('A');
this._result = this._chandle.query(this._connection._conn, stmt);
performance.mark('B');
performance.measure('query', 'A', 'B');
}
else {
this._result = this._chandle.query(this._connection._conn, stmt);
}
res = this._chandle.errno(this._result);
if (res == 0) {
let fieldCount = this._chandle.fieldsCount(this._result);
if (fieldCount == 0) {
let affectedRowCount = this._chandle.affectedRows(this._result);
let response = this._createAffectedResponse(affectedRowCount, time)
if (options['quiet'] != true) {
console.log(response);
}
wrapCB(callback);
return affectedRowCount; //return num of affected rows, common with insert, use statements
}
else {
this._fields = this._chandle.useResult(this._result);
this.fields = this._fields;
wrapCB(callback);
return this._result; //return a pointer to the result
}
}
else {
throw new errors.ProgrammingError(this._chandle.errStr(this._result))
}
}
TDengineCursor.prototype._createAffectedResponse = function (num, time) {
return "Query OK, " + num + " row(s) affected (" + (time * 0.001).toFixed(8) + "s)";
}
TDengineCursor.prototype._createSetResponse = function (num, time) {
return "Query OK, " + num + " row(s) in set (" + (time * 0.001).toFixed(8) + "s)";
}
TDengineCursor.prototype.executemany = function executemany() {
}
TDengineCursor.prototype.fetchone = function fetchone() {
}
TDengineCursor.prototype.fetchmany = function fetchmany() {
}
/**
* Fetches all results from a query and also stores results into cursor.data. It is preferable to use cursor.query() to create
* queries and execute them instead of using the cursor object directly.
* @param {function} callback - callback function executing on the complete fetched data
* @return {Array<Array>} The resultant array, with entries corresponding to each retreived row from the query results, sorted in
* order by the field name ordering in the table.
* @since 1.0.0
* @example
* cursor.execute('select * from db.table');
* var data = cursor.fetchall(function(results) {
* results.forEach(row => console.log(row));
* })
*/
TDengineCursor.prototype.fetchall = function fetchall(options, callback) {
if (this._result == null || this._fields == null) {
throw new errors.OperationalError("Invalid use of fetchall, either result or fields from query are null. First execute a query first");
}
let num_of_rows = this._chandle.affectedRows(this._result);
let data = new Array(num_of_rows);
this._rowcount = 0;
let time = 0;
const obs = new PerformanceObserver((items) => {
time += items.getEntries()[0].duration;
performance.clearMarks();
});
obs.observe({ entryTypes: ['measure'] });
performance.mark('A');
while (true) {
let blockAndRows = this._chandle.fetchBlock(this._result, this._fields);
// console.log(blockAndRows);
// break;
let block = blockAndRows.blocks;
let num_of_rows = blockAndRows.num_of_rows;
if (num_of_rows == 0) {
break;
}
this._rowcount += num_of_rows;
let numoffields = this._fields.length;
for (let i = 0; i < num_of_rows; i++) {
// data.push([]);
let rowBlock = new Array(numoffields);
for (let j = 0; j < numoffields; j++) {
rowBlock[j] = block[j][i];
}
data[this._rowcount - num_of_rows + i] = (rowBlock);
// data.push(rowBlock);
}
}
performance.mark('B');
performance.measure('query', 'A', 'B');
let response = this._createSetResponse(this._rowcount, time)
console.log(response);
// this._connection._clearResultSet();
let fields = this.fields;
this._reset_result();
this.data = data;
this.fields = fields;
wrapCB(callback, data);
return data;
}
/**
* Asynchrnously execute a query to TDengine. NOTE, insertion requests must be done in sync if on the same table.
* @param {string} operation - The query operation to execute in the taos shell
* @param {Object} options - Execution options object. quiet : true turns off logging from queries
* @param {boolean} options.quiet - True if you want to surpress logging such as "Query OK, 1 row(s) ..."
* @param {function} callback - A callback function to execute after the query is made to TDengine
* @return {number | Buffer} Number of affected rows or a Buffer that points to the results of the query
* @since 1.0.0
*/
TDengineCursor.prototype.execute_a = function execute_a(operation, options, callback, param) {
if (operation == undefined) {
throw new errors.ProgrammingError('No operation passed as argument');
return null;
}
if (typeof options == 'function') {
//we expect the parameter after callback to be param
param = callback;
callback = options;
}
if (typeof options != 'object') options = {}
if (this._connection == null) {
throw new errors.ProgrammingError('Cursor is not connected');
}
if (typeof callback != 'function') {
throw new errors.ProgrammingError("No callback function passed to execute_a function");
}
// Async wrapper for callback;
var cr = this;
let asyncCallbackWrapper = function (param2, res2, resCode) {
if (typeof callback == 'function') {
callback(param2, res2, resCode);
}
if (resCode >= 0) {
// let fieldCount = cr._chandle.numFields(res2);
// if (fieldCount == 0) {
// //cr._chandle.freeResult(res2);
// return res2;
// }
// else {
// return res2;
// }
return res2;
}
else {
throw new errors.ProgrammingError("Error occuring with use of execute_a async function. Status code was returned with failure");
}
}
let stmt = operation;
let time = 0;
// Use ref module to write to buffer in cursor.js instead of taosquery to maintain a difference in levels. Have taosquery stay high level
// through letting it pass an object as param
var buf = ref.alloc('Object');
ref.writeObject(buf, 0, param);
const obs = new PerformanceObserver((items) => {
time = items.getEntries()[0].duration;
performance.clearMarks();
});
obs.observe({ entryTypes: ['measure'] });
performance.mark('A');
this._chandle.query_a(this._connection._conn, stmt, asyncCallbackWrapper, buf);
performance.mark('B');
performance.measure('query', 'A', 'B');
return param;
}
/**
* Fetches all results from an async query. It is preferable to use cursor.query_a() to create
* async queries and execute them instead of using the cursor object directly.
* @param {Object} options - An options object containing options for this function
* @param {function} callback - callback function that is callbacked on the COMPLETE fetched data (it is calledback only once!).
* Must be of form function (param, result, rowCount, rowData)
* @param {Object} param - A parameter that is also passed to the main callback function. Important! Param must be an object, and the key "data" cannot be used
* @return {{param:Object, result:Buffer}} An object with the passed parameters object and the buffer instance that is a pointer to the result handle.
* @since 1.2.0
* @example
* cursor.execute('select * from db.table');
* var data = cursor.fetchall(function(results) {
* results.forEach(row => console.log(row));
* })
*/
TDengineCursor.prototype.fetchall_a = function fetchall_a(result, options, callback, param = {}) {
if (typeof options == 'function') {
//we expect the parameter after callback to be param
param = callback;
callback = options;
}
if (typeof options != 'object') options = {}
if (this._connection == null) {
throw new errors.ProgrammingError('Cursor is not connected');
}
if (typeof callback != 'function') {
throw new errors.ProgrammingError('No callback function passed to fetchall_a function')
}
if (param.data) {
throw new errors.ProgrammingError("You aren't allowed to set the key 'data' for the parameters object");
}
let buf = ref.alloc('Object');
param.data = [];
var cr = this;
// This callback wrapper accumulates the data from the fetch_rows_a function from the cinterface. It is accumulated by passing the param2
// object which holds accumulated data in the data key.
let asyncCallbackWrapper = function asyncCallbackWrapper(param2, result2, numOfRows2, rowData) {
param2 = ref.readObject(param2); //return the object back from the pointer
if (numOfRows2 > 0 && rowData.length != 0) {
// Keep fetching until now rows left.
let buf2 = ref.alloc('Object');
param2.data.push(rowData);
ref.writeObject(buf2, 0, param2);
cr._chandle.fetch_rows_a(result2, asyncCallbackWrapper, buf2);
}
else {
let finalData = param2.data;
let fields = cr._chandle.fetchFields_a(result2);
let data = [];
for (let i = 0; i < finalData.length; i++) {
let num_of_rows = finalData[i][0].length; //fetched block number i;
let block = finalData[i];
for (let j = 0; j < num_of_rows; j++) {
data.push([]);
let rowBlock = new Array(fields.length);
for (let k = 0; k < fields.length; k++) {
rowBlock[k] = block[k][j];
}
data[data.length - 1] = rowBlock;
}
}
cr._chandle.freeResult(result2); // free result, avoid seg faults and mem leaks!
callback(param2, result2, numOfRows2, { data: data, fields: fields });
}
}
ref.writeObject(buf, 0, param);
param = this._chandle.fetch_rows_a(result, asyncCallbackWrapper, buf); //returned param
return { param: param, result: result };
}
/**
* Stop a query given the result handle.
* @param {Buffer} result - The buffer that acts as the result handle
* @since 1.3.0
*/
TDengineCursor.prototype.stopQuery = function stopQuery(result) {
this._chandle.stopQuery(result);
}
TDengineCursor.prototype._reset_result = function _reset_result() {
this._rowcount = -1;
if (this._result != null) {
this._chandle.freeResult(this._result);
}
this._result = null;
this._fields = null;
this.data = [];
this.fields = null;
}
/**
* Get server info such as version number
* @return {string}
* @since 1.3.0
*/
TDengineCursor.prototype.getServerInfo = function getServerInfo() {
return this._chandle.getServerInfo(this._connection._conn);
}
/**
* Get client info such as version number
* @return {string}
* @since 1.3.0
*/
TDengineCursor.prototype.getClientInfo = function getClientInfo() {
return this._chandle.getClientInfo();
}
/**
* Subscribe to a table from a database in TDengine.
* @param {Object} config - A configuration object containing the configuration options for the subscription
* @param {string} config.restart - whether or not to continue a subscription if it already exits, otherwise start from beginning
* @param {string} config.topic - The unique identifier of a subscription
* @param {string} config.sql - A sql statement for data query
* @param {string} config.interval - The pulling interval
* @return {Buffer} A buffer pointing to the subscription session handle
* @since 1.3.0
*/
TDengineCursor.prototype.subscribe = function subscribe(config) {
let restart = config.restart ? 1 : 0;
return this._chandle.subscribe(this._connection._conn, restart, config.topic, config.sql, config.interval);
};
/**
* An infinite loop that consumes the latest data and calls a callback function that is provided.
* @param {Buffer} subscription - A buffer object pointing to the subscription session handle
* @param {function} callback - The callback function that takes the row data, field/column meta data, and the subscription session handle as input
* @since 1.3.0
*/
TDengineCursor.prototype.consumeData = async function consumeData(subscription, callback) {
while (true) {
let { data, fields, result } = this._chandle.consume(subscription);
callback(data, fields, result);
}
}
/**
* Unsubscribe the provided buffer object pointing to the subscription session handle
* @param {Buffer} subscription - A buffer object pointing to the subscription session handle that is to be unsubscribed
* @since 1.3.0
*/
TDengineCursor.prototype.unsubscribe = function unsubscribe(subscription) {
this._chandle.unsubscribe(subscription);
}
/**
* Open a stream with TDengine to run the sql query periodically in the background
* @param {string} sql - The query to run
* @param {function} callback - The callback function to run after each query, accepting inputs as param, result handle, data, fields meta data
* @param {number} stime - The time of the stream starts in the form of epoch milliseconds. If 0 is given, the start time is set as the current time.
* @param {function} stoppingCallback - The callback function to run when the continuous query stops. It takes no inputs
* @param {object} param - A parameter that is passed to the main callback function
* @return {Buffer} A buffer pointing to the stream handle
* @since 1.3.0
*/
TDengineCursor.prototype.openStream = function openStream(sql, callback, stime = 0, stoppingCallback, param = {}) {
let buf = ref.alloc('Object');
ref.writeObject(buf, 0, param);
let asyncCallbackWrapper = function (param2, result2, blocks, fields) {
let data = [];
let num_of_rows = blocks[0].length;
for (let j = 0; j < num_of_rows; j++) {
data.push([]);
let rowBlock = new Array(fields.length);
for (let k = 0; k < fields.length; k++) {
rowBlock[k] = blocks[k][j];
}
data[data.length - 1] = rowBlock;
}
callback(param2, result2, blocks, fields);
}
return this._chandle.openStream(this._connection._conn, sql, asyncCallbackWrapper, stime, stoppingCallback, buf);
}
/**
* Close a stream
* @param {Buffer} - A buffer pointing to the handle of the stream to be closed
* @since 1.3.0
*/
TDengineCursor.prototype.closeStream = function closeStream(stream) {
this._chandle.closeStream(stream);
}
/**
* 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 FieldTypes = require('./constants');
const util = require('util');
/**
* Various objects such as TaosRow and TaosColumn that help make parsing data easier
* @module TaosObjects
*
*/
/**
* The TaosRow object. Contains the data from a retrieved row from a database and functions that parse the data.
* @typedef {Object} TaosRow - A row of data retrieved from a table.
* @global
* @example
* var trow = new TaosRow(row);
* console.log(trow.data);
*/
function TaosRow(row) {
this.data = row;
this.length = row.length;
return this;
}
/**
* @typedef {Object} TaosField - A field/column's metadata from a table.
* @global
* @example
* var tfield = new TaosField(field);
* console.log(tfield.name);
*/
function TaosField(field) {
this._field = field;
this.name = field.name;
this.type = FieldTypes.getType(field.type);
return this;
}
/**
* A TaosTimestamp object, which is the standard date object with added functionality
* @global
* @memberof TaosObjects
* @param {Date} date - A Javascript date time object or the time in milliseconds past 1970-1-1 00:00:00.000
*/
class TaosTimestamp extends Date {
constructor(date, precision = 0) {
if (precision === 1) {
super(Math.floor(date / 1000));
this.precisionExtras = date % 1000;
} else if (precision === 2) {
// use BigInt to fix: 1623254400999999999 / 1000000 = 1623254401000 which not expected
super(parseInt(BigInt(date) / 1000000n));
// use BigInt to fix: 1625801548423914405 % 1000000 = 914496 which not expected (914405)
this.precisionExtras = parseInt(BigInt(date) % 1000000n);
} else {
super(parseInt(date));
}
this.precision = precision;
}
/**
* TDengine raw timestamp.
* @returns raw taos timestamp (int64)
*/
taosTimestamp() {
if (this.precision == 1) {
return (this * 1000 + this.precisionExtras);
} else if (this.precision == 2) {
return (this * 1000000 + this.precisionExtras);
} else {
return Math.floor(this);
}
}
/**
* Gets the microseconds of a Date.
* @return {Int} A microseconds integer
*/
getMicroseconds() {
if (this.precision == 1) {
return this.getMilliseconds() * 1000 + this.precisionExtras;
} else if (this.precision == 2) {
return this.getMilliseconds() * 1000 + this.precisionExtras / 1000;
} else {
return 0;
}
}
/**
* Gets the nanoseconds of a TaosTimestamp.
* @return {Int} A nanoseconds integer
*/
getNanoseconds() {
if (this.precision == 1) {
return this.getMilliseconds() * 1000000 + this.precisionExtras * 1000;
} else if (this.precision == 2) {
return this.getMilliseconds() * 1000000 + this.precisionExtras;
} else {
return 0;
}
}
/**
* @returns {String} a string for timestamp string format
*/
_precisionExtra() {
if (this.precision == 1) {
return String(this.precisionExtras).padStart(3, '0');
} else if (this.precision == 2) {
return String(this.precisionExtras).padStart(6, '0');
} else {
return '';
}
}
/**
* @function Returns the date into a string usable by TDengine
* @return {string} A Taos Timestamp String
*/
toTaosString() {
var tzo = -this.getTimezoneOffset(),
dif = tzo >= 0 ? '+' : '-',
pad = function (num) {
var norm = Math.floor(Math.abs(num));
return (norm < 10 ? '0' : '') + norm;
},
pad2 = function (num) {
var norm = Math.floor(Math.abs(num));
if (norm < 10) return '00' + norm;
if (norm < 100) return '0' + norm;
if (norm < 1000) return norm;
};
return this.getFullYear() +
'-' + pad(this.getMonth() + 1) +
'-' + pad(this.getDate()) +
' ' + pad(this.getHours()) +
':' + pad(this.getMinutes()) +
':' + pad(this.getSeconds()) +
'.' + pad2(this.getMilliseconds()) +
'' + this._precisionExtra();
}
/**
* Custom console.log
* @returns {String} string format for debug
*/
[util.inspect.custom](depth, opts) {
return this.toTaosString() + JSON.stringify({ precision: this.precision, precisionExtras: this.precisionExtras }, opts);
}
toString() {
return this.toTaosString();
}
}
module.exports = { TaosRow, TaosField, TaosTimestamp }
var TaosResult = require('./taosresult')
require('./globalfunc.js')
module.exports = TaosQuery;
/**
* @class TaosQuery
* @classdesc The TaosQuery class is one level above the TDengine Cursor in that it makes sure to generally return promises from functions, and wrap
* all data with objects such as wrapping a row of data with Taos Row. This is meant to enable an higher level API that allows additional
* functionality and save time whilst also making it easier to debug and enter less problems with the use of promises.
* @param {string} query - Query to construct object from
* @param {TDengineCursor} cursor - The cursor from which this query will execute from
* @param {boolean} execute - Whether or not to immedietely execute the query synchronously and fetch all results. Default is false.
* @property {string} query - The current query in string format the TaosQuery object represents
* @return {TaosQuery}
* @since 1.0.6
*/
function TaosQuery(query = "", cursor = null, execute = false) {
this.query = query;
this._cursor = cursor;
if (execute == true) {
return this.execute();
}
return this;
}
/**
* Executes the query object and returns a Promise
* @memberof TaosQuery
* @return {Promise<TaosResult>} A promise that resolves with a TaosResult object, or rejects with an error
* @since 1.0.6
*/
TaosQuery.prototype.execute = async function execute() {
var taosQuery = this; //store the current instance of taosQuery to avoid async issues?
var executionPromise = new Promise(function(resolve, reject) {
let data = [];
let fields = [];
let result;
try {
taosQuery._cursor.execute(taosQuery.query);
if (taosQuery._cursor._fields) fields = taosQuery._cursor._fields;
if (taosQuery._cursor._result != null) data = taosQuery._cursor.fetchall();
result = new TaosResult(data, fields)
}
catch(err) {
reject(err);
}
resolve(result)
});
return executionPromise;
}
/**
* Executes the query object asynchronously and returns a Promise. Completes query to completion.
* @memberof TaosQuery
* @param {Object} options - Execution options
* @return {Promise<TaosResult>} A promise that resolves with a TaosResult object, or rejects with an error
* @since 1.2.0
*/
TaosQuery.prototype.execute_a = async function execute_a(options = {}) {
var executionPromise = new Promise( (resolve, reject) => {
});
var fres;
var frej;
var fetchPromise = new Promise( (resolve, reject) => {
fres = resolve;
frej = reject;
});
let asyncCallbackFetchall = async function(param, res, numOfRows, blocks) {
if (numOfRows > 0) {
// Likely a query like insert
fres();
}
else {
fres(new TaosResult(blocks.data, blocks.fields));
}
}
let asyncCallback = async function(param, res, code) {
//upon success, we fetchall results
this._cursor.fetchall_a(res, options, asyncCallbackFetchall, {});
}
this._cursor.execute_a(this.query, asyncCallback.bind(this), {});
return fetchPromise;
}
/**
* Bind arguments to the query and automatically parses them into the right format
* @param {array | ...args} args - A number of arguments to bind to each ? in the query
* @return {TaosQuery}
* @example
* // An example of binding a javascript date and a number to a query
* var query = cursor.query("select count(*) from meterinfo.meters where ts <= ? and areaid = ?").bind(new Date(), 3);
* var promise1 = query.execute();
* promise1.then(function(result) {
* result.pretty(); // Log the prettified version of the results.
* });
* @since 1.0.6
*/
TaosQuery.prototype.bind = function bind(f, ...args) {
if (typeof f == 'object' && f.constructor.name != 'Array') args.unshift(f); //param is not an array object
else if (typeof f != 'object') args.unshift(f);
else { args = f; }
args.forEach(function(arg) {
if (arg.constructor.name == 'TaosTimestamp') arg = "\"" + arg.toTaosString() + "\"";
else if (arg.constructor.name == 'Date') arg = "\"" + toTaosTSString(arg) + "\"";
else if (typeof arg == 'string') arg = "\"" + arg + "\"";
this.query = this.query.replace(/\?/,arg);
}, this);
return this;
}
require('./globalfunc.js')
const TaosObjects = require('./taosobjects');
const TaosRow = TaosObjects.TaosRow;
const TaosField = TaosObjects.TaosField;
module.exports = TaosResult;
/**
* @class TaosResult
* @classdesc A TaosResult class consts of the row data and the fields metadata, all wrapped under various objects for higher functionality.
* @param {Array<TaosRow>} data - Array of result rows
* @param {Array<TaosField>} fields - Array of field meta data
* @property {Array<TaosRow>} data - Array of TaosRows forming the result data (this does not include field meta data)
* @property {Array<TaosField>} fields - Array of TaosFields forming the fields meta data array.
* @return {TaosResult}
* @since 1.0.6
*/
function TaosResult(data, fields) {
this.data = data.map(row => new TaosRow(row));
this.rowcount = this.data.length;
this.fields = fields.map(field => new TaosField(field));
}
/**
* Pretty print data and the fields meta data as if you were using the taos shell
* @memberof TaosResult
* @function pretty
* @since 1.0.6
*/
TaosResult.prototype.pretty = function pretty() {
let fieldsStr = "";
let sizing = [];
this.fields.forEach((field,i) => {
if (field._field.type == 8 || field._field.type == 10){
sizing.push(Math.max(field.name.length, field._field.bytes));
}
else {
sizing.push(Math.max(field.name.length, suggestedMinWidths[field._field.type]));
}
fieldsStr += fillEmpty(Math.floor(sizing[i]/2 - field.name.length / 2)) + field.name + fillEmpty(Math.ceil(sizing[i]/2 - field.name.length / 2)) + " | ";
});
var sumLengths = sizing.reduce((a,b)=> a+=b,(0)) + sizing.length * 3;
console.log("\n" + fieldsStr);
console.log(printN("=",sumLengths));
this.data.forEach(row => {
let rowStr = "";
row.data.forEach((entry, i) => {
if (this.fields[i]._field.type == 9) {
entry = entry.toTaosString();
} else {
entry = entry == null ? 'null' : entry.toString();
}
rowStr += entry
rowStr += fillEmpty(sizing[i] - entry.length) + " | ";
});
console.log(rowStr);
});
}
const suggestedMinWidths = {
0: 4,
1: 4,
2: 4,
3: 6,
4: 11,
5: 12,
6: 24,
7: 24,
8: 10,
9: 25,
10: 10,
}
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;
}
# TDengine Node.js connector
[![minzip](https://img.shields.io/bundlephobia/minzip/td2.0-connector.svg)](https://github.com/taosdata/TDengine/tree/master/src/connector/nodejs) [![NPM](https://img.shields.io/npm/l/td2.0-connector.svg)](https://github.com/taosdata/TDengine/#what-is-tdengine)
This is the Node.js library that lets you connect to [TDengine](https://www.github.com/taosdata/tdengine) 2.0 version. It is built so that you can use as much of it as you want or as little of it as you want through providing an extensive API. If you want the raw data in the form of an array of arrays for the row data retrieved from a table, you can do that. If you want to wrap that data with objects that allow you easily manipulate and display data such as using a prettifier function, you can do that!
## Installation
To get started, just type in the following to install the connector through [npm](https://www.npmjs.com/)
```cmd
npm install td2.0-connector
```
To interact with TDengine, we make use of the [node-gyp](https://github.com/nodejs/node-gyp) library. To install, you will need to install the following depending on platform (the following instructions are quoted from node-gyp)
### On Linux
- `python` (`v2.7` recommended, `v3.x.x` is **not** supported)
- `make`
- A proper C/C++ compiler toolchain, like [GCC](https://gcc.gnu.org)
- `node` (between `v10.x` and `v11.x`, other version has some dependency compatibility problems)
### On macOS
- `python` (`v2.7` recommended, `v3.x.x` is **not** supported) (already installed on macOS)
- Xcode
- You also need to install the
```
Command Line Tools
```
via Xcode. You can find this under the menu
```
Xcode -> Preferences -> Locations
```
(or by running
```
xcode-select --install
```
in your Terminal)
- This step will install `gcc` and the related toolchain containing `make`
### On Windows
#### Option 1
Install all the required tools and configurations using Microsoft's [windows-build-tools](https://github.com/felixrieseberg/windows-build-tools) using `npm install --global --production windows-build-tools` from an elevated PowerShell or CMD.exe (run as Administrator).
#### Option 2
Install tools and configuration manually:
- Install Visual C++ Build Environment: [Visual Studio Build Tools](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools) (using "Visual C++ build tools" workload) or [Visual Studio 2017 Community](https://visualstudio.microsoft.com/pl/thank-you-downloading-visual-studio/?sku=Community) (using the "Desktop development with C++" workload)
- Install [Python 2.7](https://www.python.org/downloads/) (`v3.x.x` is not supported), and run `npm config set python python2.7` (or see below for further instructions on specifying the proper Python version and path.)
- Launch cmd, `npm config set msvs_version 2017`
If the above steps didn't work for you, please visit [Microsoft's Node.js Guidelines for Windows](https://github.com/Microsoft/nodejs-guidelines/blob/master/windows-environment.md#compiling-native-addon-modules) for additional tips.
To target native ARM64 Node.js on Windows 10 on ARM, add the components "Visual C++ compilers and libraries for ARM64" and "Visual C++ ATL for ARM64".
## Usage
The following is a short summary of the basic usage of the connector, the full api and documentation can be found [here](http://docs.taosdata.com/node)
### Connection
To use the connector, first require the library ```td2.0-connector```. Running the function ```taos.connect``` with the connection options passed in as an object will return a TDengine connection object. The required connection option is ```host```, other options if not set, will be the default values as shown below.
A cursor also needs to be initialized in order to interact with TDengine from Node.js.
```javascript
const taos = require('td2.0-connector');
var conn = taos.connect({host:"127.0.0.1", user:"root", password:"taosdata", config:"/etc/taos",port:0})
var cursor = conn.cursor(); // Initializing a new cursor
```
Close a connection
```javascript
conn.close();
```
### Queries
We can now start executing simple queries through the ```cursor.query``` function, which returns a TaosQuery object.
```javascript
var query = cursor.query('show databases;')
```
We can get the results of the queries through the ```query.execute()``` function, which returns a promise that resolves with a TaosResult object, which contains the raw data and additional functionalities such as pretty printing the results.
```javascript
var promise = query.execute();
promise.then(function(result) {
result.pretty(); //logs the results to the console as if you were in the taos shell
});
```
You can also query by binding parameters to a query by filling in the question marks in a string as so. The query will automatically parse what was binded and convert it to the proper format for use with TDengine
```javascript
var query = cursor.query('select * from meterinfo.meters where ts <= ? and areaid = ?;').bind(new Date(), 5);
query.execute().then(function(result) {
result.pretty();
})
```
The TaosQuery object can also be immediately executed upon creation by passing true as the second argument, returning a promise instead of a TaosQuery.
```javascript
var promise = cursor.query('select * from meterinfo.meters where v1 = 30;', true)
promise.then(function(result) {
result.pretty();
})
```
If you want to execute queries without objects being wrapped around the data, use ```cursor.execute()``` directly and ```cursor.fetchall()``` to retrieve data if there is any.
```javascript
cursor.execute('select count(*), avg(v1), min(v2) from meterinfo.meters where ts >= \"2019-07-20 00:00:00.000\";');
var data = cursor.fetchall();
console.log(cursor.fields); // Latest query's Field metadata is stored in cursor.fields
console.log(cursor.data); // Latest query's result data is stored in cursor.data, also returned by fetchall.
```
### Async functionality
Async queries can be performed using the same functions such as `cursor.execute`, `TaosQuery.query`, but now with `_a` appended to them.
Say you want to execute an two async query on two separate tables, using `cursor.query`, you can do that and get a TaosQuery object, which upon executing with the `execute_a` function, returns a promise that resolves with a TaosResult object.
```javascript
var promise1 = cursor.query('select count(*), avg(v1), avg(v2) from meter1;').execute_a()
var promise2 = cursor.query('select count(*), avg(v1), avg(v2) from meter2;').execute_a();
promise1.then(function(result) {
result.pretty();
})
promise2.then(function(result) {
result.pretty();
})
```
## Example
An example of using the NodeJS connector to create a table with weather data and create and execute queries can be found [here](https://github.com/taosdata/TDengine/tree/master/tests/examples/nodejs/node-example.js) (The preferred method for using the connector)
An example of using the NodeJS connector to achieve the same things but without all the object wrappers that wrap around the data returned to achieve higher functionality can be found [here](https://github.com/taosdata/TDengine/tree/master/tests/examples/nodejs/node-example-raw.js)
## Contributing to TDengine
Please follow the [contribution guidelines](https://github.com/taosdata/TDengine/blob/master/CONTRIBUTING.md) to contribute to the project.
## License
[GNU AGPL v3.0](http://www.gnu.org/licenses/agpl-3.0.html)
var TDengineConnection = require('./nodetaos/connection.js')
module.exports.connect = function (connection={}) {
return new TDengineConnection(connection);
}
const taos = require('../tdengine');
var conn = taos.connect({config:"/etc/taos"});
var c1 = conn.cursor();
function checkData(sql,row,col,data){
c1.execute(sql)
var d = c1.fetchall();
let checkdata = d[row][col];
if (checkdata == data) {
// console.log('check pass')
}
else{
console.log('check failed')
console.log(checkdata)
console.log(data)
}
}
// nano basic case
c1.execute('reset query cache')
c1.execute('drop database if exists db')
c1.execute('create database db precision "ns";')
c1.execute('use db');
c1.execute('create table tb (ts timestamp, speed int)')
c1.execute('insert into tb values(\'2021-06-10 00:00:00.100000001\', 1);')
c1.execute('insert into tb values(1623254400150000000, 2);')
c1.execute('import into tb values(1623254400300000000, 3);')
c1.execute('import into tb values(1623254400299999999, 4);')
c1.execute('insert into tb values(1623254400300000001, 5);')
c1.execute('insert into tb values(1623254400999999999, 7);')
c1.execute('insert into tb values(1623254400123456789, 8);')
sql = 'select * from tb;'
console.log('*******************************************')
console.log('this is area about checkdata result')
//check data about insert data
checkData(sql,0,0,'2021-06-10 00:00:00.100000001')
checkData(sql,1,0,'2021-06-10 00:00:00.123456789')
checkData(sql,2,0,'2021-06-10 00:00:00.150000000')
checkData(sql,3,0,'2021-06-10 00:00:00.299999999') //error
checkData(sql,4,0,'2021-06-10 00:00:00.300000000')
checkData(sql,5,0,'2021-06-10 00:00:00.300000001')
checkData(sql,6,0,'2021-06-10 00:00:00.999999999') //error
// // us basic case
// c1.execute('reset query cache')
// c1.execute('drop database if exists db')
// c1.execute('create database db precision "us";')
// c1.execute('use db');
// c1.execute('create table tb (ts timestamp, speed int)')
// c1.execute('insert into tb values(\'2021-06-10 00:00:00.100001\', 1);')
// c1.execute('insert into tb values(1623254400150000, 2);')
// c1.execute('import into tb values(1623254400300000, 3);')
// c1.execute('import into tb values(1623254400299999, 4);')
// c1.execute('insert into tb values(1623254400300001, 5);')
// c1.execute('insert into tb values(1623254400999999, 7);')
// c1.execute('insert into tb values(1623254400123789, 8);')
// sql = 'select * from tb;'
// console.log('*******************************************')
// //check data about insert data
// checkData(sql,0,0,'2021-06-10 00:00:00.100001')
// checkData(sql,1,0,'2021-06-10 00:00:00.123789')
// checkData(sql,2,0,'2021-06-10 00:00:00.150000')
// checkData(sql,3,0,'2021-06-10 00:00:00.299999')
// checkData(sql,4,0,'2021-06-10 00:00:00.300000')
// checkData(sql,5,0,'2021-06-10 00:00:00.300001')
// checkData(sql,6,0,'2021-06-10 00:00:00.999999')
// console.log('*******************************************')
// // ms basic case
// c1.execute('reset query cache')
// c1.execute('drop database if exists db')
// c1.execute('create database db precision "ms";')
// c1.execute('use db');
// c1.execute('create table tb (ts timestamp, speed int)')
// c1.execute('insert into tb values(\'2021-06-10 00:00:00.101\', 1);')
// c1.execute('insert into tb values(1623254400150, 2);')
// c1.execute('import into tb values(1623254400300, 3);')
// c1.execute('import into tb values(1623254400299, 4);')
// c1.execute('insert into tb values(1623254400301, 5);')
// c1.execute('insert into tb values(1623254400789, 7);')
// c1.execute('insert into tb values(1623254400999, 8);')
// sql = 'select * from tb;'
// console.log('*******************************************')
// console.log('this is area about checkdata result')
// //check data about insert data
// checkData(sql,0,0,'2021-06-10 00:00:00.101')
// checkData(sql,1,0,'2021-06-10 00:00:00.150')
// checkData(sql,2,0,'2021-06-10 00:00:00.299')
// checkData(sql,3,0,'2021-06-10 00:00:00.300')
// checkData(sql,4,0,'2021-06-10 00:00:00.301')
// checkData(sql,5,0,'2021-06-10 00:00:00.789')
// checkData(sql,6,0,'2021-06-10 00:00:00.999')
console.log('*******************************************')
// offfical query result to show
// console.log('this is area about fetch all data')
// var query = c1.query(sql)
// var promise = query.execute();
// promise.then(function(result) {
// result.pretty();
// });
console.log('*******************************************')
// checkData(sql,3,1,3)
// checkData(sql,4,1,5)
// checkData(sql,5,1,7)
// checkData(3,1,3)
// checkData(4,1,5)
// checkData(5,1,7)
// tdSql.query('select count(*) from tb where ts > 1623254400100000000 and ts < 1623254400100000002;')
// tdSql.checkData(0,0,1)
// tdSql.query('select count(*) from tb where ts > \'2021-06-10 0:00:00.100000001\' and ts < \'2021-06-10 0:00:00.160000000\';')
// tdSql.checkData(0,0,1)
// tdSql.query('select count(*) from tb where ts > 1623254400100000000 and ts < 1623254400150000000;')
// tdSql.checkData(0,0,1)
// tdSql.query('select count(*) from tb where ts > \'2021-06-10 0:00:00.100000000\' and ts < \'2021-06-10 0:00:00.150000000\';')
// tdSql.checkData(0,0,1)
// tdSql.query('select count(*) from tb where ts > 1623254400400000000;')
// tdSql.checkData(0,0,1)
// tdSql.query('select count(*) from tb where ts < \'2021-06-10 00:00:00.400000000\';')
// tdSql.checkData(0,0,5)
// tdSql.query('select count(*) from tb where ts > now + 400000000b;')
// tdSql.checkRows(0)
// tdSql.query('select count(*) from tb where ts >= \'2021-06-10 0:00:00.100000001\';')
// tdSql.checkData(0,0,6)
// tdSql.query('select count(*) from tb where ts <= 1623254400300000000;')
// tdSql.checkData(0,0,4)
// tdSql.query('select count(*) from tb where ts = \'2021-06-10 0:00:00.000000000\';')
// tdSql.checkRows(0)
// tdSql.query('select count(*) from tb where ts = 1623254400150000000;')
// tdSql.checkData(0,0,1)
// tdSql.query('select count(*) from tb where ts = \'2021-06-10 0:00:00.100000001\';')
// tdSql.checkData(0,0,1)
// tdSql.query('select count(*) from tb where ts between 1623254400000000000 and 1623254400400000000;')
// tdSql.checkData(0,0,5)
// tdSql.query('select count(*) from tb where ts between \'2021-06-10 0:00:00.299999999\' and \'2021-06-10 0:00:00.300000001\';')
// tdSql.checkData(0,0,3)
// tdSql.query('select avg(speed) from tb interval(5000000000b);')
// tdSql.checkRows(1)
// tdSql.query('select avg(speed) from tb interval(100000000b)')
// tdSql.checkRows(4)
// tdSql.error('select avg(speed) from tb interval(1b);')
// tdSql.error('select avg(speed) from tb interval(999b);')
// tdSql.query('select avg(speed) from tb interval(1000b);')
// tdSql.checkRows(5)
// tdSql.query('select avg(speed) from tb interval(1u);')
// tdSql.checkRows(5)
// tdSql.query('select avg(speed) from tb interval(100000000b) sliding (100000000b);')
// tdSql.checkRows(4)
// tdSql.query('select last(*) from tb')
// tdSql.checkData(0,0, '2021-06-10 0:00:00.999999999')
// tdSql.checkData(0,0, 1623254400999999999)
// tdSql.query('select first(*) from tb')
// tdSql.checkData(0,0, 1623254400100000001)
// tdSql.checkData(0,0, '2021-06-10 0:00:00.100000001')
// c1.execute('insert into tb values(now + 500000000b, 6);')
// tdSql.query('select * from tb;')
// tdSql.checkRows(7)
// tdLog.debug('testing nanosecond support in other timestamps')
// c1.execute('create table tb2 (ts timestamp, speed int, ts2 timestamp);')
// c1.execute('insert into tb2 values(\'2021-06-10 0:00:00.100000001\', 1, \'2021-06-11 0:00:00.100000001\');')
// c1.execute('insert into tb2 values(1623254400150000000, 2, 1623340800150000000);')
// c1.execute('import into tb2 values(1623254400300000000, 3, 1623340800300000000);')
// c1.execute('import into tb2 values(1623254400299999999, 4, 1623340800299999999);')
// c1.execute('insert into tb2 values(1623254400300000001, 5, 1623340800300000001);')
// c1.execute('insert into tb2 values(1623254400999999999, 7, 1623513600999999999);')
// tdSql.query('select * from tb2;')
// tdSql.checkData(0,0,'2021-06-10 0:00:00.100000001')
// tdSql.checkData(1,0,'2021-06-10 0:00:00.150000000')
// tdSql.checkData(2,1,4)
// tdSql.checkData(3,1,3)
// tdSql.checkData(4,2,'2021-06-11 00:00:00.300000001')
// tdSql.checkData(5,2,'2021-06-13 00:00:00.999999999')
// tdSql.checkRows(6)
// tdSql.query('select count(*) from tb2 where ts2 > 1623340800000000000 and ts2 < 1623340800150000000;')
// tdSql.checkData(0,0,1)
// tdSql.query('select count(*) from tb2 where ts2 > \'2021-06-11 0:00:00.100000000\' and ts2 < \'2021-06-11 0:00:00.100000002\';')
// tdSql.checkData(0,0,1)
// tdSql.query('select count(*) from tb2 where ts2 > 1623340800500000000;')
// tdSql.checkData(0,0,1)
// tdSql.query('select count(*) from tb2 where ts2 < \'2021-06-11 0:00:00.400000000\';')
// tdSql.checkData(0,0,5)
// tdSql.query('select count(*) from tb2 where ts2 > now + 400000000b;')
// tdSql.checkRows(0)
// tdSql.query('select count(*) from tb2 where ts2 >= \'2021-06-11 0:00:00.100000001\';')
// tdSql.checkData(0,0,6)
// tdSql.query('select count(*) from tb2 where ts2 <= 1623340800400000000;')
// tdSql.checkData(0,0,5)
// tdSql.query('select count(*) from tb2 where ts2 = \'2021-06-11 0:00:00.000000000\';')
// tdSql.checkRows(0)
// tdSql.query('select count(*) from tb2 where ts2 = \'2021-06-11 0:00:00.300000001\';')
// tdSql.checkData(0,0,1)
// tdSql.query('select count(*) from tb2 where ts2 = 1623340800300000001;')
// tdSql.checkData(0,0,1)
// tdSql.query('select count(*) from tb2 where ts2 between 1623340800000000000 and 1623340800450000000;')
// tdSql.checkData(0,0,5)
// tdSql.query('select count(*) from tb2 where ts2 between \'2021-06-11 0:00:00.299999999\' and \'2021-06-11 0:00:00.300000001\';')
// tdSql.checkData(0,0,3)
// tdSql.query('select count(*) from tb2 where ts2 <> 1623513600999999999;')
// tdSql.checkData(0,0,5)
// tdSql.query('select count(*) from tb2 where ts2 <> \'2021-06-11 0:00:00.100000001\';')
// tdSql.checkData(0,0,5)
// tdSql.query('select count(*) from tb2 where ts2 <> \'2021-06-11 0:00:00.100000000\';')
// tdSql.checkData(0,0,6)
// tdSql.query('select count(*) from tb2 where ts2 != 1623513600999999999;')
// tdSql.checkData(0,0,5)
// tdSql.query('select count(*) from tb2 where ts2 != \'2021-06-11 0:00:00.100000001\';')
// tdSql.checkData(0,0,5)
// tdSql.query('select count(*) from tb2 where ts2 != \'2021-06-11 0:00:00.100000000\';')
// tdSql.checkData(0,0,6)
// c1.execute('insert into tb2 values(now + 500000000b, 6, now +2d);')
// tdSql.query('select * from tb2;')
// tdSql.checkRows(7)
// tdLog.debug('testing ill nanosecond format handling')
// c1.execute('create table tb3 (ts timestamp, speed int);')
// tdSql.error('insert into tb3 values(16232544001500000, 2);')
// c1.execute('insert into tb3 values(\'2021-06-10 0:00:00.123456\', 2);')
// tdSql.query('select * from tb3 where ts = \'2021-06-10 0:00:00.123456000\';')
// tdSql.checkRows(1)
// c1.execute('insert into tb3 values(\'2021-06-10 0:00:00.123456789000\', 2);')
// tdSql.query('select * from tb3 where ts = \'2021-06-10 0:00:00.123456789\';')
// tdSql.checkRows(1)
// # check timezone support
// c1.execute('use db;')
// c1.execute('create stable st (ts timestamp ,speed float ) tags(time timestamp ,id int);')
// c1.execute('insert into tb1 using st tags("2021-06-10 0:00:00.123456789" , 1 ) values("2021-06-10 0:00:00.123456789+07:00" , 1.0);' )
// tdSql.query("select first(*) from tb1;")
// tdSql.checkData(0,0,1623258000123456789)
// c1.execute('insert into tb1 using st tags("2021-06-10 0:00:00.123456789" , 1 ) values("2021-06-10T0:00:00.123456789+06:00" , 2.0);' )
// tdSql.query("select last(*) from tb1;")
// tdSql.checkData(0,0,1623261600123456789)
// c1.execute('create database usdb precision "us";')
// c1.execute('use usdb;')
// c1.execute('create stable st (ts timestamp ,speed float ) tags(time timestamp ,id int);')
// c1.execute('insert into tb1 using st tags("2021-06-10 0:00:00.123456" , 1 ) values("2021-06-10 0:00:00.123456+07:00" , 1.0);' )
// res = tdSql.getResult("select first(*) from tb1;")
// print(res)
// if res == [(datetime.datetime(2021, 6, 10, 1, 0, 0, 123456), 1.0)]:
// tdLog.info('check timezone pass about us database')
// c1.execute('create database msdb precision "ms";')
// c1.execute('use msdb;')
// c1.execute('create stable st (ts timestamp ,speed float ) tags(time timestamp ,id int);')
// c1.execute('insert into tb1 using st tags("2021-06-10 0:00:00.123" , 1 ) values("2021-06-10 0:00:00.123+07:00" , 1.0);' )
// res = tdSql.getResult("select first(*) from tb1;")
// print(res)
// if res ==[(datetime.datetime(2021, 6, 10, 1, 0, 0, 123000), 1.0)]:
// tdLog.info('check timezone pass about ms database')
// 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);
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();
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 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);
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 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
import pyodbc
import argparse
import sys
parser = argparse.ArgumentParser(description='Access TDengine via ODBC.')
parser.add_argument('--DSN', help='DSN to use')
parser.add_argument('--UID', help='UID to use')
parser.add_argument('--PWD', help='PWD to use')
parser.add_argument('--Server', help='Server to use')
parser.add_argument('-C', metavar='CONNSTR', help='Connection string to use')
args = parser.parse_args()
a = 'DSN=%s'%args.DSN if args.DSN else None
b = 'UID=%s'%args.UID if args.UID else None
c = 'PWD=%s'%args.PWD if args.PWD else None
d = 'Server=%s'%args.Server if args.Server else None
conn_str = ';'.join(filter(None, [a,b,c,d])) if args.DSN else None
conn_str = conn_str if conn_str else args.C
if not conn_str:
parser.print_help(file=sys.stderr)
exit()
print('connecting: [%s]' % conn_str)
cnxn = pyodbc.connect(conn_str, autocommit=True)
cnxn.setdecoding(pyodbc.SQL_CHAR, encoding='utf-8')
cursor = cnxn.cursor()
cursor.execute("drop database if exists db");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("create database db");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("create table db.mt (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(10), blob nchar(10))");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("insert into db.mt values('2020-10-13 06:44:00.123', 1, 127, 32767, 2147483647, 32769, 123.456, 789.987, 'hello', 'helloworld')")
cursor.close()
cursor = cnxn.cursor()
cursor.execute("insert into db.mt values(?,?,?,?,?,?,?,?,?,?)", "2020-10-13 07:06:00.234", 0, 127, 32767, 32768, 32769, 123.456, 789.987, "hel后lo".encode('utf-8'), "wo哈rlxd129")
##cursor.execute("insert into db.mt values(?,?,?,?,?,?,?,?,?,?)", 1502535178128, 9223372036854775807, 127, 32767, 32768, 32769, 123.456, 789.987, "hel后lo".encode('utf-8'), "wo哈rlxd123");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("""
INSERT INTO db.mt (ts,b,v1,v2,v4,v8,f4,f8,bin,blob) values (?,?,?,?,?,?,?,?,?,?)
""",
"2020-12-12 00:00:00",
'true',
'-127',
'-32767',
'-2147483647',
'-9223372036854775807',
'-1.23e10',
'-11.23e6',
'abcdefghij'.encode('utf-8'),
"人啊大发测试及abc")
cursor.close()
cursor = cnxn.cursor()
cursor.execute("drop database if exists db");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("create database db");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("create table db.t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(4), blob nchar(4))");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("insert into db.t values('2020-10-13 06:44:00', 1, 127, 32767, 32768, 32769, 123.456, 789.987, 'hell', 'w我你z')")
cursor.close()
cursor = cnxn.cursor()
cursor.execute("create table db.v (ts timestamp, v1 tinyint, v2 smallint, name nchar(10), ts2 timestamp)")
cursor.close()
params = [ ('2020-10-16 00:00:00.123', 19, '2111-01-02 01:02:03.123'),
('2020-10-16 00:00:01', 41, '2111-01-02 01:02:03.423'),
('2020-10-16 00:00:02', 57, '2111-01-02 01:02:03.153'),
('2020-10-16 00:00:03.009', 26, '2111-01-02 01:02:03.623') ]
cursor = cnxn.cursor()
cursor.fast_executemany = True
print('py:...................')
cursor.executemany("insert into db.v (ts, v1, ts2) values (?, ?, ?)", params)
print('py:...................')
cursor.close()
## cursor = cnxn.cursor()
## cursor.execute("SELECT * from db.v where v1 > ?", 4)
## row = cursor.fetchone()
## while row:
## print(row)
## row = cursor.fetchone()
## cursor.close()
##
## cursor = cnxn.cursor()
## cursor.execute("SELECT * from db.v where v1 > ?", '5')
## row = cursor.fetchone()
## while row:
## print(row)
## row = cursor.fetchone()
## cursor.close()
package main
import (
"context"
"database/sql"
"flag"
"log"
"os"
"os/signal"
"time"
_ "github.com/alexbrainman/odbc"
)
var pool *sql.DB // Database connection pool.
func main() {
id := flag.Int64("id", 32768, "person ID to find")
dsn := flag.String("dsn", os.Getenv("DSN"), "connection data source name")
flag.Parse()
if len(*dsn) == 0 {
log.Fatal("missing dsn flag")
}
if *id == 0 {
log.Fatal("missing person ID")
}
var err error
// Opening a driver typically will not attempt to connect to the database.
pool, err = sql.Open("odbc", *dsn)
if err != nil {
// This will not be a connection error, but a DSN parse error or
// another initialization error.
log.Fatal("unable to use data source name", err)
}
defer pool.Close()
pool.SetConnMaxLifetime(0)
pool.SetMaxIdleConns(3)
pool.SetMaxOpenConns(3)
ctx, stop := context.WithCancel(context.Background())
defer stop()
appSignal := make(chan os.Signal, 3)
signal.Notify(appSignal, os.Interrupt)
go func() {
select {
case <-appSignal:
stop()
}
}()
Ping(ctx)
Query(ctx, *id)
}
// Ping the database to verify DSN provided by the user is valid and the
// server accessible. If the ping fails exit the program with an error.
func Ping(ctx context.Context) {
ctx, cancel := context.WithTimeout(ctx, 1*time.Second)
defer cancel()
if err := pool.PingContext(ctx); err != nil {
log.Fatalf("unable to connect to database: %v", err)
}
}
// Query the database for the information requested and prints the results.
// If the query fails exit the program with an error.
func Query(ctx context.Context, id int64) {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
var name string
err := pool.QueryRowContext(ctx, "select name from m.t").Scan(&name)
if err != nil {
log.Fatal("unable to execute search query", err)
}
log.Println("name=", name)
}
import pyodbc
import argparse
import sys
parser = argparse.ArgumentParser(description='Access TDengine via ODBC.')
parser.add_argument('--DSN', help='DSN to use')
parser.add_argument('--UID', help='UID to use')
parser.add_argument('--PWD', help='PWD to use')
parser.add_argument('--Server', help='Server to use')
parser.add_argument('-C', metavar='CONNSTR', help='Connection string to use')
args = parser.parse_args()
a = 'DSN=%s'%args.DSN if args.DSN else None
b = 'UID=%s'%args.UID if args.UID else None
c = 'PWD=%s'%args.PWD if args.PWD else None
d = 'Server=%s'%args.Server if args.Server else None
conn_str = ';'.join(filter(None, [a,b,c,d])) if args.DSN else None
conn_str = conn_str if conn_str else args.C
if not conn_str:
parser.print_help(file=sys.stderr)
exit()
print('connecting: [%s]' % conn_str)
cnxn = pyodbc.connect(conn_str, autocommit=True)
cnxn.setdecoding(pyodbc.SQL_CHAR, encoding='utf-8')
cursor = cnxn.cursor()
cursor.execute("drop database if exists db");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("create database db");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("create table db.mt (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(10), blob nchar(10))");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("insert into db.mt values('2020-10-13 06:44:00.123', 1, 127, 32767, 2147483647, 32769, 123.456, 789.987, 'hello', 'helloworld')")
cursor.close()
cursor = cnxn.cursor()
cursor.execute("insert into db.mt values(?,?,?,?,?,?,?,?,?,?)", "2020-10-13 07:06:00.234", 0, 127, 32767, 32768, 32769, 123.456, 789.987, "hel后lo".encode('utf-8'), "wo哈rlxd129")
##cursor.execute("insert into db.mt values(?,?,?,?,?,?,?,?,?,?)", 1502535178128, 9223372036854775807, 127, 32767, 32768, 32769, 123.456, 789.987, "hel后lo".encode('utf-8'), "wo哈rlxd123");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("""
INSERT INTO db.mt (ts,b,v1,v2,v4,v8,f4,f8,bin,blob) values (?,?,?,?,?,?,?,?,?,?)
""",
"2020-12-12 00:00:00",
'true',
'-127',
'-32767',
'-2147483647',
'-9223372036854775807',
'-1.23e10',
'-11.23e6',
'abcdefghij'.encode('utf-8'),
"人啊大发测试及abc")
cursor.close()
cursor = cnxn.cursor()
cursor.execute("drop database if exists db");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("create database db");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("create table db.t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(4), blob nchar(4))");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("insert into db.t values('2020-10-13 06:44:00', 1, 127, 32767, 32768, 32769, 123.456, 789.987, 'hell', 'w我你z')")
cursor.close()
cursor = cnxn.cursor()
cursor.execute("create table db.v (ts timestamp, v1 tinyint, v2 smallint, name nchar(10), ts2 timestamp)")
cursor.close()
cursor = cnxn.cursor()
cursor.execute("select * from db.v")
cursor.close()
params = [ ('2020-10-16 00:00:00.123', 19, '2111-01-02 01:02:03.123'),
('2020-10-16 00:00:01', 41, '2111-01-02 01:02:03.423'),
('2020-10-16 00:00:02', 57, '2111-01-02 01:02:03.153'),
('2020-10-16 00:00:03.009', 26, '2111-01-02 01:02:03.623') ]
cursor = cnxn.cursor()
cursor.fast_executemany = True
print('py:...................')
cursor.executemany("insert into db.v (ts, v1, ts2) values (?, ?, ?)", params)
print('py:...................')
cursor.close()
## cursor = cnxn.cursor()
## cursor.execute("SELECT * from db.v where v1 > ?", 4)
## row = cursor.fetchone()
## while row:
## print(row)
## row = cursor.fetchone()
## cursor.close()
##
## cursor = cnxn.cursor()
## cursor.execute("SELECT * from db.v where v1 > ?", '5')
## row = cursor.fetchone()
## while row:
## print(row)
## row = cursor.fetchone()
## cursor.close()
@echo off
echo ==== start Go connector test cases test ====
cd /d %~dp0
......@@ -18,3 +19,10 @@ rem case002.bat
:: cd case002
:: case002.bat
rem cd nanosupport
rem nanoCase.bat
:: cd nanosupport
:: nanoCase.bat
\ No newline at end of file
......@@ -19,3 +19,4 @@ go env -w GOPROXY=https://goproxy.io,direct
bash ./case001/case001.sh $severIp $serverPort
bash ./case002/case002.sh $severIp $serverPort
#bash ./case003/case003.sh $severIp $serverPort
bash ./nanosupport/nanoCase.sh $severIp $serverPort
......@@ -12,7 +12,6 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
......
......@@ -15,8 +15,7 @@ script_dir="$(dirname $(readlink -f $0))"
###### step 3: start build
cd $script_dir
rm -f go.*
go mod init demotest > /dev/null 2>&1
go mod tidy > /dev/null 2>&1
go build > /dev/null 2>&1
go mod init demotest
go build
sleep 1s
./demotest -h $1 -p $2
@echo off
echo ==== start run cases001.go
echo ==== start run cases002.go
del go.*
go mod init demotest
......
......@@ -43,10 +43,9 @@ func main() {
os.Exit(1)
}
defer db.Close()
db.Exec("drop if exists database test")
db.Exec("create if not exists database test")
db.Exec("drop database if exists test")
db.Exec("create database if not exists test ")
db.Exec("use test")
db.Exec("drop if exists database test")
db.Exec("create table test (ts timestamp ,level int)")
for i := 0; i < 10; i++ {
sqlcmd := fmt.Sprintf("insert into test values(%d,%d)", ts+i, i)
......
#!/bin/bash
echo "==== start run cases001.go"
echo "==== start run cases002.go"
set +e
#set -x
......
package connector
import (
"context"
"fmt"
"reflect"
"time"
"github.com/taosdata/go-utils/log"
"github.com/taosdata/go-utils/tdengine/config"
"github.com/taosdata/go-utils/tdengine/connector"
tdengineExecutor "github.com/taosdata/go-utils/tdengine/executor"
)
type Executor struct {
executor *tdengineExecutor.Executor
ctx context.Context
}
var Logger = log.NewLogger("taos test")
func NewExecutor(conf *config.TDengineGo, db string, showSql bool) (*Executor, error) {
tdengineConnector, err := connector.NewTDengineConnector("go", conf)
if err != nil {
return nil, err
}
executor := tdengineExecutor.NewExecutor(tdengineConnector, db, showSql, Logger)
return &Executor{
executor: executor,
ctx: context.Background(),
}, nil
}
func (e *Executor) Execute(sql string) (int64, error) {
return e.executor.DoExec(e.ctx, sql)
}
func (e *Executor) Query(sql string) (*connector.Data, error) {
fmt.Println("query :", sql)
return e.executor.DoQuery(e.ctx, sql)
}
func (e *Executor) CheckData(row, col int, value interface{}, data *connector.Data) (bool, error) {
if data == nil {
return false, fmt.Errorf("data is nil")
}
if col >= len(data.Head) {
return false, fmt.Errorf("col out of data")
}
if row >= len(data.Data) {
return false, fmt.Errorf("row out of data")
}
dataValue := data.Data[row][col]
if dataValue == nil && value != nil {
return false, fmt.Errorf("dataValue is nil but value is not nil")
}
if dataValue == nil && value == nil {
return true, nil
}
if reflect.TypeOf(dataValue) != reflect.TypeOf(value) {
return false, fmt.Errorf("type not match expect %s got %s", reflect.TypeOf(value), reflect.TypeOf(dataValue))
}
switch value.(type) {
case time.Time:
t, _ := dataValue.(time.Time)
if value.(time.Time).Nanosecond() != t.Nanosecond() {
return false, fmt.Errorf("value not match expect %d got %d", value.(time.Time).Nanosecond(), t.Nanosecond())
}
case string:
if value.(string) != dataValue.(string) {
return false, fmt.Errorf("value not match expect %s got %s", value.(string), dataValue.(string))
}
case int8:
if value.(int8) != dataValue.(int8) {
return false, fmt.Errorf("value not match expect %d got %d", value.(int8), dataValue.(int8))
}
case int16:
if value.(int16) != dataValue.(int16) {
return false, fmt.Errorf("value not match expect %d got %d", value.(int16), dataValue.(int16))
}
case int32:
if value.(int32) != dataValue.(int32) {
return false, fmt.Errorf("value not match expect %d got %d", value.(int32), dataValue.(int32))
}
case int64:
if value.(int64) != dataValue.(int64) {
return false, fmt.Errorf("value not match expect %d got %d", value.(int64), dataValue.(int64))
}
case float32:
if value.(float32) != dataValue.(float32) {
return false, fmt.Errorf("value not match expect %f got %f", value.(float32), dataValue.(float32))
}
case float64:
if value.(float64) != dataValue.(float64) {
return false, fmt.Errorf("value not match expect %f got %f", value.(float32), dataValue.(float32))
}
case bool:
if value.(bool) != dataValue.(bool) {
return false, fmt.Errorf("value not match expect %t got %t", value.(bool), dataValue.(bool))
}
default:
return false, fmt.Errorf("unsupport type %v", reflect.TypeOf(value))
}
return true, nil
}
func (e *Executor) CheckData2(row, col int, value interface{}, data *connector.Data) {
match, err := e.CheckData(row, col, value, data)
fmt.Println("expect data is :", value)
fmt.Println("go got data is :", data.Data[row][col])
if err != nil {
fmt.Println(err)
}
if !match {
fmt.Println(" data not match")
}
/*
fmt.Println(value)
if data == nil {
// return false, fmt.Errorf("data is nil")
// fmt.Println("check failed")
}
if col >= len(data.Head) {
// return false, fmt.Errorf("col out of data")
// fmt.Println("check failed")
}
if row >= len(data.Data) {
// return false, fmt.Errorf("row out of data")
// fmt.Println("check failed")
}
dataValue := data.Data[row][col]
if dataValue == nil && value != nil {
// return false, fmt.Errorf("dataValue is nil but value is not nil")
// fmt.Println("check failed")
}
if dataValue == nil && value == nil {
// return true, nil
fmt.Println("check pass")
}
if reflect.TypeOf(dataValue) != reflect.TypeOf(value) {
// return false, fmt.Errorf("type not match expect %s got %s", reflect.TypeOf(value), reflect.TypeOf(dataValue))
fmt.Println("check failed")
}
switch value.(type) {
case time.Time:
t, _ := dataValue.(time.Time)
if value.(time.Time).Nanosecond() != t.Nanosecond() {
// return false, fmt.Errorf("value not match expect %d got %d", value.(time.Time).Nanosecond(), t.Nanosecond())
// fmt.Println("check failed")
}
case string:
if value.(string) != dataValue.(string) {
// return false, fmt.Errorf("value not match expect %s got %s", value.(string), dataValue.(string))
// fmt.Println("check failed")
}
case int8:
if value.(int8) != dataValue.(int8) {
// return false, fmt.Errorf("value not match expect %d got %d", value.(int8), dataValue.(int8))
// fmt.Println("check failed")
}
case int16:
if value.(int16) != dataValue.(int16) {
// return false, fmt.Errorf("value not match expect %d got %d", value.(int16), dataValue.(int16))
// fmt.Println("check failed")
}
case int32:
if value.(int32) != dataValue.(int32) {
// return false, fmt.Errorf("value not match expect %d got %d", value.(int32), dataValue.(int32))
// fmt.Println("check failed")
}
case int64:
if value.(int64) != dataValue.(int64) {
// return false, fmt.Errorf("value not match expect %d got %d", value.(int64), dataValue.(int64))
// fmt.Println("check failed")
}
case float32:
if value.(float32) != dataValue.(float32) {
// return false, fmt.Errorf("value not match expect %f got %f", value.(float32), dataValue.(float32))
// fmt.Println("check failed")
}
case float64:
if value.(float64) != dataValue.(float64) {
// return false, fmt.Errorf("value not match expect %f got %f", value.(float32), dataValue.(float32))
// fmt.Println("check failed")
}
case bool:
if value.(bool) != dataValue.(bool) {
// return false, fmt.Errorf("value not match expect %t got %t", value.(bool), dataValue.(bool))
// fmt.Println("check failed")
}
default:
// return false, fmt.Errorf("unsupport type %v", reflect.TypeOf(value))
// fmt.Println("check failed")
}
// return true, nil
// fmt.Println("check pass")
*/
}
func (e *Executor) CheckRow(count int, data *connector.Data) {
if len(data.Data) != count {
fmt.Println("check failed !")
}
}
@echo off
echo ==== start run nanosupport.go
del go.*
go mod init nano
go mod tidy
go build
nano.exe -h %1 -p %2
cd ..
#!/bin/bash
echo "==== start run nanosupport.go "
set +e
#set -x
script_dir="$(dirname $(readlink -f $0))"
#echo "pwd: $script_dir, para0: $0"
#execName=$0
#execName=`echo ${execName##*/}`
#goName=`echo ${execName%.*}`
###### step 3: start build
cd $script_dir
rm -f go.*
go mod init nano
go mod tidy
go build
sleep 10s
./nano -h $1 -p $2
package main
import (
"fmt"
"log"
"nano/connector"
"time"
"github.com/taosdata/go-utils/tdengine/config"
)
func main() {
e, err := connector.NewExecutor(&config.TDengineGo{
Address: "root:taosdata@/tcp(127.0.0.1:6030)/",
MaxIdle: 20,
MaxOpen: 30,
MaxLifetime: 30,
}, "db", false)
if err != nil {
panic(err)
}
prepareData(e)
data, err := e.Query("select * from tb")
if err != nil {
panic(err)
}
layout := "2006-01-02 15:04:05.999999999"
t0, _ := time.Parse(layout, "2021-06-10 00:00:00.100000001")
t1, _ := time.Parse(layout, "2021-06-10 00:00:00.150000000")
t2, _ := time.Parse(layout, "2021-06-10 00:00:00.299999999")
t3, _ := time.Parse(layout, "2021-06-10 00:00:00.300000000")
t4, _ := time.Parse(layout, "2021-06-10 00:00:00.300000001")
t5, _ := time.Parse(layout, "2021-06-10 00:00:00.999999999")
e.CheckData2(0, 0, t0, data)
e.CheckData2(1, 0, t1, data)
e.CheckData2(2, 0, t2, data)
e.CheckData2(3, 0, t3, data)
e.CheckData2(4, 0, t4, data)
e.CheckData2(5, 0, t5, data)
e.CheckData2(3, 1, int32(3), data)
e.CheckData2(4, 1, int32(5), data)
e.CheckData2(5, 1, int32(7), data)
fmt.Println(" start check nano support!")
data, _ = e.Query("select count(*) from tb where ts > 1623254400100000000 and ts < 1623254400100000002;")
e.CheckData2(0, 0, int64(1), data)
data, _ = e.Query("select count(*) from tb where ts > \"2021-06-10 0:00:00.100000001\" and ts < \"2021-06-10 0:00:00.160000000\";")
e.CheckData2(0, 0, int64(1), data)
data, _ = e.Query("select count(*) from tb where ts > 1623254400100000000 and ts < 1623254400150000000;")
e.CheckData2(0, 0, int64(1), data)
data, _ = e.Query("select count(*) from tb where ts > \"2021-06-10 0:00:00.100000000\" and ts < \"2021-06-10 0:00:00.150000000\";")
e.CheckData2(0, 0, int64(1), data)
data, _ = e.Query("select count(*) from tb where ts > 1623254400400000000;")
e.CheckData2(0, 0, int64(1), data)
data, _ = e.Query("select count(*) from tb where ts < \"2021-06-10 00:00:00.400000000\";")
e.CheckData2(0, 0, int64(5), data)
data, _ = e.Query("select count(*) from tb where ts < now + 400000000b;")
e.CheckData2(0, 0, int64(6), data)
data, _ = e.Query("select count(*) from tb where ts >= \"2021-06-10 0:00:00.100000001\";")
e.CheckData2(0, 0, int64(6), data)
data, _ = e.Query("select count(*) from tb where ts <= 1623254400300000000;")
e.CheckData2(0, 0, int64(4), data)
data, _ = e.Query("select count(*) from tb where ts = \"2021-06-10 0:00:00.000000000\";")
data, _ = e.Query("select count(*) from tb where ts = 1623254400150000000;")
e.CheckData2(0, 0, int64(1), data)
data, _ = e.Query("select count(*) from tb where ts = \"2021-06-10 0:00:00.100000001\";")
e.CheckData2(0, 0, int64(1), data)
data, _ = e.Query("select count(*) from tb where ts between 1623254400000000000 and 1623254400400000000;")
e.CheckData2(0, 0, int64(5), data)
data, _ = e.Query("select count(*) from tb where ts between \"2021-06-10 0:00:00.299999999\" and \"2021-06-10 0:00:00.300000001\";")
e.CheckData2(0, 0, int64(3), data)
data, _ = e.Query("select avg(speed) from tb interval(5000000000b);")
e.CheckRow(1, data)
data, _ = e.Query("select avg(speed) from tb interval(100000000b)")
e.CheckRow(4, data)
data, _ = e.Query("select avg(speed) from tb interval(1000b);")
e.CheckRow(5, data)
data, _ = e.Query("select avg(speed) from tb interval(1u);")
e.CheckRow(5, data)
data, _ = e.Query("select avg(speed) from tb interval(100000000b) sliding (100000000b);")
e.CheckRow(4, data)
data, _ = e.Query("select last(*) from tb")
tt, _ := time.Parse(layout, "2021-06-10 0:00:00.999999999")
e.CheckData2(0, 0, tt, data)
data, _ = e.Query("select first(*) from tb")
tt1, _ := time.Parse(layout, "2021-06-10 0:00:00.100000001")
e.CheckData2(0, 0, tt1, data)
e.Execute("insert into tb values(now + 500000000b, 6);")
data, _ = e.Query("select * from tb;")
e.CheckRow(7, data)
e.Execute("create table tb2 (ts timestamp, speed int, ts2 timestamp);")
e.Execute("insert into tb2 values(\"2021-06-10 0:00:00.100000001\", 1, \"2021-06-11 0:00:00.100000001\");")
e.Execute("insert into tb2 values(1623254400150000000, 2, 1623340800150000000);")
e.Execute("import into tb2 values(1623254400300000000, 3, 1623340800300000000);")
e.Execute("import into tb2 values(1623254400299999999, 4, 1623340800299999999);")
e.Execute("insert into tb2 values(1623254400300000001, 5, 1623340800300000001);")
e.Execute("insert into tb2 values(1623254400999999999, 7, 1623513600999999999);")
data, _ = e.Query("select * from tb2;")
tt2, _ := time.Parse(layout, "2021-06-10 0:00:00.100000001")
tt3, _ := time.Parse(layout, "2021-06-10 0:00:00.150000000")
e.CheckData2(0, 0, tt2, data)
e.CheckData2(1, 0, tt3, data)
e.CheckData2(2, 1, int32(4), data)
e.CheckData2(3, 1, int32(3), data)
tt4, _ := time.Parse(layout, "2021-06-11 00:00:00.300000001")
e.CheckData2(4, 2, tt4, data)
e.CheckRow(6, data)
data, _ = e.Query("select count(*) from tb2 where ts2 > 1623340800000000000 and ts2 < 1623340800150000000;")
e.CheckData2(0, 0, int64(1), data)
data, _ = e.Query("select count(*) from tb2 where ts2 > \"2021-06-11 0:00:00.100000000\" and ts2 < \"2021-06-11 0:00:00.100000002\";")
e.CheckData2(0, 0, int64(1), data)
data, _ = e.Query("select count(*) from tb2 where ts2 > 1623340800500000000;")
e.CheckData2(0, 0, int64(1), data)
data, _ = e.Query("select count(*) from tb2 where ts2 < \"2021-06-11 0:00:00.400000000\";")
e.CheckData2(0, 0, int64(5), data)
data, _ = e.Query("select count(*) from tb2 where ts2 < now + 400000000b;")
e.CheckData2(0, 0, int64(6), data)
data, _ = e.Query("select count(*) from tb2 where ts2 >= \"2021-06-11 0:00:00.100000001\";")
e.CheckData2(0, 0, int64(6), data)
data, _ = e.Query("select count(*) from tb2 where ts2 <= 1623340800400000000;")
e.CheckData2(0, 0, int64(5), data)
data, _ = e.Query("select count(*) from tb2 where ts2 = \"2021-06-11 0:00:00.000000000\";")
data, _ = e.Query("select count(*) from tb2 where ts2 = \"2021-06-11 0:00:00.300000001\";")
e.CheckData2(0, 0, int64(1), data)
data, _ = e.Query("select count(*) from tb2 where ts2 = 1623340800300000001;")
e.CheckData2(0, 0, int64(1), data)
data, _ = e.Query("select count(*) from tb2 where ts2 between 1623340800000000000 and 1623340800450000000;")
e.CheckData2(0, 0, int64(5), data)
data, _ = e.Query("select count(*) from tb2 where ts2 between \"2021-06-11 0:00:00.299999999\" and \"2021-06-11 0:00:00.300000001\";")
e.CheckData2(0, 0, int64(3), data)
data, _ = e.Query("select count(*) from tb2 where ts2 <> 1623513600999999999;")
e.CheckData2(0, 0, int64(5), data)
data, _ = e.Query("select count(*) from tb2 where ts2 <> \"2021-06-11 0:00:00.100000001\";")
e.CheckData2(0, 0, int64(5), data)
data, _ = e.Query("select count(*) from tb2 where ts2 <> \"2021-06-11 0:00:00.100000000\";")
e.CheckData2(0, 0, int64(6), data)
data, _ = e.Query("select count(*) from tb2 where ts2 != 1623513600999999999;")
e.CheckData2(0, 0, int64(5), data)
data, _ = e.Query("select count(*) from tb2 where ts2 != \"2021-06-11 0:00:00.100000001\";")
e.CheckData2(0, 0, int64(5), data)
data, _ = e.Query("select count(*) from tb2 where ts2 != \"2021-06-11 0:00:00.100000000\";")
e.CheckData2(0, 0, int64(6), data)
e.Execute("insert into tb2 values(now + 500000000b, 6, now +2d);")
data, _ = e.Query("select * from tb2;")
e.CheckRow(7, data)
e.Execute("create table tb3 (ts timestamp, speed int);")
_, err = e.Execute("insert into tb3 values(16232544001500000, 2);")
if err != nil {
fmt.Println("check pass! ")
}
e.Execute("insert into tb3 values(\"2021-06-10 0:00:00.123456\", 2);")
data, _ = e.Query("select * from tb3 where ts = \"2021-06-10 0:00:00.123456000\";")
e.CheckRow(1, data)
e.Execute("insert into tb3 values(\"2021-06-10 0:00:00.123456789000\", 2);")
data, _ = e.Query("select * from tb3 where ts = \"2021-06-10 0:00:00.123456789\";")
e.CheckRow(1, data)
// check timezone support
e.Execute("drop database if exists nsdb;")
e.Execute("create database nsdb precision 'ns';")
e.Execute("use nsdb;")
e.Execute("create stable st (ts timestamp ,speed float ) tags(time timestamp ,id int);")
e.Execute("insert into tb1 using st tags('2021-06-10 0:00:00.123456789' , 1 ) values('2021-06-10T0:00:00.123456789+07:00' , 1.0);")
data, _ = e.Query("select first(*) from tb1;")
ttt, _ := time.Parse(layout, "2021-06-10 01:00:00.123456789")
e.CheckData2(0, 0, ttt, data)
e.Execute("create database usdb precision 'us';")
e.Execute("use usdb;")
e.Execute("create stable st (ts timestamp ,speed float ) tags(time timestamp ,id int);")
e.Execute("insert into tb1 using st tags('2021-06-10 0:00:00.123456' , 1 ) values('2021-06-10T0:00:00.123456+07:00' , 1.0);")
data, _ = e.Query("select first(*) from tb1;")
ttt2, _ := time.Parse(layout, "2021-06-10 01:00:00.123456")
e.CheckData2(0, 0, ttt2, data)
e.Execute("drop database if exists msdb;")
e.Execute("create database msdb precision 'ms';")
e.Execute("use msdb;")
e.Execute("create stable st (ts timestamp ,speed float ) tags(time timestamp ,id int);")
e.Execute("insert into tb1 using st tags('2021-06-10 0:00:00.123' , 1 ) values('2021-06-10T0:00:00.123+07:00' , 1.0);")
data, _ = e.Query("select first(*) from tb1;")
ttt3, _ := time.Parse(layout, "2021-06-10 01:00:00.123")
e.CheckData2(0, 0, ttt3, data)
fmt.Println("all test done!")
}
func prepareData(e *connector.Executor) {
sqlList := []string{
"reset query cache;",
"drop database if exists db;",
"create database db;",
"use db;",
"reset query cache;",
"drop database if exists db;",
"create database db precision 'ns';",
"show databases;",
"use db;",
"create table tb (ts timestamp, speed int);",
"insert into tb values('2021-06-10 0:00:00.100000001', 1);",
"insert into tb values(1623254400150000000, 2);",
"import into tb values(1623254400300000000, 3);",
"import into tb values(1623254400299999999, 4);",
"insert into tb values(1623254400300000001, 5);",
"insert into tb values(1623254400999999999, 7);",
}
for _, sql := range sqlList {
err := executeSql(e, sql)
if err != nil {
log.Fatalf("prepare data error:%v, sql:%s", err, sql)
}
}
}
func executeSql(e *connector.Executor, sql string) error {
_, err := e.Execute(sql)
if err != nil {
return err
}
return nil
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册