cursor.js 8.8 KB
Newer Older
S
StoneT2000 已提交
1
require('./globalfunc.js')
2 3
const CTaosInterface = require('./cinterface')
const errors = require ('./error')
4
const TaosQuery = require('./taosquery')
S
StoneT2000 已提交
5
const { PerformanceObserver, performance } = require('perf_hooks');
6 7
module.exports = TDengineCursor;

8
/**
S
StoneT2000 已提交
9 10 11 12 13
 * @typedef {Object} Buffer - A Node.JS buffer. Please refer to {@link https://nodejs.org/api/buffer.html} for more details
 * @global
 */

/**
14
 * @class TDengineCursor
S
StoneT2000 已提交
15 16 17 18
 * @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.
19 20
 * @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
S
StoneT2000 已提交
21 22
 * @property {fields} - Array of the field objects in order from left to right of the latest data retrieved
 * @since 1.0.0
23
 */
24 25 26 27 28 29
function TDengineCursor(connection=null) {
  this._description = null;
  this._rowcount = -1;
  this._connection = null;
  this._result = null;
  this._fields = null;
30
  this.data = [];
S
StoneT2000 已提交
31
  this.fields = null;
32
  this._chandle = new CTaosInterface(null, true); //pass through, just need library loaded.
33 34 35 36 37
  if (connection != null) {
    this._connection = connection
  }

}
38 39
/**
 * Get the description of the latest query
S
StoneT2000 已提交
40
 * @since 1.0.0
41 42
 * @return {string} Description
 */
43 44 45
TDengineCursor.prototype.description = function description() {
  return this._description;
}
46 47
/**
 * Get the row counts of the latest query
S
StoneT2000 已提交
48
 * @since 1.0.0
49 50
 * @return {number} Rowcount
 */
51 52 53 54 55 56
TDengineCursor.prototype.rowcount = function rowcount() {
  return this._rowcount;
}
TDengineCursor.prototype.callproc = function callproc() {
  return;
}
57 58 59
/**
 * 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
S
StoneT2000 已提交
60
 * @since 1.0.0
61
 */
62 63 64 65
TDengineCursor.prototype.close = function close() {
  if (this._connection == null) {
    return false;
  }
66
  this._connection._clearResultSet();
67 68 69 70
  this._reset_result();
  this._connection = null;
  return true;
}
71
/**
S
StoneT2000 已提交
72 73 74 75 76 77 78 79
 * 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
80
 */
S
StoneT2000 已提交
81 82
TDengineCursor.prototype.query = function query(operation, execute = false) {
  return new TaosQuery(operation, this, execute);
83 84 85
}

/**
S
StoneT2000 已提交
86 87 88 89 90 91 92 93
 * 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
94
 */
S
StoneT2000 已提交
95
TDengineCursor.prototype.execute = function execute(operation, options, callback) {
96 97 98
  if (operation == undefined) {
    return null;
  }
S
StoneT2000 已提交
99 100 101 102 103

  if (typeof options == 'function')  {
    callback = options;
  }
  if (typeof options != 'object') options = {}
104 105 106
  if (this._connection == null) {
    throw new errors.ProgrammingError('Cursor is not connected');
  }
107
  this._connection._clearResultSet();
108 109 110
  this._reset_result();

  let stmt = operation;
S
StoneT2000 已提交
111 112 113 114 115 116 117 118
  let time = 0;
  const obs = new PerformanceObserver((items) => {
    time = items.getEntries()[0].duration;
    performance.clearMarks();
  });
  obs.observe({ entryTypes: ['measure'] });
  performance.mark('A');
  performance.mark('B');
119
  res = this._chandle.query(this._connection._conn, stmt);
S
StoneT2000 已提交
120 121 122

  performance.measure('query', 'A', 'B');

123
  if (res == 0) {
124
    let fieldCount = this._chandle.fieldsCount(this._connection._conn);
125
    if (fieldCount == 0) {
S
StoneT2000 已提交
126 127 128 129 130 131 132
      let affectedRowCount = this._chandle.affectedRows(this._connection._conn);
      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
133 134
    }
    else {
135
      let resAndField = this._chandle.useResult(this._connection._conn, fieldCount)
136 137
      this._result = resAndField.result;
      this._fields = resAndField.fields;
S
StoneT2000 已提交
138 139
      this.fields = resAndField.fields;
      wrapCB(callback);
140
      return this._handle_result(); //return a pointer to the result
141 142 143
    }
  }
  else {
144
    throw new errors.ProgrammingError(this._chandle.errStr(this._connection._conn))
145 146 147
  }

}
S
StoneT2000 已提交
148 149 150 151 152 153
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)";
}
154 155 156 157 158 159 160 161 162
TDengineCursor.prototype.executemany = function executemany() {

}
TDengineCursor.prototype.fetchone = function fetchone() {

}
TDengineCursor.prototype.fetchmany = function fetchmany() {

}
163
/**
S
StoneT2000 已提交
164 165 166 167
 * 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
168
 * order by the field name ordering in the table.
S
StoneT2000 已提交
169
 * @since 1.0.0
170 171
 * @example
 * cursor.execute('select * from db.table');
S
StoneT2000 已提交
172 173 174
 * var data = cursor.fetchall(function(results) {
 *   results.forEach(row => console.log(row));
 * })
175
 */
