dolphindb 2.0.603 → 2.0.801
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/browser.d.ts +109 -32
- package/browser.js +502 -290
- package/browser.js.map +1 -1
- package/docs.en.json +13062 -2118
- package/docs.zh.json +5224 -1600
- package/index.d.ts +110 -33
- package/index.js +390 -218
- package/index.js.map +1 -1
- package/language.d.ts +2 -2
- package/language.js +5 -3
- package/language.js.map +1 -1
- package/package.json +7 -7
package/browser.js
CHANGED
|
@@ -4,8 +4,8 @@ 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';
|
|
8
|
-
import { concat } from 'xshell/utils.browser.js';
|
|
7
|
+
import { blue, cyan, green, grey, magenta } from 'xshell/chalk.browser.js';
|
|
8
|
+
import { concat, assert } from 'xshell/utils.browser.js';
|
|
9
9
|
import { connect_websocket } from 'xshell/net.browser.js';
|
|
10
10
|
export var DdbForm;
|
|
11
11
|
(function (DdbForm) {
|
|
@@ -63,7 +63,11 @@ export var DdbType;
|
|
|
63
63
|
DdbType[DdbType["complex"] = 34] = "complex";
|
|
64
64
|
DdbType[DdbType["point"] = 35] = "point";
|
|
65
65
|
DdbType[DdbType["duration"] = 36] = "duration";
|
|
66
|
-
DdbType[DdbType["
|
|
66
|
+
DdbType[DdbType["decimal32"] = 37] = "decimal32";
|
|
67
|
+
DdbType[DdbType["decimal64"] = 38] = "decimal64";
|
|
68
|
+
DdbType[DdbType["decimal128"] = 39] = "decimal128";
|
|
69
|
+
DdbType[DdbType["object"] = 40] = "object";
|
|
70
|
+
DdbType[DdbType["pynone"] = 41] = "pynone";
|
|
67
71
|
DdbType[DdbType["symbol_extended"] = 145] = "symbol_extended";
|
|
68
72
|
})(DdbType = DdbType || (DdbType = {}));
|
|
69
73
|
export var DdbFunctionType;
|
|
@@ -115,6 +119,14 @@ export const nulls = {
|
|
|
115
119
|
bytes16: Uint8Array.from(new Array(16).fill(0))
|
|
116
120
|
};
|
|
117
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 = {}));
|
|
118
130
|
/** 可以表示所有 DolphinDB 数据库中的数据类型 */
|
|
119
131
|
export class DdbObj {
|
|
120
132
|
static dec = new TextDecoder('utf-8');
|
|
@@ -332,8 +344,7 @@ export class DdbObj {
|
|
|
332
344
|
col_labels.length += 2;
|
|
333
345
|
offset += col_labels.length;
|
|
334
346
|
}
|
|
335
|
-
|
|
336
|
-
throw new Error('matrix.datatype !== matrix.type');
|
|
347
|
+
assert(buf_data[offset] === type, 'matrix.datatype === matrix.type');
|
|
337
348
|
const rows = dv.getUint32(offset + 2, le);
|
|
338
349
|
const cols = dv.getUint32(offset + 6, le);
|
|
339
350
|
const [len_items, data] = this.parse_vector_items(buf_data.subarray(offset + 10), le, type, rows * cols // 假设小于 2**32
|
|
@@ -444,38 +455,30 @@ export class DdbObj {
|
|
|
444
455
|
case DdbType.uuid:
|
|
445
456
|
case DdbType.ipaddr:
|
|
446
457
|
case DdbType.int128:
|
|
447
|
-
return [
|
|
448
|
-
16,
|
|
449
|
-
buf.slice(0, 16)
|
|
450
|
-
];
|
|
458
|
+
return [16, buf.slice(0, 16)];
|
|
451
459
|
case DdbType.blob: {
|
|
452
460
|
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
453
461
|
const length = dv.getUint32(0, le);
|
|
454
|
-
return [
|
|
455
|
-
4 + length,
|
|
456
|
-
buf.slice(4, 4 + length)
|
|
457
|
-
];
|
|
462
|
+
return [4 + length, buf.slice(4, 4 + length)];
|
|
458
463
|
}
|
|
459
464
|
case DdbType.complex:
|
|
460
465
|
case DdbType.point: {
|
|
461
466
|
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
462
|
-
return [
|
|
463
|
-
16,
|
|
464
|
-
[
|
|
465
|
-
dv.getFloat64(0, le),
|
|
466
|
-
dv.getFloat64(8, le)
|
|
467
|
-
]
|
|
468
|
-
];
|
|
467
|
+
return [16, [dv.getFloat64(0, le), dv.getFloat64(8, le)]];
|
|
469
468
|
}
|
|
470
469
|
case DdbType.duration: {
|
|
471
470
|
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
472
|
-
return [
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
471
|
+
return [8, { unit: dv.getUint32(4, le), data: dv.getInt32(0, le) }];
|
|
472
|
+
}
|
|
473
|
+
case DdbType.decimal32: {
|
|
474
|
+
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
475
|
+
const data = dv.getInt32(4, le);
|
|
476
|
+
return [8, { scale: dv.getInt32(0, le), data: data === nulls.int32 ? null : data }];
|
|
477
|
+
}
|
|
478
|
+
case DdbType.decimal64: {
|
|
479
|
+
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
480
|
+
const data = dv.getBigInt64(4, le);
|
|
481
|
+
return [12, { scale: dv.getInt32(0, le), data: data === nulls.int64 ? null : data }];
|
|
479
482
|
}
|
|
480
483
|
default:
|
|
481
484
|
throw new Error(`${DdbType[type] || type} 暂时不支持解析`);
|
|
@@ -636,7 +639,7 @@ export class DdbObj {
|
|
|
636
639
|
// 02 00 00 00 symbol base size = 2
|
|
637
640
|
// 00 61 61 00 以 \0 分割的字符串
|
|
638
641
|
// 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00>
|
|
639
|
-
const dv = new DataView(buf.buffer, buf.byteOffset
|
|
642
|
+
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
640
643
|
const base_id = dv.getUint32(0, le);
|
|
641
644
|
const base_size = dv.getUint32(4, le);
|
|
642
645
|
let base_length = 0;
|
|
@@ -671,7 +674,7 @@ export class DdbObj {
|
|
|
671
674
|
// 04 00 00 00 61 62 63 64
|
|
672
675
|
// 04 00 00 00 61 62 63 64>
|
|
673
676
|
let value = new Array(length);
|
|
674
|
-
const dv = new DataView(buf.buffer, buf.byteOffset
|
|
677
|
+
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
675
678
|
let i_head = 0;
|
|
676
679
|
for (let i = 0; i < length; i++) {
|
|
677
680
|
const sublen = dv.getUint32(i_head, le);
|
|
@@ -711,6 +714,31 @@ export class DdbObj {
|
|
|
711
714
|
values
|
|
712
715
|
];
|
|
713
716
|
}
|
|
717
|
+
// 25 01 type = decimal32, form = vector
|
|
718
|
+
// 02 00 00 00 01 00 00 00
|
|
719
|
+
// 00 00 00 00 scale = 0
|
|
720
|
+
// 01 00 00 00 data[0] = 1
|
|
721
|
+
// 3a 01 00 00 data[1] = 0x013a = 314
|
|
722
|
+
case DdbType.decimal32: {
|
|
723
|
+
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
724
|
+
return [
|
|
725
|
+
4 + 4 * length,
|
|
726
|
+
{
|
|
727
|
+
scale: dv.getInt32(0, le),
|
|
728
|
+
data: new Int32Array(buf.buffer.slice(buf.byteOffset + 4, buf.byteOffset + 4 + 4 * length))
|
|
729
|
+
}
|
|
730
|
+
];
|
|
731
|
+
}
|
|
732
|
+
case DdbType.decimal64: {
|
|
733
|
+
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
734
|
+
return [
|
|
735
|
+
4 + 8 * length,
|
|
736
|
+
{
|
|
737
|
+
scale: dv.getInt32(0, le),
|
|
738
|
+
data: new BigInt64Array(buf.buffer.slice(buf.byteOffset + 4, buf.byteOffset + 4 + 8 * length))
|
|
739
|
+
}
|
|
740
|
+
];
|
|
741
|
+
}
|
|
714
742
|
// case DdbType.duration: -> 实际会返回一个 any vector
|
|
715
743
|
// [2y, 1M, 3d, 7H, 11m, 12s, 15ms, 16us, 17ns]
|
|
716
744
|
// <Buffer 19 01 type = any, form = vector
|
|
@@ -814,10 +842,7 @@ export class DdbObj {
|
|
|
814
842
|
case DdbType.int128:
|
|
815
843
|
return [this.value];
|
|
816
844
|
case DdbType.blob:
|
|
817
|
-
return [
|
|
818
|
-
Uint32Array.of(this.value.byteLength),
|
|
819
|
-
this.value
|
|
820
|
-
];
|
|
845
|
+
return [Uint32Array.of(this.value.byteLength), this.value];
|
|
821
846
|
case DdbType.complex:
|
|
822
847
|
case DdbType.point:
|
|
823
848
|
return [Float64Array.from(this.value)];
|
|
@@ -825,6 +850,14 @@ export class DdbObj {
|
|
|
825
850
|
const { unit, data } = this.value;
|
|
826
851
|
return [Int32Array.of(data, unit)];
|
|
827
852
|
}
|
|
853
|
+
case DdbType.decimal32: {
|
|
854
|
+
const { scale, data } = this.value;
|
|
855
|
+
return [Int32Array.of(scale, data === null ? nulls.int32 : data)];
|
|
856
|
+
}
|
|
857
|
+
case DdbType.decimal64: {
|
|
858
|
+
const { scale, data } = this.value;
|
|
859
|
+
return [Int32Array.of(scale), BigInt64Array.of(data === null ? nulls.int64 : data)];
|
|
860
|
+
}
|
|
828
861
|
default:
|
|
829
862
|
throw new Error(`${DdbType[type] || type} 暂时不支持序列化`);
|
|
830
863
|
}
|
|
@@ -846,12 +879,7 @@ export class DdbObj {
|
|
|
846
879
|
...DdbObj.pack_vector_body(value, type, this.rows)
|
|
847
880
|
];
|
|
848
881
|
case DdbForm.set:
|
|
849
|
-
return [
|
|
850
|
-
new DdbObj({
|
|
851
|
-
...this,
|
|
852
|
-
form: DdbForm.vector,
|
|
853
|
-
}).pack()
|
|
854
|
-
];
|
|
882
|
+
return [new DdbObj({ ...this, form: DdbForm.vector }).pack()];
|
|
855
883
|
case DdbForm.table:
|
|
856
884
|
return [
|
|
857
885
|
Uint32Array.of(this.rows, this.cols),
|
|
@@ -863,43 +891,27 @@ export class DdbObj {
|
|
|
863
891
|
...this.value.map(col => col.pack())
|
|
864
892
|
];
|
|
865
893
|
case DdbForm.dict:
|
|
866
|
-
return [
|
|
867
|
-
value[0].pack(),
|
|
868
|
-
value[1].pack(),
|
|
869
|
-
];
|
|
894
|
+
return [value[0].pack(), value[1].pack()];
|
|
870
895
|
case DdbForm.chart: {
|
|
871
|
-
const { type, stacking, bin_start, bin_end, bin_count, titles: { chart, x_axis, y_axis
|
|
896
|
+
const { type, stacking, bin_start, bin_end, bin_count, titles: { chart, x_axis, y_axis }, extras, data } = this.value;
|
|
872
897
|
const { value: [keys, values] } = new DdbDict({
|
|
873
898
|
chartType: new DdbInt(type),
|
|
874
899
|
stacking,
|
|
875
|
-
...bin_start ? {
|
|
876
|
-
|
|
877
|
-
binEnd: bin_end
|
|
878
|
-
} : {},
|
|
879
|
-
...bin_count ? {
|
|
880
|
-
binCount: bin_count
|
|
881
|
-
} : {},
|
|
900
|
+
...bin_start ? { binStart: bin_start, binEnd: bin_end } : {},
|
|
901
|
+
...bin_count ? { binCount: bin_count } : {},
|
|
882
902
|
title: new DdbVectorString([chart, x_axis, y_axis]),
|
|
883
903
|
...extras ? (() => {
|
|
884
904
|
const { multi_y_axes, ...extras_other } = extras;
|
|
885
|
-
return {
|
|
886
|
-
extras: new DdbDict({
|
|
887
|
-
multiYAxes: multi_y_axes,
|
|
888
|
-
...extras_other
|
|
889
|
-
})
|
|
890
|
-
};
|
|
905
|
+
return { extras: new DdbDict({ multiYAxes: multi_y_axes, ...extras_other }) };
|
|
891
906
|
})() : {},
|
|
892
907
|
data,
|
|
893
908
|
});
|
|
894
|
-
return [
|
|
895
|
-
keys.pack(),
|
|
896
|
-
values.pack(),
|
|
897
|
-
];
|
|
909
|
+
return [keys.pack(), values.pack()];
|
|
898
910
|
}
|
|
899
911
|
case DdbForm.matrix: {
|
|
900
912
|
const { rows, cols, data } = value;
|
|
901
913
|
return [
|
|
902
|
-
Uint8Array.of((rows
|
|
914
|
+
Uint8Array.of((rows ? 0x01 : 0x00) | (cols ? 0x02 : 0x00)),
|
|
903
915
|
...rows ? [rows.pack()] : [],
|
|
904
916
|
...cols ? [cols.pack()] : [],
|
|
905
917
|
Uint8Array.of(this.type, this.form),
|
|
@@ -908,7 +920,7 @@ export class DdbObj {
|
|
|
908
920
|
];
|
|
909
921
|
}
|
|
910
922
|
default:
|
|
911
|
-
throw new Error(`${DdbForm[form]}
|
|
923
|
+
throw new Error(`${DdbForm[form]} serialization is not currently supported`);
|
|
912
924
|
}
|
|
913
925
|
})();
|
|
914
926
|
if (!body)
|
|
@@ -997,16 +1009,21 @@ export class DdbObj {
|
|
|
997
1009
|
data
|
|
998
1010
|
];
|
|
999
1011
|
}
|
|
1012
|
+
case DdbType.decimal32:
|
|
1013
|
+
case DdbType.decimal64: {
|
|
1014
|
+
const { scale, data } = value;
|
|
1015
|
+
return [Int32Array.of(scale), data];
|
|
1016
|
+
}
|
|
1000
1017
|
default:
|
|
1001
1018
|
throw new Error(`vector ${DdbType[type]} 暂不支持序列化`);
|
|
1002
1019
|
}
|
|
1003
1020
|
}
|
|
1004
|
-
toString(
|
|
1021
|
+
toString(options = { nullstr: true, quote: true }) {
|
|
1005
1022
|
const type = this.inspect_type();
|
|
1006
1023
|
const data = (() => {
|
|
1007
1024
|
switch (this.form) {
|
|
1008
1025
|
case DdbForm.scalar:
|
|
1009
|
-
return format(this.type, this.value, this.le);
|
|
1026
|
+
return format(this.type, this.value, this.le, options);
|
|
1010
1027
|
case DdbForm.vector:
|
|
1011
1028
|
case DdbForm.pair:
|
|
1012
1029
|
case DdbForm.set: {
|
|
@@ -1037,7 +1054,7 @@ export class DdbObj {
|
|
|
1037
1054
|
for (const length of lengths) {
|
|
1038
1055
|
let _items = new Array(Math.min(limit, length));
|
|
1039
1056
|
for (let i = 0; i < _items.length; i++)
|
|
1040
|
-
_items[i] = format(_type, data[acc_len + i], this.le);
|
|
1057
|
+
_items[i] = format(_type, data[acc_len + i], this.le, options);
|
|
1041
1058
|
items[i_items++] = format_array(_items, length > limit);
|
|
1042
1059
|
acc_len += length;
|
|
1043
1060
|
}
|
|
@@ -1063,7 +1080,7 @@ export class DdbObj {
|
|
|
1063
1080
|
const len_data = value.length / 16;
|
|
1064
1081
|
let items = new Array(Math.min(limit, len_data));
|
|
1065
1082
|
for (let i = 0; i < items.length; i++)
|
|
1066
|
-
items[i] = format(this.type, value.subarray(16 * i, 16 * (i + 1)), this.le);
|
|
1083
|
+
items[i] = format(this.type, value.subarray(16 * i, 16 * (i + 1)), this.le, options);
|
|
1067
1084
|
return format_array(items, len_data > limit);
|
|
1068
1085
|
}
|
|
1069
1086
|
case DdbType.complex:
|
|
@@ -1073,24 +1090,33 @@ export class DdbObj {
|
|
|
1073
1090
|
const len_data = value.length / 2;
|
|
1074
1091
|
let items = new Array(Math.min(limit, len_data));
|
|
1075
1092
|
for (let i = 0; i < items.length; i++)
|
|
1076
|
-
items[i] = format(this.type, value.subarray(2 * i, 2 * (i + 1)), this.le);
|
|
1093
|
+
items[i] = format(this.type, value.subarray(2 * i, 2 * (i + 1)), this.le, options);
|
|
1077
1094
|
return format_array(items, len_data > limit);
|
|
1078
1095
|
}
|
|
1096
|
+
case DdbType.decimal32:
|
|
1097
|
+
case DdbType.decimal64: {
|
|
1098
|
+
const limit = 50;
|
|
1099
|
+
const { data } = this.value;
|
|
1100
|
+
let items = new Array(Math.min(limit, data.length));
|
|
1101
|
+
for (let i = 0; i < items.length; i++)
|
|
1102
|
+
items[i] = formati(this, i, options);
|
|
1103
|
+
return format_array(items, data.length > limit);
|
|
1104
|
+
}
|
|
1079
1105
|
default: {
|
|
1080
1106
|
const limit = 50;
|
|
1081
1107
|
let items = new Array(Math.min(limit, this.value.length));
|
|
1082
1108
|
for (let i = 0; i < items.length; i++)
|
|
1083
|
-
items[i] = format(this.type, this.value[i], this.le);
|
|
1109
|
+
items[i] = format(this.type, this.value[i], this.le, options);
|
|
1084
1110
|
return format_array(items, this.value.length > limit);
|
|
1085
1111
|
}
|
|
1086
1112
|
}
|
|
1087
1113
|
}
|
|
1088
1114
|
case DdbForm.chart:
|
|
1089
|
-
return JSON.stringify(this.value, (key, value) => key === 'data' ? value.toString() : value, 4);
|
|
1115
|
+
return JSON.stringify(this.value, (key, value) => key === 'data' ? value.toString(options) : value, 4);
|
|
1090
1116
|
}
|
|
1091
1117
|
return String(this.value);
|
|
1092
1118
|
})();
|
|
1093
|
-
return `${colors ? blue(type) : type}(${this.name ? `'${this.name}', ` : ''}${data})\n`;
|
|
1119
|
+
return `${options?.colors ? blue(type) : type}(${this.name ? `'${this.name}', ` : ''}${data})\n`;
|
|
1094
1120
|
}
|
|
1095
1121
|
inspect_type() {
|
|
1096
1122
|
const tname = DdbType[this.type];
|
|
@@ -1138,28 +1164,15 @@ export class DdbObj {
|
|
|
1138
1164
|
return values.map(value => this.to_ddbobj(value));
|
|
1139
1165
|
}
|
|
1140
1166
|
to_cols() {
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
|
-
case DdbType.timestamp:
|
|
1148
|
-
col_.render = value => timestamp2str(value);
|
|
1149
|
-
break;
|
|
1150
|
-
case DdbType.date:
|
|
1151
|
-
col_.render = value => date2str(value);
|
|
1152
|
-
break;
|
|
1153
|
-
case DdbType.ipaddr:
|
|
1154
|
-
col_.render = value => ipaddr2str(value, this.le);
|
|
1155
|
-
break;
|
|
1156
|
-
}
|
|
1157
|
-
return col_;
|
|
1158
|
-
});
|
|
1167
|
+
assert(this.form === DdbForm.table, 'form must be DdbForm.table, otherwise it cannot to_cols');
|
|
1168
|
+
return this.value.map(col => ({
|
|
1169
|
+
title: col.name,
|
|
1170
|
+
dataIndex: col.name,
|
|
1171
|
+
render: (value) => format(col.type, value, col.le, { nullstr: false, quote: false })
|
|
1172
|
+
}));
|
|
1159
1173
|
}
|
|
1160
1174
|
to_rows() {
|
|
1161
|
-
|
|
1162
|
-
throw new Error('this.form is not DdbForm.table, cannot call to_rows');
|
|
1175
|
+
assert(this.form === DdbForm.table, 'form must be DdbForm.table, otherwise it cannot to_rows');
|
|
1163
1176
|
let rows = new Array(this.rows);
|
|
1164
1177
|
for (let i = 0; i < this.rows; i++) {
|
|
1165
1178
|
let row = {};
|
|
@@ -1191,13 +1204,10 @@ export class DdbObj {
|
|
|
1191
1204
|
return rows;
|
|
1192
1205
|
}
|
|
1193
1206
|
to_dict({ strip, deep, } = {}) {
|
|
1194
|
-
|
|
1195
|
-
throw new Error('this.form is not DdbForm.dict and cannot be converted to js object');
|
|
1207
|
+
assert(this.form === DdbForm.dict, 'this.form must be DdbForm.dict, otherwise to_dict cannot be called to convert to js object');
|
|
1196
1208
|
const [{ value: keys, type: key_type }, { value: values, type: value_type }] = this.value;
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
if (deep && !strip)
|
|
1200
|
-
throw new Error('strip = true must be set when deep = true');
|
|
1209
|
+
assert(key_type === DdbType.string && value_type === DdbType.any, 'Currently only supports automatic conversion of dict<string, any> to js object');
|
|
1210
|
+
assert(!(deep && !strip), 'strip = true must be set when deep = true');
|
|
1201
1211
|
let obj = {};
|
|
1202
1212
|
for (let i = 0; i < this.rows; i++) {
|
|
1203
1213
|
let value = values[i];
|
|
@@ -1209,144 +1219,222 @@ export class DdbObj {
|
|
|
1209
1219
|
return obj;
|
|
1210
1220
|
}
|
|
1211
1221
|
}
|
|
1222
|
+
/** Integer must use this number formatter, InspectOptions.decimals also use this if not passed */
|
|
1223
|
+
let default_formatter = Intl.NumberFormat('en-US', { maximumFractionDigits: 20 });
|
|
1224
|
+
let _decimals = 20;
|
|
1225
|
+
/** Cache, in order to optimize performance, usually options.decimals are unchanged */
|
|
1226
|
+
let _formatter = Intl.NumberFormat('en-US', { maximumFractionDigits: 20 });
|
|
1212
1227
|
/** Formats a single element (value) as a string according to DdbType, null returns a 'null' string */
|
|
1213
1228
|
export function format(type, value, le, options = {}) {
|
|
1229
|
+
const { decimals, nullstr = false, colors = false, quote = false } = options;
|
|
1230
|
+
const formatter = (() => {
|
|
1231
|
+
if (decimals === undefined || decimals === null)
|
|
1232
|
+
return default_formatter;
|
|
1233
|
+
if (decimals !== _decimals) {
|
|
1234
|
+
_decimals = decimals;
|
|
1235
|
+
_formatter = Intl.NumberFormat('en-US', { maximumFractionDigits: decimals, minimumFractionDigits: decimals });
|
|
1236
|
+
}
|
|
1237
|
+
return _formatter;
|
|
1238
|
+
})();
|
|
1239
|
+
function get_nullstr() {
|
|
1240
|
+
return nullstr ?
|
|
1241
|
+
colors ? grey('null') : 'null'
|
|
1242
|
+
:
|
|
1243
|
+
'';
|
|
1244
|
+
}
|
|
1214
1245
|
switch (type) {
|
|
1215
1246
|
case DdbType.bool:
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1247
|
+
if (value === null || value === nulls.int8)
|
|
1248
|
+
return get_nullstr();
|
|
1249
|
+
else {
|
|
1250
|
+
const str = String(Boolean(value));
|
|
1251
|
+
return colors ? blue(str) : str;
|
|
1252
|
+
}
|
|
1220
1253
|
case DdbType.char:
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1254
|
+
if (value === null || value === nulls.int8)
|
|
1255
|
+
return get_nullstr();
|
|
1256
|
+
else {
|
|
1257
|
+
let str =
|
|
1258
|
+
// ascii printable
|
|
1259
|
+
// http://facweb.cs.depaul.edu/sjost/it212/documents/ascii-pr.htm
|
|
1260
|
+
(32 <= value && value <= 126) ?
|
|
1261
|
+
String.fromCharCode(value)
|
|
1262
|
+
:
|
|
1263
|
+
'\\' + String(value);
|
|
1264
|
+
if (quote)
|
|
1265
|
+
str = str.quote();
|
|
1266
|
+
return colors ? green(str) : str;
|
|
1267
|
+
}
|
|
1230
1268
|
case DdbType.short:
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1269
|
+
if (value === null || value === nulls.int16)
|
|
1270
|
+
return get_nullstr();
|
|
1271
|
+
else {
|
|
1272
|
+
const str = default_formatter.format(value);
|
|
1273
|
+
return colors ? green(str) : str;
|
|
1274
|
+
}
|
|
1235
1275
|
case DdbType.int:
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1276
|
+
if (value === null || value === nulls.int32)
|
|
1277
|
+
return get_nullstr();
|
|
1278
|
+
else {
|
|
1279
|
+
const str = default_formatter.format(value);
|
|
1280
|
+
return colors ? green(str) : str;
|
|
1281
|
+
}
|
|
1240
1282
|
case DdbType.long:
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1283
|
+
if (value === null || value === nulls.int64)
|
|
1284
|
+
return get_nullstr();
|
|
1285
|
+
else {
|
|
1286
|
+
const str = default_formatter.format(value);
|
|
1287
|
+
return colors ? green(str) : str;
|
|
1288
|
+
}
|
|
1245
1289
|
case DdbType.date:
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1290
|
+
if (value === null || value === nulls.int16)
|
|
1291
|
+
return get_nullstr();
|
|
1292
|
+
else {
|
|
1293
|
+
const str = date2str(value);
|
|
1294
|
+
return colors ? magenta(str) : str;
|
|
1295
|
+
}
|
|
1250
1296
|
case DdbType.month:
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1297
|
+
if (value === null || value === nulls.int32)
|
|
1298
|
+
return get_nullstr();
|
|
1299
|
+
else {
|
|
1300
|
+
const str = month2str(value);
|
|
1301
|
+
return colors ? magenta(str) : str;
|
|
1302
|
+
}
|
|
1255
1303
|
case DdbType.time:
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1304
|
+
if (value === null || value === nulls.int32)
|
|
1305
|
+
return get_nullstr();
|
|
1306
|
+
else {
|
|
1307
|
+
const str = time2str(value);
|
|
1308
|
+
return colors ? magenta(str) : str;
|
|
1309
|
+
}
|
|
1260
1310
|
case DdbType.minute:
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1311
|
+
if (value === null || value === nulls.int16)
|
|
1312
|
+
return get_nullstr();
|
|
1313
|
+
else {
|
|
1314
|
+
const str = minute2str(value);
|
|
1315
|
+
return colors ? magenta(str) : str;
|
|
1316
|
+
}
|
|
1265
1317
|
case DdbType.second:
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1318
|
+
if (value === null || value === nulls.int32)
|
|
1319
|
+
return get_nullstr();
|
|
1320
|
+
else {
|
|
1321
|
+
const str = second2str(value);
|
|
1322
|
+
return colors ? magenta(str) : str;
|
|
1323
|
+
}
|
|
1270
1324
|
case DdbType.datetime:
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1325
|
+
if (value === null || value === nulls.int32)
|
|
1326
|
+
return get_nullstr();
|
|
1327
|
+
else {
|
|
1328
|
+
const str = datetime2str(value);
|
|
1329
|
+
return colors ? magenta(str) : str;
|
|
1330
|
+
}
|
|
1275
1331
|
case DdbType.timestamp:
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1332
|
+
if (value === null || value === nulls.int64)
|
|
1333
|
+
return get_nullstr();
|
|
1334
|
+
else {
|
|
1335
|
+
const str = timestamp2str(value);
|
|
1336
|
+
return colors ? magenta(str) : str;
|
|
1337
|
+
}
|
|
1280
1338
|
case DdbType.nanotime:
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1339
|
+
if (value === null || value === nulls.int64)
|
|
1340
|
+
return get_nullstr();
|
|
1341
|
+
else {
|
|
1342
|
+
const str = nanotime2str(value);
|
|
1343
|
+
return colors ? magenta(str) : str;
|
|
1344
|
+
}
|
|
1285
1345
|
case DdbType.nanotimestamp:
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1346
|
+
if (value === null || value === nulls.int64)
|
|
1347
|
+
return get_nullstr();
|
|
1348
|
+
else {
|
|
1349
|
+
const str = nanotimestamp2str(value);
|
|
1350
|
+
return colors ? magenta(str) : str;
|
|
1351
|
+
}
|
|
1290
1352
|
case DdbType.float:
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1353
|
+
if (value === null || value === nulls.float32)
|
|
1354
|
+
return get_nullstr();
|
|
1355
|
+
else {
|
|
1356
|
+
const str = formatter.format(value);
|
|
1357
|
+
return colors ? green(str) : str;
|
|
1358
|
+
}
|
|
1295
1359
|
case DdbType.double:
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1360
|
+
if (value === null || value === nulls.double)
|
|
1361
|
+
return get_nullstr();
|
|
1362
|
+
else {
|
|
1363
|
+
const str = formatter.format(value);
|
|
1364
|
+
return colors ? green(str) : str;
|
|
1365
|
+
}
|
|
1300
1366
|
case DdbType.symbol:
|
|
1301
|
-
case DdbType.string:
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1367
|
+
case DdbType.string: {
|
|
1368
|
+
let str = value;
|
|
1369
|
+
if (quote)
|
|
1370
|
+
str = str.quote('single');
|
|
1371
|
+
return colors ? cyan(str) : str;
|
|
1372
|
+
}
|
|
1373
|
+
case DdbType.uuid: {
|
|
1374
|
+
const str = uuid2str(value, le);
|
|
1375
|
+
return colors ? cyan(str) : str;
|
|
1376
|
+
}
|
|
1377
|
+
case DdbType.functiondef: {
|
|
1378
|
+
const str = value.name.quote('single');
|
|
1379
|
+
return colors ? cyan(str) : str;
|
|
1380
|
+
}
|
|
1307
1381
|
case DdbType.handle:
|
|
1308
1382
|
case DdbType.code:
|
|
1309
|
-
case DdbType.resource:
|
|
1310
|
-
|
|
1383
|
+
case DdbType.resource: {
|
|
1384
|
+
const str = value.quote('single');
|
|
1385
|
+
return colors ? cyan(str) : str;
|
|
1386
|
+
}
|
|
1311
1387
|
case DdbType.datehour:
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
case DdbType.
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1388
|
+
if (value === null || value === nulls.int32)
|
|
1389
|
+
return get_nullstr();
|
|
1390
|
+
else {
|
|
1391
|
+
const str = datehour2str(value);
|
|
1392
|
+
return colors ? magenta(str) : str;
|
|
1393
|
+
}
|
|
1394
|
+
case DdbType.ipaddr: {
|
|
1395
|
+
const str = ipaddr2str(value, le);
|
|
1396
|
+
return colors ? cyan(str) : str;
|
|
1397
|
+
}
|
|
1398
|
+
case DdbType.int128: {
|
|
1399
|
+
const str = int1282str(value, le);
|
|
1400
|
+
return colors ? cyan(str) : str;
|
|
1401
|
+
}
|
|
1402
|
+
case DdbType.blob: {
|
|
1403
|
+
const str = value.length > 100 ?
|
|
1322
1404
|
DdbObj.dec.decode(value.subarray(0, 98)) + '…'
|
|
1323
1405
|
:
|
|
1324
|
-
DdbObj.dec.decode(value)
|
|
1406
|
+
DdbObj.dec.decode(value);
|
|
1407
|
+
return colors ? cyan(str) : str;
|
|
1408
|
+
}
|
|
1325
1409
|
case DdbType.point: {
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
x = null;
|
|
1329
|
-
if (y === nulls.double)
|
|
1330
|
-
y = null;
|
|
1331
|
-
return `(${String(x)}, ${String(y)})`;
|
|
1410
|
+
const [x, y] = value;
|
|
1411
|
+
return `(${format(DdbType.double, x, le, options)}, ${format(DdbType.double, y, le, options)})`;
|
|
1332
1412
|
}
|
|
1333
1413
|
case DdbType.complex: {
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
x = null;
|
|
1337
|
-
if (y === nulls.double)
|
|
1338
|
-
y = null;
|
|
1339
|
-
return `${String(x)}+${String(y)}i`;
|
|
1414
|
+
const [x, y] = value;
|
|
1415
|
+
return `${format(DdbType.double, x, le, options)}+${format(DdbType.double, y, le, options)}i`;
|
|
1340
1416
|
}
|
|
1341
1417
|
case DdbType.duration: {
|
|
1342
1418
|
const { data, unit } = value;
|
|
1343
|
-
|
|
1419
|
+
const str = `${data}${DdbDurationUnit[unit]}`;
|
|
1420
|
+
return colors ? magenta(str) : str;
|
|
1421
|
+
}
|
|
1422
|
+
case DdbType.decimal32:
|
|
1423
|
+
case DdbType.decimal64: {
|
|
1424
|
+
const { scale, data } = value;
|
|
1425
|
+
if (data === null ||
|
|
1426
|
+
(data === nulls.int64 && type === DdbType.decimal64) ||
|
|
1427
|
+
(data === nulls.int32 && type === DdbType.decimal32))
|
|
1428
|
+
return get_nullstr();
|
|
1429
|
+
const s = String(data < 0 ? -data : data).padStart(scale, '0');
|
|
1430
|
+
const str = (data < 0 ? '-' : '') + (scale ? `${s.slice(0, -scale) || '0'}.${s.slice(-scale)}` : s);
|
|
1431
|
+
return colors ? green(str) : str;
|
|
1344
1432
|
}
|
|
1345
1433
|
default:
|
|
1346
1434
|
return String(value);
|
|
1347
1435
|
}
|
|
1348
1436
|
}
|
|
1349
|
-
/**
|
|
1437
|
+
/** formatted vector, the index-th item in the collection is a string */
|
|
1350
1438
|
export function formati(obj, index, options = {}) {
|
|
1351
1439
|
if (64 <= obj.type && obj.type < 128) { // array vector
|
|
1352
1440
|
// 因为 array vector 目前只支持:Logical, Integral(不包括 INT128, COMPRESS 类型), Floating, Temporal
|
|
@@ -1381,12 +1469,9 @@ export function formati(obj, index, options = {}) {
|
|
|
1381
1469
|
}
|
|
1382
1470
|
}
|
|
1383
1471
|
switch (obj.type) {
|
|
1384
|
-
case DdbType.string:
|
|
1385
|
-
case DdbType.symbol:
|
|
1386
|
-
return obj.value[index];
|
|
1387
1472
|
case DdbType.symbol_extended: {
|
|
1388
1473
|
const { base, data } = obj.value;
|
|
1389
|
-
return base[data[index]];
|
|
1474
|
+
return format(DdbType.string, base[data[index]], obj.le, options);
|
|
1390
1475
|
}
|
|
1391
1476
|
case DdbType.uuid:
|
|
1392
1477
|
case DdbType.int128:
|
|
@@ -1402,6 +1487,17 @@ export function formati(obj, index, options = {}) {
|
|
|
1402
1487
|
case DdbType.complex:
|
|
1403
1488
|
case DdbType.point:
|
|
1404
1489
|
return format(obj.type, obj.value.subarray(2 * index, 2 * (index + 1)), obj.le, options);
|
|
1490
|
+
case DdbType.decimal32:
|
|
1491
|
+
case DdbType.decimal64: {
|
|
1492
|
+
const { scale, data } = obj.value;
|
|
1493
|
+
const x = data[index];
|
|
1494
|
+
if (x === nulls.int64 && obj.type === DdbType.decimal64 ||
|
|
1495
|
+
x === nulls.int32 && obj.type === DdbType.decimal32)
|
|
1496
|
+
return '';
|
|
1497
|
+
const s = String(x < 0 ? -x : x).padStart(scale, '0');
|
|
1498
|
+
const str = (x < 0 ? '-' : '') + (scale ? `${s.slice(0, -scale) || '0'}.${s.slice(-scale)}` : s);
|
|
1499
|
+
return options.colors ? green(str) : str;
|
|
1500
|
+
}
|
|
1405
1501
|
default:
|
|
1406
1502
|
return format(obj.type, obj.value[index], obj.le, options);
|
|
1407
1503
|
}
|
|
@@ -1816,8 +1912,7 @@ export function datehour2str(datehour, format = 'YYYY.MM.DDTHH') {
|
|
|
1816
1912
|
export function str2timestamp(str, format = 'YYYY.MM.DD HH:mm:ss.SSS') {
|
|
1817
1913
|
if (!str || str === 'null')
|
|
1818
1914
|
return nulls.int64;
|
|
1819
|
-
|
|
1820
|
-
throw new Error('The length of the timestamp string is not equal to the length of the format string');
|
|
1915
|
+
assert('The length of the timestamp string must be equal to the length of the format string');
|
|
1821
1916
|
return BigInt(-timezone_offset +
|
|
1822
1917
|
dayjs(str, format).valueOf());
|
|
1823
1918
|
}
|
|
@@ -1830,12 +1925,10 @@ export function nanotime2str(nanotime, format = 'HH:mm:ss.SSSSSSSSS') {
|
|
|
1830
1925
|
if (nanotime < 0n)
|
|
1831
1926
|
return String(nanotime);
|
|
1832
1927
|
const i_second_start = format.indexOf('ss');
|
|
1833
|
-
|
|
1834
|
-
throw new Error('格式串必须包含秒的格式 (ss)');
|
|
1928
|
+
assert(i_second_start !== -1, 'The format string must contain the format for seconds (ss)');
|
|
1835
1929
|
const i_second_end = i_second_start + 2;
|
|
1836
1930
|
const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
|
|
1837
|
-
|
|
1838
|
-
throw new Error('格式串必须包含纳秒的格式 (SSSSSSSSS)');
|
|
1931
|
+
assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
|
|
1839
1932
|
return (dayjs(timezone_offset + (Number(nanotime) / 1000000)).format(format.slice(0, i_second_end)) +
|
|
1840
1933
|
format.slice(i_second_end, i_nanosecond_start) +
|
|
1841
1934
|
String(nanotime % 1000000000n).padStart(9, '0'));
|
|
@@ -1863,12 +1956,10 @@ export function nanotimestamp2str(nanotimestamp, format = 'YYYY.MM.DD HH:mm:ss.S
|
|
|
1863
1956
|
if (nanotimestamp === null || nanotimestamp === nulls.int64)
|
|
1864
1957
|
return 'null';
|
|
1865
1958
|
const i_second_start = format.indexOf('ss');
|
|
1866
|
-
|
|
1867
|
-
throw new Error('The format string must contain the format for seconds (ss)');
|
|
1959
|
+
assert(i_second_start !== -1, 'The format string must contain the format for seconds (ss)');
|
|
1868
1960
|
const i_second_end = i_second_start + 2;
|
|
1869
1961
|
const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
|
|
1870
|
-
|
|
1871
|
-
throw new Error('The format string must contain the format for nanoseconds (SSSSSSSSS)');
|
|
1962
|
+
assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
|
|
1872
1963
|
const remainder = nanotimestamp % 1000000000n;
|
|
1873
1964
|
const borrow = remainder < 0n;
|
|
1874
1965
|
return (dayjs(timezone_offset +
|
|
@@ -1890,15 +1981,12 @@ export function nanotimestamp2str(nanotimestamp, format = 'YYYY.MM.DD HH:mm:ss.S
|
|
|
1890
1981
|
export function str2nanotimestamp(str, format = 'YYYY.MM.DD HH:mm:ss.SSSSSSSSS') {
|
|
1891
1982
|
if (!str || str === 'null')
|
|
1892
1983
|
return nulls.int64;
|
|
1893
|
-
|
|
1894
|
-
throw new Error('nanotimestamp string length is not equal to format string length');
|
|
1984
|
+
assert(str.length === format.length, 'nanotimestamp string length must be equal to format string length');
|
|
1895
1985
|
const i_second_start = format.indexOf('ss');
|
|
1896
|
-
|
|
1897
|
-
throw new Error('The format string must contain the format for seconds (ss)');
|
|
1986
|
+
assert(i_second_start !== -1, 'The format string must contain the format for seconds (ss)');
|
|
1898
1987
|
const i_second_end = i_second_start + 2;
|
|
1899
1988
|
const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
|
|
1900
|
-
|
|
1901
|
-
throw new Error('Format string must contain nanosecond format (SSSSSSSSS)');
|
|
1989
|
+
assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
|
|
1902
1990
|
return (BigInt(-timezone_offset +
|
|
1903
1991
|
dayjs(str.slice(0, i_second_end), format.slice(0, i_second_end)).valueOf()) * 1000000n
|
|
1904
1992
|
+
|
|
@@ -1926,6 +2014,14 @@ export function int1282str(buffer, le = true) {
|
|
|
1926
2014
|
return [...buf].map(x => x.toString(16)
|
|
1927
2015
|
.padStart(2, '0')).join('');
|
|
1928
2016
|
}
|
|
2017
|
+
export const winsize = 40;
|
|
2018
|
+
export class ConnectionError extends Error {
|
|
2019
|
+
ddb;
|
|
2020
|
+
constructor(ddb) {
|
|
2021
|
+
super(`websocket is disconnected: ${ddb.url}`);
|
|
2022
|
+
this.ddb = ddb;
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
1929
2025
|
export class DDB {
|
|
1930
2026
|
/** 当前的 session id (http 或 tcp) */
|
|
1931
2027
|
sid = '0';
|
|
@@ -1949,7 +2045,9 @@ export class DDB {
|
|
|
1949
2045
|
password = '123456';
|
|
1950
2046
|
/** python session flag (2048) */
|
|
1951
2047
|
python = false;
|
|
1952
|
-
|
|
2048
|
+
/** Whether it is a streaming data connection, this field is always null for non-streaming data */
|
|
2049
|
+
streaming = null;
|
|
2050
|
+
// --- 内部选项, 状态
|
|
1953
2051
|
print_message_buffer = false;
|
|
1954
2052
|
print_object_buffer = false;
|
|
1955
2053
|
print_message = true;
|
|
@@ -1972,6 +2070,7 @@ export class DDB {
|
|
|
1972
2070
|
- username?: DolphinDB username, default `'admin'`
|
|
1973
2071
|
- password?: DolphinDB password, default `'123456'`
|
|
1974
2072
|
- python?: set python session flag, default `false`
|
|
2073
|
+
- streaming?: When this option is set, the WebSocket connection is only used for streaming data
|
|
1975
2074
|
|
|
1976
2075
|
@example
|
|
1977
2076
|
let ddb = new DDB('ws://127.0.0.1:8848')
|
|
@@ -1989,7 +2088,7 @@ export class DDB {
|
|
|
1989
2088
|
const _url = new URL(location.href);
|
|
1990
2089
|
const hostname = _url.searchParams.get('hostname') || location.hostname;
|
|
1991
2090
|
const port = _url.searchParams.get('port') || location.port;
|
|
1992
|
-
url = `${location.protocol === 'https:' ? 'wss' : 'ws'}://${hostname}
|
|
2091
|
+
url = `${location.protocol === 'https:' ? 'wss' : 'ws'}://${hostname}:${port}/`;
|
|
1993
2092
|
}
|
|
1994
2093
|
this.url = url;
|
|
1995
2094
|
if (options.autologin !== undefined)
|
|
@@ -2000,43 +2099,54 @@ export class DDB {
|
|
|
2000
2099
|
this.password = options.password;
|
|
2001
2100
|
if (options.python !== undefined)
|
|
2002
2101
|
this.python = options.python;
|
|
2102
|
+
if (options.streaming !== undefined)
|
|
2103
|
+
this.streaming = options.streaming;
|
|
2003
2104
|
}
|
|
2004
|
-
on_message(event) {
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
this.autologin = options.autologin;
|
|
2018
|
-
if (options.username !== undefined)
|
|
2019
|
-
this.username = options.username;
|
|
2020
|
-
if (options.password !== undefined)
|
|
2021
|
-
this.password = options.password;
|
|
2022
|
-
if (options.python !== undefined)
|
|
2023
|
-
this.python = options.python;
|
|
2024
|
-
this.disconnect();
|
|
2025
|
-
await connect_websocket(this.url, {
|
|
2026
|
-
protocols: this.python ? ['python'] : [],
|
|
2027
|
-
on_open: async (event, websocket) => {
|
|
2028
|
-
this.websocket = websocket;
|
|
2029
|
-
await this.rpc('connect', {});
|
|
2030
|
-
},
|
|
2031
|
-
on_message: (event) => {
|
|
2032
|
-
this.on_message(event);
|
|
2033
|
-
}
|
|
2105
|
+
on_message(event) {
|
|
2106
|
+
// 这里的实现一定会被 connect 中的实现覆盖
|
|
2107
|
+
}
|
|
2108
|
+
/** Establish a actual websocket connection to the DolphindB corresponding to the URL
|
|
2109
|
+
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
|
|
2110
|
+
this.autologin automatically log in when it is true */
|
|
2111
|
+
async connect() {
|
|
2112
|
+
if (this.connected)
|
|
2113
|
+
return;
|
|
2114
|
+
const ptail = this.pconnect;
|
|
2115
|
+
let resolve;
|
|
2116
|
+
this.pconnect = new Promise((_resolve, _reject) => {
|
|
2117
|
+
resolve = _resolve;
|
|
2034
2118
|
});
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2119
|
+
await ptail;
|
|
2120
|
+
try {
|
|
2121
|
+
if (!this.connected) {
|
|
2122
|
+
this.on_message = (event) => {
|
|
2123
|
+
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');
|
|
2124
|
+
};
|
|
2125
|
+
this.presult = Promise.resolve(null);
|
|
2126
|
+
this.pnode_run_defined = false;
|
|
2127
|
+
this.websocket = await connect_websocket(this.url, {
|
|
2128
|
+
protocols: (() => {
|
|
2129
|
+
if (this.streaming)
|
|
2130
|
+
return 'streaming';
|
|
2131
|
+
if (this.python)
|
|
2132
|
+
return 'python';
|
|
2133
|
+
})(),
|
|
2134
|
+
on_message: (event) => {
|
|
2135
|
+
this.on_message(event);
|
|
2136
|
+
}
|
|
2137
|
+
});
|
|
2138
|
+
if (this.streaming)
|
|
2139
|
+
await this.subscribe();
|
|
2140
|
+
else {
|
|
2141
|
+
await this.rpc('connect', {});
|
|
2142
|
+
if (this.autologin)
|
|
2143
|
+
await this.call('login', [this.username, this.password], { urgent: true });
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
finally {
|
|
2148
|
+
resolve();
|
|
2149
|
+
}
|
|
2040
2150
|
}
|
|
2041
2151
|
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, } = {}) {
|
|
2042
2152
|
let flag = 0;
|
|
@@ -2072,12 +2182,9 @@ export class DDB {
|
|
|
2072
2182
|
disconnect() {
|
|
2073
2183
|
if (this.connected)
|
|
2074
2184
|
this.websocket.close(1000);
|
|
2075
|
-
this.on_message = () => { };
|
|
2076
|
-
this.presult = Promise.resolve(null);
|
|
2077
|
-
this.pconnect = Promise.resolve();
|
|
2078
|
-
this.pnode_run_defined = false;
|
|
2079
2185
|
}
|
|
2080
2186
|
/** rpc through websocket (function/script/variable command)
|
|
2187
|
+
When the DDB is not connected, the call will be automatically connected. When the connection is disconnected, the call will throw the Connectionerror
|
|
2081
2188
|
- type: API 类型: 'script' | 'function' | 'variable'
|
|
2082
2189
|
- options:
|
|
2083
2190
|
- urgent?: 决定 `行为标识` 那一行字符串的取值(只适用于 script 和 function)
|
|
@@ -2087,24 +2194,12 @@ export class DDB {
|
|
|
2087
2194
|
为 false 时返回的 DdbObj 仅含有 buffer 和 le,不做解析,以便后续转发、序列化
|
|
2088
2195
|
*/
|
|
2089
2196
|
async rpc(type, { script, func, args = [], vars = [], urgent, listener, parse_object, }) {
|
|
2090
|
-
if (!this.websocket)
|
|
2091
|
-
|
|
2092
|
-
let resolve;
|
|
2093
|
-
this.pconnect = new Promise((_resolve, _reject) => {
|
|
2094
|
-
resolve = _resolve;
|
|
2095
|
-
});
|
|
2096
|
-
await ptail;
|
|
2097
|
-
try {
|
|
2098
|
-
if (!this.websocket)
|
|
2099
|
-
await this.connect();
|
|
2100
|
-
}
|
|
2101
|
-
finally {
|
|
2102
|
-
resolve();
|
|
2103
|
-
}
|
|
2104
|
-
}
|
|
2197
|
+
if (!this.websocket)
|
|
2198
|
+
await this.connect();
|
|
2105
2199
|
if (!this.connected)
|
|
2106
|
-
throw new
|
|
2200
|
+
throw new ConnectionError(this);
|
|
2107
2201
|
if (func === 'pnode_run' && !this.pnode_run_defined) {
|
|
2202
|
+
// 保证并发调用 rpc 时只定义一次 pnode_run
|
|
2108
2203
|
const ptail = this.ppnoderun;
|
|
2109
2204
|
let resolve;
|
|
2110
2205
|
this.ppnoderun = new Promise((_resolve, _reject) => {
|
|
@@ -2254,8 +2349,7 @@ export class DDB {
|
|
|
2254
2349
|
*/
|
|
2255
2350
|
async call(func, args = [], { urgent, node, nodes, func_type, add_node_alias, listener, parse_object, } = {}) {
|
|
2256
2351
|
if (node) {
|
|
2257
|
-
|
|
2258
|
-
throw new Error('指定 node 时必须设置 func_type');
|
|
2352
|
+
assert(func_type in DdbFunctionType, 'func_type must be set when specifying node');
|
|
2259
2353
|
args = [
|
|
2260
2354
|
node,
|
|
2261
2355
|
new DdbFunction(func, func_type),
|
|
@@ -2292,10 +2386,21 @@ export class DDB {
|
|
|
2292
2386
|
vars,
|
|
2293
2387
|
/** Uploaded variables' value */
|
|
2294
2388
|
args, { listener, parse_object, } = {}) {
|
|
2295
|
-
|
|
2296
|
-
throw new Error('variable 指令参数为空或参数名为空,或数量不匹配');
|
|
2389
|
+
assert(args.length && args.length === vars.length, 'variable command parameter cannot be empty and parameter name cannot be empty, and the number should match');
|
|
2297
2390
|
return this.rpc('variable', { vars, args, listener, parse_object });
|
|
2298
2391
|
}
|
|
2392
|
+
/** Cancel all console jobs corresponding to the current session id */
|
|
2393
|
+
async cancel() {
|
|
2394
|
+
let ddb = new DDB(this.url, this);
|
|
2395
|
+
try {
|
|
2396
|
+
await ddb.eval(`jobs = exec rootJobId from getConsoleJobs() where sessionId = ${this.sid}\n` +
|
|
2397
|
+
'if (size(jobs))\n' +
|
|
2398
|
+
' cancelConsoleJob(jobs)\n', { urgent: true });
|
|
2399
|
+
}
|
|
2400
|
+
finally {
|
|
2401
|
+
ddb.disconnect();
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2299
2404
|
/** 解析服务端响应报文,返回去掉 header 的 data buf */
|
|
2300
2405
|
parse_message(buf, parse_object = this.parse_object) {
|
|
2301
2406
|
// MSG\n
|
|
@@ -2310,8 +2415,8 @@ export class DDB {
|
|
|
2310
2415
|
// 'OK\n'
|
|
2311
2416
|
// '\x04\x00\x02\x00\x00\x00'
|
|
2312
2417
|
/** index of line feed 0 */
|
|
2313
|
-
const
|
|
2314
|
-
const parts = this.dec.decode(buf.subarray(0,
|
|
2418
|
+
const ilf0 = buf.indexOf(0x0a); // '\n'
|
|
2419
|
+
const parts = this.dec.decode(buf.subarray(0, ilf0)).split(' ');
|
|
2315
2420
|
/** session id */
|
|
2316
2421
|
const sid = parts[0];
|
|
2317
2422
|
if (sid !== this.sid) {
|
|
@@ -2319,35 +2424,142 @@ export class DDB {
|
|
|
2319
2424
|
this.sid = sid;
|
|
2320
2425
|
}
|
|
2321
2426
|
/** 返回对象的数量 */
|
|
2322
|
-
const
|
|
2427
|
+
const nobj = Number(parts[1]);
|
|
2323
2428
|
/** 大小端: 协议中大端为 0, 小端为 1 */
|
|
2324
2429
|
this.le = Number(parts[2]) !== 0;
|
|
2325
|
-
const
|
|
2326
|
-
const
|
|
2430
|
+
const ils1 = ilf0 + 1;
|
|
2431
|
+
const ilf1 = buf.indexOf(0x0a, ils1); // '\n'
|
|
2327
2432
|
/** 'OK' 表示成功,其它文本表示失败 */
|
|
2328
|
-
const message = this.dec.decode(buf.subarray(
|
|
2433
|
+
const message = this.dec.decode(buf.subarray(ils1, ilf1));
|
|
2329
2434
|
if (message !== 'OK')
|
|
2330
2435
|
return {
|
|
2331
2436
|
type: 'error',
|
|
2332
2437
|
data: new Error(message)
|
|
2333
2438
|
};
|
|
2334
|
-
const
|
|
2439
|
+
const bufobj = buf.subarray(ilf1 + 1);
|
|
2335
2440
|
if (this.print_object_buffer)
|
|
2336
|
-
console.log(
|
|
2441
|
+
console.log(bufobj);
|
|
2337
2442
|
return {
|
|
2338
2443
|
type: 'object',
|
|
2339
2444
|
data: parse_object ?
|
|
2340
|
-
DdbObj.parse(
|
|
2445
|
+
DdbObj.parse(bufobj, this.le)
|
|
2341
2446
|
:
|
|
2342
2447
|
new DdbObj({
|
|
2343
2448
|
form: DdbForm.scalar,
|
|
2344
2449
|
type: DdbType.void,
|
|
2345
2450
|
length: 0,
|
|
2346
2451
|
le: this.le,
|
|
2347
|
-
buffer:
|
|
2452
|
+
buffer: bufobj,
|
|
2348
2453
|
})
|
|
2349
2454
|
};
|
|
2350
2455
|
}
|
|
2456
|
+
/** Internal stream subscription method */
|
|
2457
|
+
async subscribe() {
|
|
2458
|
+
const args = DdbObj.to_ddbobjs([
|
|
2459
|
+
new DdbVectorString(this.autologin ?
|
|
2460
|
+
[this.username, this.password]
|
|
2461
|
+
:
|
|
2462
|
+
// 不自动登录也要传空的 string vector, 否则 server 无法解析
|
|
2463
|
+
[]),
|
|
2464
|
+
// todo: 还有用吗?需要获取客户端 IP 吗
|
|
2465
|
+
'localhost',
|
|
2466
|
+
// todo: 端口还有用吗?
|
|
2467
|
+
new DdbInt(0),
|
|
2468
|
+
this.streaming.table,
|
|
2469
|
+
(this.streaming.action ||= `api_js_${new Date().getTime()}`),
|
|
2470
|
+
// new DdbInt(-1), // offset
|
|
2471
|
+
// filter
|
|
2472
|
+
// allow exists
|
|
2473
|
+
]);
|
|
2474
|
+
return new Promise((resolve, reject) => {
|
|
2475
|
+
let first = true;
|
|
2476
|
+
let second = true;
|
|
2477
|
+
// 先准备好收到 websocket message 的 callback
|
|
2478
|
+
this.on_message = ({ data: buffer }) => {
|
|
2479
|
+
if (first) {
|
|
2480
|
+
first = false;
|
|
2481
|
+
try {
|
|
2482
|
+
const dv = new DataView(buffer);
|
|
2483
|
+
const status = dv.getUint8(1);
|
|
2484
|
+
this.le = Boolean(dv.getUint8(2));
|
|
2485
|
+
const nobjs = dv.getUint32(3, this.le);
|
|
2486
|
+
// 解析返回数据,从 server 实现看其实只有一个 DdbObj
|
|
2487
|
+
let offset = 7;
|
|
2488
|
+
let objs = new Array(nobjs);
|
|
2489
|
+
const buf = new Uint8Array(buffer);
|
|
2490
|
+
for (let i = 0; i < nobjs; i++) {
|
|
2491
|
+
const obj = DdbObj.parse(buf.subarray(offset), this.le);
|
|
2492
|
+
offset += obj.length;
|
|
2493
|
+
objs[i] = obj;
|
|
2494
|
+
console.log(`订阅流表的响应数据: objs[${i}]: ${obj.toString()}`);
|
|
2495
|
+
}
|
|
2496
|
+
this.streaming.colnames = objs[0].value;
|
|
2497
|
+
if (status === StreamingStatusCode.ok)
|
|
2498
|
+
console.log('Subscribed to streaming table:', { status: StreamingStatusCode[status], result: objs[0] });
|
|
2499
|
+
else
|
|
2500
|
+
reject(new Error(`Failed to subscribe to streaming table: { status: ${StreamingStatusCode[status]}, error: '${objs[0]?.value}' }`));
|
|
2501
|
+
}
|
|
2502
|
+
catch (error) {
|
|
2503
|
+
reject(error);
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
else {
|
|
2507
|
+
try {
|
|
2508
|
+
const dv = new DataView(buffer);
|
|
2509
|
+
const buf = new Uint8Array(buffer);
|
|
2510
|
+
let { streaming } = this;
|
|
2511
|
+
streaming.time = dv.getBigInt64(1, this.le);
|
|
2512
|
+
streaming.id = dv.getBigInt64(9, this.le);
|
|
2513
|
+
const i_topic_end = buf.indexOf(0, 17);
|
|
2514
|
+
streaming.topic = this.dec.decode(buf.subarray(17, i_topic_end));
|
|
2515
|
+
if (second) {
|
|
2516
|
+
second = false;
|
|
2517
|
+
// 是 table
|
|
2518
|
+
streaming.schema = DdbObj.parse(buf.subarray(i_topic_end + 1), this.le);
|
|
2519
|
+
console.log('Received streaming table schema:', streaming.schema);
|
|
2520
|
+
streaming.window = {
|
|
2521
|
+
offset: 0,
|
|
2522
|
+
rows: 0,
|
|
2523
|
+
segments: [],
|
|
2524
|
+
};
|
|
2525
|
+
assert(streaming.schema.rows === 0, 'schema.rows === 0');
|
|
2526
|
+
resolve(streaming.schema);
|
|
2527
|
+
}
|
|
2528
|
+
else {
|
|
2529
|
+
// 是 column 片段组成的 any vector
|
|
2530
|
+
const data = streaming.data = DdbObj.parse(buf.subarray(i_topic_end + 1), this.le);
|
|
2531
|
+
let { window: win } = streaming;
|
|
2532
|
+
win.rows += data.value[0].rows;
|
|
2533
|
+
win.segments.push(data);
|
|
2534
|
+
if (win.rows >= winsize * 2 && win.segments.length >= 2) {
|
|
2535
|
+
let winsize_ = 0;
|
|
2536
|
+
let i = win.segments.length - 1;
|
|
2537
|
+
// 往前移动至首个累计 winsize_ 超过 winsize 的位置
|
|
2538
|
+
for (; winsize_ < winsize; i--)
|
|
2539
|
+
winsize_ += win.segments[i].rows;
|
|
2540
|
+
win.segments = win.segments.slice(i);
|
|
2541
|
+
win.offset += win.rows - winsize_;
|
|
2542
|
+
win.rows = winsize_;
|
|
2543
|
+
console.log('exceeds winsize, shrink window.segments', win);
|
|
2544
|
+
}
|
|
2545
|
+
streaming.handler(streaming);
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
catch (error) {
|
|
2549
|
+
reject(error);
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
};
|
|
2553
|
+
// 发送订阅 websocket message
|
|
2554
|
+
this.websocket.send(concat([
|
|
2555
|
+
Uint8Array.of(1, // from = SubscriberFromType::FROM_API
|
|
2556
|
+
1, // type = SubscriberRPCType::RPC_START
|
|
2557
|
+
Number(DDB.le_client)),
|
|
2558
|
+
Uint32Array.of(args.length),
|
|
2559
|
+
...args.map(obj => obj.pack())
|
|
2560
|
+
]));
|
|
2561
|
+
});
|
|
2562
|
+
}
|
|
2351
2563
|
}
|
|
2352
2564
|
export let ddb = window.ddb = new DDB();
|
|
2353
2565
|
//# sourceMappingURL=browser.js.map
|