dolphindb 2.0.800 → 2.0.900
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -5
- package/README.zh.md +83 -9
- package/browser.d.ts +37 -44
- package/browser.js +370 -315
- package/browser.js.map +1 -1
- package/docs.en.json +11544 -1607
- package/docs.zh.json +4215 -819
- package/index.d.ts +32 -43
- package/index.js +219 -210
- package/index.js.map +1 -1
- package/language.d.ts +5 -5
- package/language.js +3 -1
- package/language.js.map +1 -1
- package/package.json +7 -7
package/browser.js
CHANGED
|
@@ -4,7 +4,7 @@ dayjs.extend(DayjsCustomParseFormat);
|
|
|
4
4
|
import ipaddrjs from 'ipaddr.js';
|
|
5
5
|
const { fromByteArray: buf2ipaddr } = ipaddrjs;
|
|
6
6
|
import 'xshell/prototype.browser.js';
|
|
7
|
-
import { blue } from 'xshell/chalk.browser.js';
|
|
7
|
+
import { blue, cyan, green, grey, magenta } from 'xshell/chalk.browser.js';
|
|
8
8
|
import { concat, assert } from 'xshell/utils.browser.js';
|
|
9
9
|
import { connect_websocket } from 'xshell/net.browser.js';
|
|
10
10
|
export var DdbForm;
|
|
@@ -118,15 +118,6 @@ export const nulls = {
|
|
|
118
118
|
double: -Number.MAX_VALUE,
|
|
119
119
|
bytes16: Uint8Array.from(new Array(16).fill(0))
|
|
120
120
|
};
|
|
121
|
-
export const timezone_offset = 1000 * 60 * new Date().getTimezoneOffset();
|
|
122
|
-
export var StreamingStatusCode;
|
|
123
|
-
(function (StreamingStatusCode) {
|
|
124
|
-
StreamingStatusCode[StreamingStatusCode["ok"] = 0] = "ok";
|
|
125
|
-
StreamingStatusCode[StreamingStatusCode["connection_existed"] = 1] = "connection_existed";
|
|
126
|
-
StreamingStatusCode[StreamingStatusCode["not_leader"] = 2] = "not_leader";
|
|
127
|
-
StreamingStatusCode[StreamingStatusCode["other"] = 3] = "other";
|
|
128
|
-
StreamingStatusCode[StreamingStatusCode["error"] = 4] = "error";
|
|
129
|
-
})(StreamingStatusCode = StreamingStatusCode || (StreamingStatusCode = {}));
|
|
130
121
|
/** 可以表示所有 DolphinDB 数据库中的数据类型 */
|
|
131
122
|
export class DdbObj {
|
|
132
123
|
static dec = new TextDecoder('utf-8');
|
|
@@ -522,8 +513,37 @@ export class DdbObj {
|
|
|
522
513
|
// 00 reserved
|
|
523
514
|
// 70 11 01 00 block.lengths = [70000 (0x00011170)]
|
|
524
515
|
// 01 00 00 00 02 00 00 00 ... 279992 more bytes> block.data
|
|
516
|
+
// decimal array vector
|
|
517
|
+
// a = array(DECIMAL32(2)[], 0, 10)
|
|
518
|
+
// append!(a, [
|
|
519
|
+
// [1, 2, 3],
|
|
520
|
+
// [4, 5],
|
|
521
|
+
// [6, 7, 8],
|
|
522
|
+
// [9, 10]
|
|
523
|
+
// ])
|
|
524
|
+
// print(a)
|
|
525
|
+
// a
|
|
526
|
+
// [[1.00,2.00,3.00],[4.00,5.00],[6.00,7.00,8.00],[9.00,10.00]]
|
|
527
|
+
// <Buffer 65 01 type = 101 = 64 + 37 (decimal32) , form = vector
|
|
528
|
+
// 04 00 00 00 0a 00 00 00 rows = 4, cols = 10
|
|
529
|
+
// scale (只有 decimal32 才有)
|
|
530
|
+
// 02 00 00 00 scale = 2
|
|
531
|
+
// block 0
|
|
532
|
+
// 04 00 block.rows = 4
|
|
533
|
+
// 01 block.unit = 1
|
|
534
|
+
// 00 reserved
|
|
535
|
+
// 03 02 03 02 block.lengths = [3, 2, 3, 2]
|
|
536
|
+
// 64 00 00 00 c8 00 00 00 2c 01 00 00 90 01 00 00 f4 01 00 00 58 02 00 00 bc 02 00 00 ... 12 more bytes> [100, 200, 300, ...]
|
|
537
|
+
// block 1
|
|
538
|
+
// ...
|
|
539
|
+
const type_ = type - 64;
|
|
525
540
|
const cols = dv.getUint32(4, le);
|
|
526
541
|
let blocks = [];
|
|
542
|
+
// decimal32/64 会在所有 blocks 之前多一个 scale
|
|
543
|
+
if (type_ === DdbType.decimal32 || type_ === DdbType.decimal64) {
|
|
544
|
+
blocks.scale = dv.getInt32(i_items_start, le);
|
|
545
|
+
i_items_start += 4;
|
|
546
|
+
}
|
|
527
547
|
let i_block_start = i_items_start;
|
|
528
548
|
// 解析一个 block
|
|
529
549
|
for (let i_row = 0; i_row < rows;) {
|
|
@@ -552,7 +572,20 @@ export class DdbObj {
|
|
|
552
572
|
let total_length = 0;
|
|
553
573
|
for (const x of lengths)
|
|
554
574
|
total_length += x;
|
|
555
|
-
|
|
575
|
+
let len_items;
|
|
576
|
+
let data;
|
|
577
|
+
switch (type_) {
|
|
578
|
+
case DdbType.decimal32:
|
|
579
|
+
len_items = total_length * 4;
|
|
580
|
+
data = new Int32Array(buf.buffer.slice(buf.byteOffset + i_data_start, buf.byteOffset + i_data_start + len_items));
|
|
581
|
+
break;
|
|
582
|
+
case DdbType.decimal64:
|
|
583
|
+
len_items = total_length * 8;
|
|
584
|
+
data = new BigInt64Array(buf.buffer.slice(buf.byteOffset + i_data_start, buf.byteOffset + i_data_start + len_items));
|
|
585
|
+
break;
|
|
586
|
+
default:
|
|
587
|
+
[len_items, data] = this.parse_vector_items(buf.subarray(i_data_start), le, type - 64, total_length);
|
|
588
|
+
}
|
|
556
589
|
blocks.push({
|
|
557
590
|
unit,
|
|
558
591
|
rows,
|
|
@@ -867,6 +900,10 @@ export class DdbObj {
|
|
|
867
900
|
if (form === DdbForm.vector && 64 <= type && type < 128)
|
|
868
901
|
return [
|
|
869
902
|
Uint32Array.of(this.rows, this.cols),
|
|
903
|
+
...(type - 64 === DdbType.decimal32 || type - 64 === DdbType.decimal64) ?
|
|
904
|
+
[Int32Array.of(this.value.scale)]
|
|
905
|
+
:
|
|
906
|
+
[],
|
|
870
907
|
...this.value.map(block => ([
|
|
871
908
|
Uint16Array.of(block.rows),
|
|
872
909
|
Uint8Array.of(block.unit, 0),
|
|
@@ -925,10 +962,7 @@ export class DdbObj {
|
|
|
925
962
|
})();
|
|
926
963
|
if (!body)
|
|
927
964
|
return new Uint8Array(0);
|
|
928
|
-
return concat([
|
|
929
|
-
header,
|
|
930
|
-
...body
|
|
931
|
-
]);
|
|
965
|
+
return concat([header, ...body]);
|
|
932
966
|
}
|
|
933
967
|
static pack_vector_body(value, type, length) {
|
|
934
968
|
switch (type) {
|
|
@@ -1018,7 +1052,7 @@ export class DdbObj {
|
|
|
1018
1052
|
throw new Error(`vector ${DdbType[type]} 暂不支持序列化`);
|
|
1019
1053
|
}
|
|
1020
1054
|
}
|
|
1021
|
-
toString(options) {
|
|
1055
|
+
toString(options = { nullstr: true, quote: true }) {
|
|
1022
1056
|
const type = this.inspect_type();
|
|
1023
1057
|
const data = (() => {
|
|
1024
1058
|
switch (this.form) {
|
|
@@ -1054,7 +1088,18 @@ export class DdbObj {
|
|
|
1054
1088
|
for (const length of lengths) {
|
|
1055
1089
|
let _items = new Array(Math.min(limit, length));
|
|
1056
1090
|
for (let i = 0; i < _items.length; i++)
|
|
1057
|
-
|
|
1091
|
+
if (_type === DdbType.decimal32 || _type === DdbType.decimal64) {
|
|
1092
|
+
const x = data[acc_len + i];
|
|
1093
|
+
if (x === nulls.int64 && _type === DdbType.decimal64 ||
|
|
1094
|
+
x === nulls.int32 && _type === DdbType.decimal32)
|
|
1095
|
+
return '';
|
|
1096
|
+
const { scale } = this.value;
|
|
1097
|
+
const s = String(x < 0 ? -x : x).padStart(scale, '0');
|
|
1098
|
+
const str = (x < 0 ? '-' : '') + (scale ? `${s.slice(0, -scale) || '0'}.${s.slice(-scale)}` : s);
|
|
1099
|
+
_items[i] = options.colors ? green(str) : str;
|
|
1100
|
+
}
|
|
1101
|
+
else
|
|
1102
|
+
_items[i] = format(_type, data[acc_len + i], this.le, options);
|
|
1058
1103
|
items[i_items++] = format_array(_items, length > limit);
|
|
1059
1104
|
acc_len += length;
|
|
1060
1105
|
}
|
|
@@ -1165,27 +1210,11 @@ export class DdbObj {
|
|
|
1165
1210
|
}
|
|
1166
1211
|
to_cols() {
|
|
1167
1212
|
assert(this.form === DdbForm.table, 'form must be DdbForm.table, otherwise it cannot to_cols');
|
|
1168
|
-
return this.value.map(col => {
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
switch (col.type) {
|
|
1174
|
-
case DdbType.char:
|
|
1175
|
-
col_.render = (value) => format(DdbType.char, value, this.le);
|
|
1176
|
-
break;
|
|
1177
|
-
case DdbType.timestamp:
|
|
1178
|
-
col_.render = (value) => timestamp2str(value);
|
|
1179
|
-
break;
|
|
1180
|
-
case DdbType.date:
|
|
1181
|
-
col_.render = (value) => date2str(value);
|
|
1182
|
-
break;
|
|
1183
|
-
case DdbType.ipaddr:
|
|
1184
|
-
col_.render = (value) => ipaddr2str(value, this.le);
|
|
1185
|
-
break;
|
|
1186
|
-
}
|
|
1187
|
-
return col_;
|
|
1188
|
-
});
|
|
1213
|
+
return this.value.map(col => ({
|
|
1214
|
+
title: col.name,
|
|
1215
|
+
dataIndex: col.name,
|
|
1216
|
+
render: (value) => format(col.type, value, col.le, { nullstr: false, quote: false })
|
|
1217
|
+
}));
|
|
1189
1218
|
}
|
|
1190
1219
|
to_rows() {
|
|
1191
1220
|
assert(this.form === DdbForm.table, 'form must be DdbForm.table, otherwise it cannot to_rows');
|
|
@@ -1242,8 +1271,8 @@ let _decimals = 20;
|
|
|
1242
1271
|
let _formatter = Intl.NumberFormat('en-US', { maximumFractionDigits: 20 });
|
|
1243
1272
|
/** Formats a single element (value) as a string according to DdbType, null returns a 'null' string */
|
|
1244
1273
|
export function format(type, value, le, options = {}) {
|
|
1274
|
+
const { decimals, nullstr = false, colors = false, quote = false } = options;
|
|
1245
1275
|
const formatter = (() => {
|
|
1246
|
-
const { decimals } = options;
|
|
1247
1276
|
if (decimals === undefined || decimals === null)
|
|
1248
1277
|
return default_formatter;
|
|
1249
1278
|
if (decimals !== _decimals) {
|
|
@@ -1252,117 +1281,176 @@ export function format(type, value, le, options = {}) {
|
|
|
1252
1281
|
}
|
|
1253
1282
|
return _formatter;
|
|
1254
1283
|
})();
|
|
1284
|
+
function get_nullstr() {
|
|
1285
|
+
return nullstr ?
|
|
1286
|
+
colors ? grey('null') : 'null'
|
|
1287
|
+
:
|
|
1288
|
+
'';
|
|
1289
|
+
}
|
|
1255
1290
|
switch (type) {
|
|
1256
1291
|
case DdbType.bool:
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1292
|
+
if (value === null || value === nulls.int8)
|
|
1293
|
+
return get_nullstr();
|
|
1294
|
+
else {
|
|
1295
|
+
const str = String(Boolean(value));
|
|
1296
|
+
return colors ? blue(str) : str;
|
|
1297
|
+
}
|
|
1261
1298
|
case DdbType.char:
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1299
|
+
if (value === null || value === nulls.int8)
|
|
1300
|
+
return get_nullstr();
|
|
1301
|
+
else {
|
|
1302
|
+
let str =
|
|
1303
|
+
// ascii printable
|
|
1304
|
+
// http://facweb.cs.depaul.edu/sjost/it212/documents/ascii-pr.htm
|
|
1305
|
+
(32 <= value && value <= 126) ?
|
|
1306
|
+
String.fromCharCode(value)
|
|
1307
|
+
:
|
|
1308
|
+
'\\' + String(value);
|
|
1309
|
+
if (quote)
|
|
1310
|
+
str = str.quote();
|
|
1311
|
+
return colors ? green(str) : str;
|
|
1312
|
+
}
|
|
1271
1313
|
case DdbType.short:
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1314
|
+
if (value === null || value === nulls.int16)
|
|
1315
|
+
return get_nullstr();
|
|
1316
|
+
else {
|
|
1317
|
+
const str = default_formatter.format(value);
|
|
1318
|
+
return colors ? green(str) : str;
|
|
1319
|
+
}
|
|
1276
1320
|
case DdbType.int:
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1321
|
+
if (value === null || value === nulls.int32)
|
|
1322
|
+
return get_nullstr();
|
|
1323
|
+
else {
|
|
1324
|
+
const str = default_formatter.format(value);
|
|
1325
|
+
return colors ? green(str) : str;
|
|
1326
|
+
}
|
|
1281
1327
|
case DdbType.long:
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1328
|
+
if (value === null || value === nulls.int64)
|
|
1329
|
+
return get_nullstr();
|
|
1330
|
+
else {
|
|
1331
|
+
const str = default_formatter.format(value);
|
|
1332
|
+
return colors ? green(str) : str;
|
|
1333
|
+
}
|
|
1286
1334
|
case DdbType.date:
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1335
|
+
if (value === null || value === nulls.int16)
|
|
1336
|
+
return get_nullstr();
|
|
1337
|
+
else {
|
|
1338
|
+
const str = date2str(value);
|
|
1339
|
+
return colors ? magenta(str) : str;
|
|
1340
|
+
}
|
|
1291
1341
|
case DdbType.month:
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1342
|
+
if (value === null || value === nulls.int32)
|
|
1343
|
+
return get_nullstr();
|
|
1344
|
+
else {
|
|
1345
|
+
const str = month2str(value);
|
|
1346
|
+
return colors ? magenta(str) : str;
|
|
1347
|
+
}
|
|
1296
1348
|
case DdbType.time:
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1349
|
+
if (value === null || value === nulls.int32)
|
|
1350
|
+
return get_nullstr();
|
|
1351
|
+
else {
|
|
1352
|
+
const str = time2str(value);
|
|
1353
|
+
return colors ? magenta(str) : str;
|
|
1354
|
+
}
|
|
1301
1355
|
case DdbType.minute:
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1356
|
+
if (value === null || value === nulls.int16)
|
|
1357
|
+
return get_nullstr();
|
|
1358
|
+
else {
|
|
1359
|
+
const str = minute2str(value);
|
|
1360
|
+
return colors ? magenta(str) : str;
|
|
1361
|
+
}
|
|
1306
1362
|
case DdbType.second:
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1363
|
+
if (value === null || value === nulls.int32)
|
|
1364
|
+
return get_nullstr();
|
|
1365
|
+
else {
|
|
1366
|
+
const str = second2str(value);
|
|
1367
|
+
return colors ? magenta(str) : str;
|
|
1368
|
+
}
|
|
1311
1369
|
case DdbType.datetime:
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1370
|
+
if (value === null || value === nulls.int32)
|
|
1371
|
+
return get_nullstr();
|
|
1372
|
+
else {
|
|
1373
|
+
const str = datetime2str(value);
|
|
1374
|
+
return colors ? magenta(str) : str;
|
|
1375
|
+
}
|
|
1316
1376
|
case DdbType.timestamp:
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1377
|
+
if (value === null || value === nulls.int64)
|
|
1378
|
+
return get_nullstr();
|
|
1379
|
+
else {
|
|
1380
|
+
const str = timestamp2str(value);
|
|
1381
|
+
return colors ? magenta(str) : str;
|
|
1382
|
+
}
|
|
1321
1383
|
case DdbType.nanotime:
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1384
|
+
if (value === null || value === nulls.int64)
|
|
1385
|
+
return get_nullstr();
|
|
1386
|
+
else {
|
|
1387
|
+
const str = nanotime2str(value);
|
|
1388
|
+
return colors ? magenta(str) : str;
|
|
1389
|
+
}
|
|
1326
1390
|
case DdbType.nanotimestamp:
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1391
|
+
if (value === null || value === nulls.int64)
|
|
1392
|
+
return get_nullstr();
|
|
1393
|
+
else {
|
|
1394
|
+
const str = nanotimestamp2str(value);
|
|
1395
|
+
return colors ? magenta(str) : str;
|
|
1396
|
+
}
|
|
1331
1397
|
case DdbType.float:
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1398
|
+
if (value === null || value === nulls.float32)
|
|
1399
|
+
return get_nullstr();
|
|
1400
|
+
else {
|
|
1401
|
+
const str = formatter.format(value);
|
|
1402
|
+
return colors ? green(str) : str;
|
|
1403
|
+
}
|
|
1336
1404
|
case DdbType.double:
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1405
|
+
if (value === null || value === nulls.double)
|
|
1406
|
+
return get_nullstr();
|
|
1407
|
+
else {
|
|
1408
|
+
const str = formatter.format(value);
|
|
1409
|
+
return colors ? green(str) : str;
|
|
1410
|
+
}
|
|
1341
1411
|
case DdbType.symbol:
|
|
1342
|
-
case DdbType.string:
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1412
|
+
case DdbType.string: {
|
|
1413
|
+
let str = value;
|
|
1414
|
+
if (quote)
|
|
1415
|
+
str = str.quote('single');
|
|
1416
|
+
return colors ? cyan(str) : str;
|
|
1417
|
+
}
|
|
1418
|
+
case DdbType.uuid: {
|
|
1419
|
+
const str = uuid2str(value, le);
|
|
1420
|
+
return colors ? cyan(str) : str;
|
|
1421
|
+
}
|
|
1422
|
+
case DdbType.functiondef: {
|
|
1423
|
+
const str = value.name.quote('single');
|
|
1424
|
+
return colors ? cyan(str) : str;
|
|
1425
|
+
}
|
|
1348
1426
|
case DdbType.handle:
|
|
1349
1427
|
case DdbType.code:
|
|
1350
|
-
case DdbType.resource:
|
|
1351
|
-
|
|
1428
|
+
case DdbType.resource: {
|
|
1429
|
+
const str = value.quote('single');
|
|
1430
|
+
return colors ? cyan(str) : str;
|
|
1431
|
+
}
|
|
1352
1432
|
case DdbType.datehour:
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
case DdbType.
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1433
|
+
if (value === null || value === nulls.int32)
|
|
1434
|
+
return get_nullstr();
|
|
1435
|
+
else {
|
|
1436
|
+
const str = datehour2str(value);
|
|
1437
|
+
return colors ? magenta(str) : str;
|
|
1438
|
+
}
|
|
1439
|
+
case DdbType.ipaddr: {
|
|
1440
|
+
const str = ipaddr2str(value, le);
|
|
1441
|
+
return colors ? cyan(str) : str;
|
|
1442
|
+
}
|
|
1443
|
+
case DdbType.int128: {
|
|
1444
|
+
const str = int1282str(value, le);
|
|
1445
|
+
return colors ? cyan(str) : str;
|
|
1446
|
+
}
|
|
1447
|
+
case DdbType.blob: {
|
|
1448
|
+
const str = value.length > 100 ?
|
|
1363
1449
|
DdbObj.dec.decode(value.subarray(0, 98)) + '…'
|
|
1364
1450
|
:
|
|
1365
|
-
DdbObj.dec.decode(value)
|
|
1451
|
+
DdbObj.dec.decode(value);
|
|
1452
|
+
return colors ? cyan(str) : str;
|
|
1453
|
+
}
|
|
1366
1454
|
case DdbType.point: {
|
|
1367
1455
|
const [x, y] = value;
|
|
1368
1456
|
return `(${format(DdbType.double, x, le, options)}, ${format(DdbType.double, y, le, options)})`;
|
|
@@ -1373,7 +1461,8 @@ export function format(type, value, le, options = {}) {
|
|
|
1373
1461
|
}
|
|
1374
1462
|
case DdbType.duration: {
|
|
1375
1463
|
const { data, unit } = value;
|
|
1376
|
-
|
|
1464
|
+
const str = `${data}${DdbDurationUnit[unit]}`;
|
|
1465
|
+
return colors ? magenta(str) : str;
|
|
1377
1466
|
}
|
|
1378
1467
|
case DdbType.decimal32:
|
|
1379
1468
|
case DdbType.decimal64: {
|
|
@@ -1381,16 +1470,18 @@ export function format(type, value, le, options = {}) {
|
|
|
1381
1470
|
if (data === null ||
|
|
1382
1471
|
(data === nulls.int64 && type === DdbType.decimal64) ||
|
|
1383
1472
|
(data === nulls.int32 && type === DdbType.decimal32))
|
|
1384
|
-
return
|
|
1473
|
+
return get_nullstr();
|
|
1385
1474
|
const s = String(data < 0 ? -data : data).padStart(scale, '0');
|
|
1386
|
-
|
|
1475
|
+
const str = (data < 0 ? '-' : '') + (scale ? `${s.slice(0, -scale) || '0'}.${s.slice(-scale)}` : s);
|
|
1476
|
+
return colors ? green(str) : str;
|
|
1387
1477
|
}
|
|
1388
1478
|
default:
|
|
1389
1479
|
return String(value);
|
|
1390
1480
|
}
|
|
1391
1481
|
}
|
|
1392
|
-
/** formatted vector, the index-th item in the collection is a string
|
|
1482
|
+
/** formatted vector, the index-th item in the collection is a string */
|
|
1393
1483
|
export function formati(obj, index, options = {}) {
|
|
1484
|
+
assert(index < obj.rows, 'index < obj.rows');
|
|
1394
1485
|
if (64 <= obj.type && obj.type < 128) { // array vector
|
|
1395
1486
|
// 因为 array vector 目前只支持:Logical, Integral(不包括 INT128, COMPRESS 类型), Floating, Temporal
|
|
1396
1487
|
// 都对应 TypedArray 中的一格,所以 lengths.length 等于 block 中的 row 的个数
|
|
@@ -1418,18 +1509,26 @@ export function formati(obj, index, options = {}) {
|
|
|
1418
1509
|
const limit = 10;
|
|
1419
1510
|
let items = new Array(Math.min(limit, length));
|
|
1420
1511
|
for (let i = 0; i < items.length; i++)
|
|
1421
|
-
|
|
1512
|
+
if (_type === DdbType.decimal32 || _type === DdbType.decimal64) {
|
|
1513
|
+
const x = data[acc_len + i];
|
|
1514
|
+
if (x === nulls.int64 && _type === DdbType.decimal64 ||
|
|
1515
|
+
x === nulls.int32 && _type === DdbType.decimal32)
|
|
1516
|
+
return '';
|
|
1517
|
+
const { scale } = obj.value;
|
|
1518
|
+
const s = String(x < 0 ? -x : x).padStart(scale, '0');
|
|
1519
|
+
const str = (x < 0 ? '-' : '') + (scale ? `${s.slice(0, -scale) || '0'}.${s.slice(-scale)}` : s);
|
|
1520
|
+
items[i] = options.colors ? green(str) : str;
|
|
1521
|
+
}
|
|
1522
|
+
else
|
|
1523
|
+
items[i] = format(_type, data[acc_len + i], obj.le, options);
|
|
1422
1524
|
return (items.join(', ') + (length > limit ? ', ...' : '')).bracket('square');
|
|
1423
1525
|
}
|
|
1424
1526
|
}
|
|
1425
1527
|
}
|
|
1426
1528
|
switch (obj.type) {
|
|
1427
|
-
case DdbType.string:
|
|
1428
|
-
case DdbType.symbol:
|
|
1429
|
-
return obj.value[index];
|
|
1430
1529
|
case DdbType.symbol_extended: {
|
|
1431
1530
|
const { base, data } = obj.value;
|
|
1432
|
-
return base[data[index]];
|
|
1531
|
+
return format(DdbType.string, base[data[index]], obj.le, options);
|
|
1433
1532
|
}
|
|
1434
1533
|
case DdbType.uuid:
|
|
1435
1534
|
case DdbType.int128:
|
|
@@ -1453,7 +1552,8 @@ export function formati(obj, index, options = {}) {
|
|
|
1453
1552
|
x === nulls.int32 && obj.type === DdbType.decimal32)
|
|
1454
1553
|
return '';
|
|
1455
1554
|
const s = String(x < 0 ? -x : x).padStart(scale, '0');
|
|
1456
|
-
|
|
1555
|
+
const str = (x < 0 ? '-' : '') + (scale ? `${s.slice(0, -scale) || '0'}.${s.slice(-scale)}` : s);
|
|
1556
|
+
return options.colors ? green(str) : str;
|
|
1457
1557
|
}
|
|
1458
1558
|
default:
|
|
1459
1559
|
return format(obj.type, obj.value[index], obj.le, options);
|
|
@@ -1756,10 +1856,14 @@ export class DdbTable extends DdbObj {
|
|
|
1756
1856
|
}
|
|
1757
1857
|
}
|
|
1758
1858
|
export function date2ms(date) {
|
|
1859
|
+
// 将 server 的本地时间 (以 ms 为单位,1970.01.01 00:00:00 作为零点) 当成是客户端这里的本地的时间,然后根据本地的时区信息转换为 utc 时间
|
|
1860
|
+
// 本地的时区与实际的时间值相关,timezone offset 可能会受到夏令时 (DST) 的影响
|
|
1861
|
+
// 得到的 utc 毫秒数交给 js date 或者 dayjs 去格式化
|
|
1862
|
+
const ms = 1000 * 3600 * 24 * date;
|
|
1759
1863
|
return (date === null || date === nulls.int32) ?
|
|
1760
1864
|
null
|
|
1761
1865
|
:
|
|
1762
|
-
|
|
1866
|
+
1000 * 60 * new Date(ms).getTimezoneOffset() + ms;
|
|
1763
1867
|
}
|
|
1764
1868
|
export function date2str(date, format = 'YYYY.MM.DD') {
|
|
1765
1869
|
return (date === null || date === nulls.int32) ?
|
|
@@ -1786,7 +1890,7 @@ export function time2ms(time) {
|
|
|
1786
1890
|
return (time === null || time === nulls.int32) ?
|
|
1787
1891
|
null
|
|
1788
1892
|
:
|
|
1789
|
-
|
|
1893
|
+
1000 * 60 * new Date(time).getTimezoneOffset() + time;
|
|
1790
1894
|
}
|
|
1791
1895
|
export function time2str(time, format = 'HH:mm:ss.SSS') {
|
|
1792
1896
|
return (time === null || time === nulls.int32) ?
|
|
@@ -1795,10 +1899,11 @@ export function time2str(time, format = 'HH:mm:ss.SSS') {
|
|
|
1795
1899
|
dayjs(time2ms(time)).format(format);
|
|
1796
1900
|
}
|
|
1797
1901
|
export function minute2ms(minute) {
|
|
1902
|
+
const ms = 60 * 1000 * minute;
|
|
1798
1903
|
return (minute === null || minute === nulls.int32) ?
|
|
1799
1904
|
null
|
|
1800
1905
|
:
|
|
1801
|
-
|
|
1906
|
+
1000 * 60 * new Date(ms).getTimezoneOffset() + ms;
|
|
1802
1907
|
}
|
|
1803
1908
|
export function minute2str(minute, format = 'HH:mm[m]') {
|
|
1804
1909
|
return (minute === null || minute === nulls.int32) ?
|
|
@@ -1807,10 +1912,11 @@ export function minute2str(minute, format = 'HH:mm[m]') {
|
|
|
1807
1912
|
dayjs(minute2ms(minute)).format(format);
|
|
1808
1913
|
}
|
|
1809
1914
|
export function second2ms(second) {
|
|
1915
|
+
const ms = 1000 * second;
|
|
1810
1916
|
return (second === null || second === nulls.int32) ?
|
|
1811
1917
|
null
|
|
1812
1918
|
:
|
|
1813
|
-
|
|
1919
|
+
1000 * 60 * new Date(ms).getTimezoneOffset() + ms;
|
|
1814
1920
|
}
|
|
1815
1921
|
export function second2str(second, format = 'HH:mm:ss') {
|
|
1816
1922
|
return (second === null || second === nulls.int32) ?
|
|
@@ -1819,10 +1925,11 @@ export function second2str(second, format = 'HH:mm:ss') {
|
|
|
1819
1925
|
dayjs(second2ms(second)).format(format);
|
|
1820
1926
|
}
|
|
1821
1927
|
export function datetime2ms(datetime) {
|
|
1928
|
+
const ms = 1000 * datetime;
|
|
1822
1929
|
return (datetime === null || datetime === nulls.int32) ?
|
|
1823
1930
|
null
|
|
1824
1931
|
:
|
|
1825
|
-
|
|
1932
|
+
1000 * 60 * new Date(ms).getTimezoneOffset() + ms;
|
|
1826
1933
|
}
|
|
1827
1934
|
export function datetime2str(datetime, format = 'YYYY.MM.DD HH:mm:ss') {
|
|
1828
1935
|
return (datetime === null || datetime === nulls.int32) ?
|
|
@@ -1831,10 +1938,11 @@ export function datetime2str(datetime, format = 'YYYY.MM.DD HH:mm:ss') {
|
|
|
1831
1938
|
dayjs(datetime2ms(datetime)).format(format);
|
|
1832
1939
|
}
|
|
1833
1940
|
export function timestamp2ms(timestamp) {
|
|
1941
|
+
const ms = Number(timestamp);
|
|
1834
1942
|
return (timestamp === null || timestamp === nulls.int64) ?
|
|
1835
1943
|
null
|
|
1836
1944
|
:
|
|
1837
|
-
|
|
1945
|
+
1000 * 60 * new Date(ms).getTimezoneOffset() + ms;
|
|
1838
1946
|
}
|
|
1839
1947
|
/** format timestamp (bigint) to string
|
|
1840
1948
|
- timestamp: bigint value
|
|
@@ -1849,16 +1957,18 @@ export function timestamp2str(timestamp, format = 'YYYY.MM.DD HH:mm:ss.SSS') {
|
|
|
1849
1957
|
dayjs(timestamp2ms(timestamp)).format(format);
|
|
1850
1958
|
}
|
|
1851
1959
|
export function datehour2ms(datehour) {
|
|
1960
|
+
const ms = 1000 * 3600 * datehour;
|
|
1852
1961
|
return (datehour === null || datehour === nulls.int32) ?
|
|
1853
1962
|
null
|
|
1854
1963
|
:
|
|
1855
|
-
|
|
1964
|
+
1000 * 60 * new Date(ms).getTimezoneOffset() + ms;
|
|
1856
1965
|
}
|
|
1857
1966
|
export function datehour2str(datehour, format = 'YYYY.MM.DDTHH') {
|
|
1967
|
+
const ms = 1000 * 3600 * datehour;
|
|
1858
1968
|
return (datehour === null || datehour === nulls.int32) ?
|
|
1859
1969
|
'null'
|
|
1860
1970
|
:
|
|
1861
|
-
dayjs(
|
|
1971
|
+
dayjs(1000 * 60 * new Date(ms).getTimezoneOffset() + ms).format(format);
|
|
1862
1972
|
}
|
|
1863
1973
|
/** parse timestamp string to bigint value
|
|
1864
1974
|
- str: timestamp string, If it is an empty string or 'null', it will return the corresponding empty value (nulls.int64)
|
|
@@ -1869,9 +1979,10 @@ export function datehour2str(datehour, format = 'YYYY.MM.DDTHH') {
|
|
|
1869
1979
|
export function str2timestamp(str, format = 'YYYY.MM.DD HH:mm:ss.SSS') {
|
|
1870
1980
|
if (!str || str === 'null')
|
|
1871
1981
|
return nulls.int64;
|
|
1872
|
-
assert('The length of the timestamp string must be equal to the length of the format string');
|
|
1873
|
-
|
|
1874
|
-
|
|
1982
|
+
assert(str.length === format.length, 'The length of the timestamp string must be equal to the length of the format string');
|
|
1983
|
+
const ms = dayjs(str, format).valueOf();
|
|
1984
|
+
return BigInt(-(1000 * 60 * new Date(ms).getTimezoneOffset()) +
|
|
1985
|
+
ms);
|
|
1875
1986
|
}
|
|
1876
1987
|
export function nanotime2ns(nanotime) {
|
|
1877
1988
|
return nanotimestamp2ns(nanotime);
|
|
@@ -1886,15 +1997,17 @@ export function nanotime2str(nanotime, format = 'HH:mm:ss.SSSSSSSSS') {
|
|
|
1886
1997
|
const i_second_end = i_second_start + 2;
|
|
1887
1998
|
const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
|
|
1888
1999
|
assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
|
|
1889
|
-
|
|
2000
|
+
const ms = Number(nanotime) / 1000000;
|
|
2001
|
+
return (dayjs(1000 * 60 * new Date(ms).getTimezoneOffset() + ms).format(format.slice(0, i_second_end)) +
|
|
1890
2002
|
format.slice(i_second_end, i_nanosecond_start) +
|
|
1891
2003
|
String(nanotime % 1000000000n).padStart(9, '0'));
|
|
1892
2004
|
}
|
|
1893
2005
|
export function nanotimestamp2ns(nanotimestamp) {
|
|
2006
|
+
const ms = Number(nanotimestamp / 1000000n);
|
|
1894
2007
|
return (nanotimestamp === null || nanotimestamp === nulls.int64) ?
|
|
1895
2008
|
null
|
|
1896
2009
|
:
|
|
1897
|
-
BigInt(
|
|
2010
|
+
BigInt(1000 * 60 * new Date(ms).getTimezoneOffset()) * 1000000n + nanotimestamp;
|
|
1898
2011
|
}
|
|
1899
2012
|
/** format nanotimestamp value (bigint) to string
|
|
1900
2013
|
- nanotimestamp: bigint value
|
|
@@ -1919,7 +2032,8 @@ export function nanotimestamp2str(nanotimestamp, format = 'YYYY.MM.DD HH:mm:ss.S
|
|
|
1919
2032
|
assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
|
|
1920
2033
|
const remainder = nanotimestamp % 1000000000n;
|
|
1921
2034
|
const borrow = remainder < 0n;
|
|
1922
|
-
|
|
2035
|
+
const ms = Number(nanotimestamp / 1000000n);
|
|
2036
|
+
return (dayjs(1000 * 60 * new Date(ms).getTimezoneOffset() +
|
|
1923
2037
|
// 去掉 9 位的纳秒部分,转化为毫秒
|
|
1924
2038
|
Number((nanotimestamp - remainder + (borrow ? -1000000000n : 0n)) / 1000000n)).format(format.slice(0, i_second_end)) +
|
|
1925
2039
|
format.slice(i_second_end, i_nanosecond_start) +
|
|
@@ -1944,8 +2058,9 @@ export function str2nanotimestamp(str, format = 'YYYY.MM.DD HH:mm:ss.SSSSSSSSS')
|
|
|
1944
2058
|
const i_second_end = i_second_start + 2;
|
|
1945
2059
|
const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
|
|
1946
2060
|
assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
|
|
1947
|
-
|
|
1948
|
-
|
|
2061
|
+
const ms = dayjs(str.slice(0, i_second_end), format.slice(0, i_second_end)).valueOf();
|
|
2062
|
+
return (BigInt(-(1000 * 60 * new Date(ms).getTimezoneOffset()) +
|
|
2063
|
+
ms) * 1000000n
|
|
1949
2064
|
+
|
|
1950
2065
|
BigInt(str.slice(i_nanosecond_start, i_nanosecond_start + 9)));
|
|
1951
2066
|
}
|
|
@@ -1971,7 +2086,14 @@ export function int1282str(buffer, le = true) {
|
|
|
1971
2086
|
return [...buf].map(x => x.toString(16)
|
|
1972
2087
|
.padStart(2, '0')).join('');
|
|
1973
2088
|
}
|
|
1974
|
-
export const winsize =
|
|
2089
|
+
export const winsize = 10_0000;
|
|
2090
|
+
export class ConnectionError extends Error {
|
|
2091
|
+
ddb;
|
|
2092
|
+
constructor(ddb) {
|
|
2093
|
+
super(`websocket is disconnected: ${ddb.url}`);
|
|
2094
|
+
this.ddb = ddb;
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
1975
2097
|
export class DDB {
|
|
1976
2098
|
/** 当前的 session id (http 或 tcp) */
|
|
1977
2099
|
sid = '0';
|
|
@@ -2036,9 +2158,10 @@ export class DDB {
|
|
|
2036
2158
|
constructor(url, options = {}) {
|
|
2037
2159
|
if (!url) {
|
|
2038
2160
|
const _url = new URL(location.href);
|
|
2039
|
-
const
|
|
2040
|
-
const
|
|
2041
|
-
|
|
2161
|
+
const dev = _url.pathname.endsWith('/console/');
|
|
2162
|
+
const hostname = _url.searchParams.get('hostname') || (dev ? '127.0.0.1' : location.hostname);
|
|
2163
|
+
const port = _url.searchParams.get('port') || (dev ? '8848' : location.port);
|
|
2164
|
+
url = `${dev ? (_url.searchParams.get('tls') === '1' ? 'wss' : 'ws') : (location.protocol === 'https:' ? 'wss' : 'ws')}://${hostname}${port ? `:${port}` : ''}/`;
|
|
2042
2165
|
}
|
|
2043
2166
|
this.url = url;
|
|
2044
2167
|
if (options.autologin !== undefined)
|
|
@@ -2053,52 +2176,50 @@ export class DDB {
|
|
|
2053
2176
|
this.streaming = options.streaming;
|
|
2054
2177
|
}
|
|
2055
2178
|
on_message(event) {
|
|
2056
|
-
|
|
2057
|
-
console.log(event.data);
|
|
2179
|
+
// 这里的实现一定会被 connect 中的实现覆盖
|
|
2058
2180
|
}
|
|
2059
|
-
/** Establish
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
this.
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
on_open: async (event, websocket) => {
|
|
2090
|
-
this.websocket = websocket;
|
|
2181
|
+
/** Establish a actual websocket connection to the DolphindB corresponding to the URL
|
|
2182
|
+
After calling, it will ensure that it has been connected to the database (ensure that websocket.ReadyState is open), otherwise an error will be reported
|
|
2183
|
+
this.autologin automatically log in when it is true */
|
|
2184
|
+
async connect() {
|
|
2185
|
+
if (this.connected)
|
|
2186
|
+
return;
|
|
2187
|
+
const ptail = this.pconnect;
|
|
2188
|
+
let resolve;
|
|
2189
|
+
this.pconnect = new Promise((_resolve, _reject) => {
|
|
2190
|
+
resolve = _resolve;
|
|
2191
|
+
});
|
|
2192
|
+
await ptail;
|
|
2193
|
+
try {
|
|
2194
|
+
if (!this.connected) {
|
|
2195
|
+
this.on_message = (event) => {
|
|
2196
|
+
assert(false, 'This is the default on_message before calling this.rpc, and it should not be called. Unless the server pushed message first after the connection is established');
|
|
2197
|
+
};
|
|
2198
|
+
this.presult = Promise.resolve(null);
|
|
2199
|
+
this.pnode_run_defined = false;
|
|
2200
|
+
this.websocket = await connect_websocket(this.url, {
|
|
2201
|
+
protocols: (() => {
|
|
2202
|
+
if (this.streaming)
|
|
2203
|
+
return 'streaming';
|
|
2204
|
+
if (this.python)
|
|
2205
|
+
return 'python';
|
|
2206
|
+
})(),
|
|
2207
|
+
on_message: (event) => {
|
|
2208
|
+
this.on_message(event);
|
|
2209
|
+
}
|
|
2210
|
+
});
|
|
2091
2211
|
if (this.streaming)
|
|
2092
2212
|
await this.subscribe();
|
|
2093
|
-
else
|
|
2213
|
+
else {
|
|
2094
2214
|
await this.rpc('connect', {});
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2215
|
+
if (this.autologin)
|
|
2216
|
+
await this.call('login', [this.username, this.password], { urgent: true });
|
|
2217
|
+
}
|
|
2098
2218
|
}
|
|
2099
|
-
}
|
|
2100
|
-
|
|
2101
|
-
|
|
2219
|
+
}
|
|
2220
|
+
finally {
|
|
2221
|
+
resolve();
|
|
2222
|
+
}
|
|
2102
2223
|
}
|
|
2103
2224
|
get_rpc_options({ urgent = false, secondary = false, async: _async = false, pickle = false, clear = false, api = false, compress = false, cancellable = true, priority = urgent ? 8 : 4, parallelism = 8, root_id = '', limit, } = {}) {
|
|
2104
2225
|
let flag = 0;
|
|
@@ -2134,12 +2255,9 @@ export class DDB {
|
|
|
2134
2255
|
disconnect() {
|
|
2135
2256
|
if (this.connected)
|
|
2136
2257
|
this.websocket.close(1000);
|
|
2137
|
-
this.on_message = () => { };
|
|
2138
|
-
this.presult = Promise.resolve(null);
|
|
2139
|
-
this.pconnect = Promise.resolve();
|
|
2140
|
-
this.pnode_run_defined = false;
|
|
2141
2258
|
}
|
|
2142
2259
|
/** rpc through websocket (function/script/variable command)
|
|
2260
|
+
When the DDB is not connected, the call will be automatically connected. When the connection is disconnected, the call will throw the Connectionerror
|
|
2143
2261
|
- type: API 类型: 'script' | 'function' | 'variable'
|
|
2144
2262
|
- options:
|
|
2145
2263
|
- urgent?: 决定 `行为标识` 那一行字符串的取值(只适用于 script 和 function)
|
|
@@ -2149,24 +2267,12 @@ export class DDB {
|
|
|
2149
2267
|
为 false 时返回的 DdbObj 仅含有 buffer 和 le,不做解析,以便后续转发、序列化
|
|
2150
2268
|
*/
|
|
2151
2269
|
async rpc(type, { script, func, args = [], vars = [], urgent, listener, parse_object, }) {
|
|
2152
|
-
if (!this.websocket)
|
|
2153
|
-
|
|
2154
|
-
let resolve;
|
|
2155
|
-
this.pconnect = new Promise((_resolve, _reject) => {
|
|
2156
|
-
resolve = _resolve;
|
|
2157
|
-
});
|
|
2158
|
-
await ptail;
|
|
2159
|
-
try {
|
|
2160
|
-
if (!this.websocket)
|
|
2161
|
-
await this.connect();
|
|
2162
|
-
}
|
|
2163
|
-
finally {
|
|
2164
|
-
resolve();
|
|
2165
|
-
}
|
|
2166
|
-
}
|
|
2270
|
+
if (!this.websocket)
|
|
2271
|
+
await this.connect();
|
|
2167
2272
|
if (!this.connected)
|
|
2168
|
-
throw new
|
|
2273
|
+
throw new ConnectionError(this);
|
|
2169
2274
|
if (func === 'pnode_run' && !this.pnode_run_defined) {
|
|
2275
|
+
// 保证并发调用 rpc 时只定义一次 pnode_run
|
|
2170
2276
|
const ptail = this.ppnoderun;
|
|
2171
2277
|
let resolve;
|
|
2172
2278
|
this.ppnoderun = new Promise((_resolve, _reject) => {
|
|
@@ -2360,8 +2466,9 @@ export class DDB {
|
|
|
2360
2466
|
async cancel() {
|
|
2361
2467
|
let ddb = new DDB(this.url, this);
|
|
2362
2468
|
try {
|
|
2469
|
+
// 因为是新建的连接,而且执行完脚本之后马上就关闭了,所以不用考虑变量泄漏的问题
|
|
2363
2470
|
await ddb.eval(`jobs = exec rootJobId from getConsoleJobs() where sessionId = ${this.sid}\n` +
|
|
2364
|
-
'if (size(jobs))\n' +
|
|
2471
|
+
(this.python ? 'if size(jobs):\n' : 'if (size(jobs))\n') +
|
|
2365
2472
|
' cancelConsoleJob(jobs)\n', { urgent: true });
|
|
2366
2473
|
}
|
|
2367
2474
|
finally {
|
|
@@ -2422,110 +2529,58 @@ export class DDB {
|
|
|
2422
2529
|
}
|
|
2423
2530
|
/** Internal stream subscription method */
|
|
2424
2531
|
async subscribe() {
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
// todo: 还有用吗?需要获取客户端 IP 吗
|
|
2532
|
+
await this.rpc('connect', {});
|
|
2533
|
+
if (this.autologin)
|
|
2534
|
+
await this.call('login', [this.username, this.password]);
|
|
2535
|
+
console.log('Subscribed to streaming table, colnames:',
|
|
2536
|
+
// string[3](['time', 'stock', 'price'])
|
|
2537
|
+
this.streaming.colnames = (await this.call('publishTable', [
|
|
2432
2538
|
'localhost',
|
|
2433
|
-
// todo: 端口还有用吗?
|
|
2434
2539
|
new DdbInt(0),
|
|
2435
2540
|
this.streaming.table,
|
|
2436
2541
|
(this.streaming.action ||= `api_js_${new Date().getTime()}`),
|
|
2437
2542
|
// new DdbInt(-1), // offset
|
|
2438
2543
|
// filter
|
|
2439
2544
|
// allow exists
|
|
2440
|
-
]);
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
reject(error);
|
|
2471
|
-
}
|
|
2472
|
-
}
|
|
2473
|
-
else {
|
|
2474
|
-
try {
|
|
2475
|
-
const dv = new DataView(buffer);
|
|
2476
|
-
const buf = new Uint8Array(buffer);
|
|
2477
|
-
let { streaming } = this;
|
|
2478
|
-
streaming.time = dv.getBigInt64(1, this.le);
|
|
2479
|
-
streaming.id = dv.getBigInt64(9, this.le);
|
|
2480
|
-
const i_topic_end = buf.indexOf(0, 17);
|
|
2481
|
-
streaming.topic = this.dec.decode(buf.subarray(17, i_topic_end));
|
|
2482
|
-
if (second) {
|
|
2483
|
-
second = false;
|
|
2484
|
-
// 是 table
|
|
2485
|
-
streaming.schema = DdbObj.parse(buf.subarray(i_topic_end + 1), this.le);
|
|
2486
|
-
console.log('Received streaming table schema:', streaming.schema);
|
|
2487
|
-
streaming.window = {
|
|
2488
|
-
offset: 0,
|
|
2489
|
-
rows: 0,
|
|
2490
|
-
segments: [],
|
|
2491
|
-
};
|
|
2492
|
-
assert(streaming.schema.rows === 0, 'schema.rows === 0');
|
|
2493
|
-
resolve(streaming.schema);
|
|
2494
|
-
}
|
|
2495
|
-
else {
|
|
2496
|
-
// 是 column 片段组成的 any vector
|
|
2497
|
-
const data = streaming.data = DdbObj.parse(buf.subarray(i_topic_end + 1), this.le);
|
|
2498
|
-
let { window: win } = streaming;
|
|
2499
|
-
win.rows += data.rows;
|
|
2500
|
-
win.segments.push(data);
|
|
2501
|
-
if (win.rows >= winsize * 2 && win.segments.length >= 2) {
|
|
2502
|
-
let winsize_ = 0;
|
|
2503
|
-
let i = win.segments.length - 1;
|
|
2504
|
-
// 往前移动至首个累计 winsize_ 超过 winsize 的位置
|
|
2505
|
-
for (; winsize_ < winsize; i--)
|
|
2506
|
-
winsize_ += win.segments[i].rows;
|
|
2507
|
-
win.segments = win.segments.slice(i);
|
|
2508
|
-
win.offset += win.rows - winsize_;
|
|
2509
|
-
win.rows = winsize_;
|
|
2510
|
-
console.log('exceeds winsize, shrink window.segments', win);
|
|
2511
|
-
}
|
|
2512
|
-
streaming.handler(streaming);
|
|
2513
|
-
}
|
|
2514
|
-
}
|
|
2515
|
-
catch (error) {
|
|
2516
|
-
reject(error);
|
|
2517
|
-
}
|
|
2545
|
+
])).value);
|
|
2546
|
+
this.streaming.window = {
|
|
2547
|
+
offset: 0,
|
|
2548
|
+
rows: 0,
|
|
2549
|
+
segments: [],
|
|
2550
|
+
};
|
|
2551
|
+
// 先准备好收到 websocket message 的 callback
|
|
2552
|
+
this.on_message = ({ data: buffer }) => {
|
|
2553
|
+
let { streaming } = this;
|
|
2554
|
+
try {
|
|
2555
|
+
const dv = new DataView(buffer);
|
|
2556
|
+
const buf = new Uint8Array(buffer);
|
|
2557
|
+
streaming.time = dv.getBigInt64(1, this.le);
|
|
2558
|
+
streaming.id = dv.getBigInt64(9, this.le);
|
|
2559
|
+
const i_topic_end = buf.indexOf(0, 17);
|
|
2560
|
+
streaming.topic = this.dec.decode(buf.subarray(17, i_topic_end));
|
|
2561
|
+
// 是 column 片段组成的 any vector
|
|
2562
|
+
const data = streaming.data = DdbObj.parse(buf.subarray(i_topic_end + 1), this.le);
|
|
2563
|
+
let { window: win } = streaming;
|
|
2564
|
+
win.rows += streaming.rows = data.value[0].rows;
|
|
2565
|
+
win.segments.push(data);
|
|
2566
|
+
if (win.rows >= winsize * 2 && win.segments.length >= 2) {
|
|
2567
|
+
let winsize_ = 0;
|
|
2568
|
+
let i = win.segments.length - 1;
|
|
2569
|
+
// 往前移动至首个累计 winsize_ 超过 winsize 的位置
|
|
2570
|
+
for (; winsize_ < winsize; i--)
|
|
2571
|
+
winsize_ += win.segments[i].value[0].rows;
|
|
2572
|
+
win.segments = win.segments.slice(i);
|
|
2573
|
+
win.offset += win.rows - winsize_;
|
|
2574
|
+
win.rows = winsize_;
|
|
2518
2575
|
}
|
|
2519
|
-
}
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
]));
|
|
2528
|
-
});
|
|
2576
|
+
}
|
|
2577
|
+
catch (error) {
|
|
2578
|
+
// 将 error 交给 handler 处理
|
|
2579
|
+
streaming.error = error;
|
|
2580
|
+
}
|
|
2581
|
+
this.streaming.handler(streaming);
|
|
2582
|
+
};
|
|
2583
|
+
return this.streaming;
|
|
2529
2584
|
}
|
|
2530
2585
|
}
|
|
2531
2586
|
export let ddb = window.ddb = new DDB();
|