[TD-4946]<feature>: Node.js connector support nanoseconds (#6817)
* [TD-4946]<feature>: Node.js connector support nanoseconds * [TD-5166]<feature>: Node.js connector support no host settings
This commit is contained in:
parent
e811cb0ad8
commit
66ab17dd58
|
@ -15,36 +15,18 @@ const { NULL_POINTER } = require('ref-napi');
|
|||
|
||||
module.exports = CTaosInterface;
|
||||
|
||||
function convertMillisecondsToDatetime(time) {
|
||||
return new TaosObjects.TaosTimestamp(time);
|
||||
}
|
||||
function convertMicrosecondsToDatetime(time) {
|
||||
return new TaosObjects.TaosTimestamp(time * 0.001, true);
|
||||
}
|
||||
|
||||
function convertTimestamp(data, num_of_rows, nbytes = 0, offset = 0, micro = false) {
|
||||
timestampConverter = convertMillisecondsToDatetime;
|
||||
if (micro == true) {
|
||||
timestampConverter = convertMicrosecondsToDatetime;
|
||||
}
|
||||
function convertTimestamp(data, num_of_rows, nbytes = 0, offset = 0, precision = 0) {
|
||||
data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
|
||||
let res = [];
|
||||
let currOffset = 0;
|
||||
while (currOffset < data.length) {
|
||||
let queue = [];
|
||||
let time = 0;
|
||||
for (let i = currOffset; i < currOffset + nbytes; i++) {
|
||||
queue.push(data[i]);
|
||||
}
|
||||
for (let i = queue.length - 1; i >= 0; i--) {
|
||||
time += queue[i] * Math.pow(16, i * 2);
|
||||
}
|
||||
let time = data.readInt64LE(currOffset);
|
||||
currOffset += nbytes;
|
||||
res.push(timestampConverter(time));
|
||||
res.push(new TaosObjects.TaosTimestamp(time, precision));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
function convertBool(data, num_of_rows, nbytes = 0, offset = 0, micro = false) {
|
||||
function convertBool(data, num_of_rows, nbytes = 0, offset = 0, precision = 0) {
|
||||
data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
|
||||
let res = new Array(data.length);
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
|
@ -60,7 +42,7 @@ function convertBool(data, num_of_rows, nbytes = 0, offset = 0, micro = false) {
|
|||
}
|
||||
return res;
|
||||
}
|
||||
function convertTinyint(data, num_of_rows, nbytes = 0, offset = 0, micro = false) {
|
||||
function convertTinyint(data, num_of_rows, nbytes = 0, offset = 0, precision = 0) {
|
||||
data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
|
||||
let res = [];
|
||||
let currOffset = 0;
|
||||
|
@ -71,7 +53,7 @@ function convertTinyint(data, num_of_rows, nbytes = 0, offset = 0, micro = false
|
|||
}
|
||||
return res;
|
||||
}
|
||||
function convertSmallint(data, num_of_rows, nbytes = 0, offset = 0, micro = false) {
|
||||
function convertSmallint(data, num_of_rows, nbytes = 0, offset = 0, precision = 0) {
|
||||
data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
|
||||
let res = [];
|
||||
let currOffset = 0;
|
||||
|
@ -82,7 +64,7 @@ function convertSmallint(data, num_of_rows, nbytes = 0, offset = 0, micro = fals
|
|||
}
|
||||
return res;
|
||||
}
|
||||
function convertInt(data, num_of_rows, nbytes = 0, offset = 0, micro = false) {
|
||||
function convertInt(data, num_of_rows, nbytes = 0, offset = 0, precision = 0) {
|
||||
data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
|
||||
let res = [];
|
||||
let currOffset = 0;
|
||||
|
@ -93,7 +75,7 @@ function convertInt(data, num_of_rows, nbytes = 0, offset = 0, micro = false) {
|
|||
}
|
||||
return res;
|
||||
}
|
||||
function convertBigint(data, num_of_rows, nbytes = 0, offset = 0, micro = false) {
|
||||
function convertBigint(data, num_of_rows, nbytes = 0, offset = 0, precision = 0) {
|
||||
data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
|
||||
let res = [];
|
||||
let currOffset = 0;
|
||||
|
@ -104,7 +86,7 @@ function convertBigint(data, num_of_rows, nbytes = 0, offset = 0, micro = false)
|
|||
}
|
||||
return res;
|
||||
}
|
||||
function convertFloat(data, num_of_rows, nbytes = 0, offset = 0, micro = false) {
|
||||
function convertFloat(data, num_of_rows, nbytes = 0, offset = 0, precision = 0) {
|
||||
data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
|
||||
let res = [];
|
||||
let currOffset = 0;
|
||||
|
@ -115,7 +97,7 @@ function convertFloat(data, num_of_rows, nbytes = 0, offset = 0, micro = false)
|
|||
}
|
||||
return res;
|
||||
}
|
||||
function convertDouble(data, num_of_rows, nbytes = 0, offset = 0, micro = false) {
|
||||
function convertDouble(data, num_of_rows, nbytes = 0, offset = 0, precision = 0) {
|
||||
data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
|
||||
let res = [];
|
||||
let currOffset = 0;
|
||||
|
@ -127,7 +109,7 @@ function convertDouble(data, num_of_rows, nbytes = 0, offset = 0, micro = false)
|
|||
return res;
|
||||
}
|
||||
|
||||
function convertNchar(data, num_of_rows, nbytes = 0, offset = 0, micro = false) {
|
||||
function convertNchar(data, num_of_rows, nbytes = 0, offset = 0, precision = 0) {
|
||||
data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
|
||||
let res = [];
|
||||
|
||||
|
@ -272,7 +254,7 @@ CTaosInterface.prototype.config = function config() {
|
|||
CTaosInterface.prototype.connect = function connect(host = null, user = "root", password = "taosdata", db = null, port = 0) {
|
||||
let _host, _user, _password, _db, _port;
|
||||
try {
|
||||
_host = host != null ? ref.allocCString(host) : ref.alloc(ref.types.char_ptr, ref.NULL);
|
||||
_host = host != null ? ref.allocCString(host) : ref.NULL;
|
||||
}
|
||||
catch (err) {
|
||||
throw "Attribute Error: host is expected as a str";
|
||||
|
@ -290,7 +272,7 @@ CTaosInterface.prototype.connect = function connect(host = null, user = "root",
|
|||
throw "Attribute Error: password is expected as a str";
|
||||
}
|
||||
try {
|
||||
_db = db != null ? ref.allocCString(db) : ref.alloc(ref.types.char_ptr, ref.NULL);
|
||||
_db = db != null ? ref.allocCString(db) : ref.NULL;
|
||||
}
|
||||
catch (err) {
|
||||
throw "Attribute Error: db is expected as a str";
|
||||
|
@ -345,8 +327,7 @@ CTaosInterface.prototype.fetchBlock = function fetchBlock(result, fields) {
|
|||
}
|
||||
|
||||
var fieldL = this.libtaos.taos_fetch_lengths(result);
|
||||
|
||||
let isMicro = (this.libtaos.taos_result_precision(result) == FieldTypes.C_TIMESTAMP_MICRO);
|
||||
let precision = this.libtaos.taos_result_precision(result);
|
||||
|
||||
var fieldlens = [];
|
||||
|
||||
|
@ -373,7 +354,7 @@ CTaosInterface.prototype.fetchBlock = function fetchBlock(result, fields) {
|
|||
if (!convertFunctions[fields[i]['type']]) {
|
||||
throw new errors.DatabaseError("Invalid data type returned from database");
|
||||
}
|
||||
blocks[i] = convertFunctions[fields[i]['type']](pdata, num_of_rows, fieldlens[i], offset, isMicro);
|
||||
blocks[i] = convertFunctions[fields[i]['type']](pdata, num_of_rows, fieldlens[i], offset, precision);
|
||||
}
|
||||
}
|
||||
return { blocks: blocks, num_of_rows }
|
||||
|
@ -423,7 +404,7 @@ CTaosInterface.prototype.fetch_rows_a = function fetch_rows_a(result, callback,
|
|||
let row = cti.libtaos.taos_fetch_row(result2);
|
||||
let fields = cti.fetchFields_a(result2);
|
||||
|
||||
let isMicro = (cti.libtaos.taos_result_precision(result2) == FieldTypes.C_TIMESTAMP_MICRO);
|
||||
let precision = cti.libtaos.taos_result_precision(result2);
|
||||
let blocks = new Array(fields.length);
|
||||
blocks.fill(null);
|
||||
numOfRows2 = Math.abs(numOfRows2);
|
||||
|
@ -449,7 +430,7 @@ CTaosInterface.prototype.fetch_rows_a = function fetch_rows_a(result, callback,
|
|||
let prow = ref.reinterpret(row, 8, i * 8);
|
||||
prow = prow.readPointer();
|
||||
prow = ref.ref(prow);
|
||||
blocks[i] = convertFunctions[fields[i]['type']](prow, 1, fieldlens[i], offset, isMicro);
|
||||
blocks[i] = convertFunctions[fields[i]['type']](prow, 1, fieldlens[i], offset, precision);
|
||||
//offset += fields[i]['bytes'] * numOfRows2;
|
||||
}
|
||||
}
|
||||
|
@ -572,7 +553,7 @@ CTaosInterface.prototype.openStream = function openStream(connection, sql, callb
|
|||
var cti = this;
|
||||
let asyncCallbackWrapper = function (param2, result2, row) {
|
||||
let fields = cti.fetchFields_a(result2);
|
||||
let isMicro = (cti.libtaos.taos_result_precision(result2) == FieldTypes.C_TIMESTAMP_MICRO);
|
||||
let precision = cti.libtaos.taos_result_precision(result2);
|
||||
let blocks = new Array(fields.length);
|
||||
blocks.fill(null);
|
||||
let numOfRows2 = 1;
|
||||
|
@ -582,7 +563,7 @@ CTaosInterface.prototype.openStream = function openStream(connection, sql, callb
|
|||
if (!convertFunctions[fields[i]['type']]) {
|
||||
throw new errors.DatabaseError("Invalid data type returned from database");
|
||||
}
|
||||
blocks[i] = convertFunctions[fields[i]['type']](row, numOfRows2, fields[i]['bytes'], offset, isMicro);
|
||||
blocks[i] = convertFunctions[fields[i]['type']](row, numOfRows2, fields[i]['bytes'], offset, precision);
|
||||
offset += fields[i]['bytes'] * numOfRows2;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const FieldTypes = require('./constants');
|
||||
|
||||
const util = require('util');
|
||||
/**
|
||||
* Various objects such as TaosRow and TaosColumn that help make parsing data easier
|
||||
* @module TaosObjects
|
||||
|
@ -14,7 +14,7 @@ const FieldTypes = require('./constants');
|
|||
* var trow = new TaosRow(row);
|
||||
* console.log(trow.data);
|
||||
*/
|
||||
function TaosRow (row) {
|
||||
function TaosRow(row) {
|
||||
this.data = row;
|
||||
this.length = row.length;
|
||||
return this;
|
||||
|
@ -42,25 +42,85 @@ function TaosField(field) {
|
|||
* @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, micro = false) {
|
||||
super(date);
|
||||
this._type = 'TaosTimestamp';
|
||||
if (micro) {
|
||||
this.microTime = date - Math.floor(date);
|
||||
constructor(date, precision = 0) {
|
||||
if (precision === 1) {
|
||||
super(Math.floor(date / 1000));
|
||||
this.precisionExtras = date % 1000;
|
||||
} else if (precision === 2) {
|
||||
super(parseInt(date / 1000000));
|
||||
// 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(){
|
||||
toTaosString() {
|
||||
var tzo = -this.getTimezoneOffset(),
|
||||
dif = tzo >= 0 ? '+' : '-',
|
||||
pad = function(num) {
|
||||
pad = function (num) {
|
||||
var norm = Math.floor(Math.abs(num));
|
||||
return (norm < 10 ? '0' : '') + norm;
|
||||
},
|
||||
pad2 = function(num) {
|
||||
pad2 = function (num) {
|
||||
var norm = Math.floor(Math.abs(num));
|
||||
if (norm < 10) return '00' + norm;
|
||||
if (norm < 100) return '0' + norm;
|
||||
|
@ -73,8 +133,19 @@ class TaosTimestamp extends Date {
|
|||
':' + pad(this.getMinutes()) +
|
||||
':' + pad(this.getSeconds()) +
|
||||
'.' + pad2(this.getMilliseconds()) +
|
||||
'' + (this.microTime ? pad2(Math.round(this.microTime * 1000)) : '');
|
||||
'' + 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}
|
||||
module.exports = { TaosRow, TaosField, TaosTimestamp }
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
{
|
||||
"name": "td2.0-connector",
|
||||
"version": "2.0.8",
|
||||
"version": "2.0.9",
|
||||
"description": "A Node.js connector for TDengine.",
|
||||
"main": "tdengine.js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "node test/test.js"
|
||||
"test": "node test/test.js && node test/testMicroseconds.js && node test/testNanoseconds.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
var TDengineConnection = require('./nodetaos/connection.js')
|
||||
module.exports.connect = function (connection=null) {
|
||||
module.exports.connect = function (connection={}) {
|
||||
return new TDengineConnection(connection);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const taos = require('../tdengine');
|
||||
var conn = taos.connect({host:"127.0.0.1", user:"root", password:"taosdata", config:"/etc/taos",port:10});
|
||||
var conn = taos.connect();
|
||||
var c1 = conn.cursor();
|
||||
let stime = new Date();
|
||||
let interval = 1000;
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
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);
|
|
@ -0,0 +1,49 @@
|
|||
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);
|
Loading…
Reference in New Issue