S
StoneT2000 已提交
176
TDengineCursor.prototype.fetchall = function fetchall(options, callback) {
177
  if (this._result == null || this._fields == null) {
S
StoneT2000 已提交
178
    throw new errors.OperationalError("Invalid use of fetchall, either result or fields from query are null. First execute a query first");
179
  }
S
StoneT2000 已提交
180

181 182 183
  let data = [];
  this._rowcount = 0;
  let k = 0;
S
StoneT2000 已提交
184 185 186 187 188 189 190 191 192 193 194 195 196 197
  //let nodetime = 0;
  let time = 0;
  const obs = new PerformanceObserver((items) => {
    time += items.getEntries()[0].duration;
    performance.clearMarks();
  });
  /*
  const obs2 = new PerformanceObserver((items) => {
    nodetime += items.getEntries()[0].duration;
    performance.clearMarks();
  });
  obs2.observe({ entryTypes: ['measure'] });
  performance.mark('nodea');
  */
198 199
  while(true) {
    k+=1;
S
StoneT2000 已提交
200 201
    obs.observe({ entryTypes: ['measure'] });
    performance.mark('A');
202
    let blockAndRows = this._chandle.fetchBlock(this._result, this._fields);
S
StoneT2000 已提交
203 204
    performance.mark('B');
    performance.measure('query', 'A', 'B');
205 206 207 208 209 210 211 212 213
    let block = blockAndRows.blocks;
    let num_of_rows = blockAndRows.num_of_rows;

    if (num_of_rows == 0) {
      break;
    }
    this._rowcount += num_of_rows;
    for (let i = 0; i < num_of_rows; i++) {
      data.push([]);
S
StoneT2000 已提交
214
      let rowBlock = new Array(this._fields.length);
215
      for (let j = 0; j < this._fields.length; j++) {
S
StoneT2000 已提交
216
        rowBlock[j] = block[j][i];
217
      }
S
StoneT2000 已提交
218
      data[data.length-1] = (rowBlock);
219 220
    }
  }
S
StoneT2000 已提交
221 222 223
  let response = this._createSetResponse(this._rowcount, time)
  console.log(response);

224
  this._connection._clearResultSet();
S
StoneT2000 已提交
225 226
  let fields = this.fields;
  this._reset_result();
227
  this.data = data;
S
StoneT2000 已提交
228 229 230 231 232 233
  this.fields = fields;
  //performance.mark('nodeb');
  //performance.measure('querynode', 'nodea', 'nodeb');
  //console.log('nodetime: ' + nodetime/1000);
  wrapCB(callback, data);

234
  return data;
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
}
TDengineCursor.prototype.nextset = function nextset() {
  return;
}
TDengineCursor.prototype.setinputsize = function setinputsize() {
  return;
}
TDengineCursor.prototype.setoutputsize = function setoutputsize(size, column=null) {
  return;
}
TDengineCursor.prototype._reset_result = function _reset_result() {
  this._description = null;
  this._rowcount = -1;
  this._result = null;
  this._fields = null;
250
  this.data = [];
S
StoneT2000 已提交
251
  this.fields = null;
252 253 254 255
}
TDengineCursor.prototype._handle_result = function _handle_result() {
  this._description = [];
  for (let field of this._fields) {
S
StoneT2000 已提交
256
    this._description.push([field.name, field.type]);
257 258 259
  }
  return this._result;
}