dolphindb 2.0.603 → 2.0.800
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 +98 -17
- package/browser.js +333 -154
- package/browser.js.map +1 -1
- package/docs.en.json +2096 -646
- package/docs.zh.json +1290 -729
- package/index.d.ts +103 -18
- package/index.js +350 -166
- 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
|
@@ -5,7 +5,7 @@ import ipaddrjs from 'ipaddr.js';
|
|
|
5
5
|
const { fromByteArray: buf2ipaddr } = ipaddrjs;
|
|
6
6
|
import 'xshell/prototype.browser.js';
|
|
7
7
|
import { blue } from 'xshell/chalk.browser.js';
|
|
8
|
-
import { concat } from 'xshell/utils.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) {
|
|
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,31 @@ export class DdbObj {
|
|
|
1138
1164
|
return values.map(value => this.to_ddbobj(value));
|
|
1139
1165
|
}
|
|
1140
1166
|
to_cols() {
|
|
1167
|
+
assert(this.form === DdbForm.table, 'form must be DdbForm.table, otherwise it cannot to_cols');
|
|
1141
1168
|
return this.value.map(col => {
|
|
1142
1169
|
let col_ = {
|
|
1143
1170
|
title: col.name,
|
|
1144
1171
|
dataIndex: col.name,
|
|
1145
1172
|
};
|
|
1146
1173
|
switch (col.type) {
|
|
1174
|
+
case DdbType.char:
|
|
1175
|
+
col_.render = (value) => format(DdbType.char, value, this.le);
|
|
1176
|
+
break;
|
|
1147
1177
|
case DdbType.timestamp:
|
|
1148
|
-
col_.render = value => timestamp2str(value);
|
|
1178
|
+
col_.render = (value) => timestamp2str(value);
|
|
1149
1179
|
break;
|
|
1150
1180
|
case DdbType.date:
|
|
1151
|
-
col_.render = value => date2str(value);
|
|
1181
|
+
col_.render = (value) => date2str(value);
|
|
1152
1182
|
break;
|
|
1153
1183
|
case DdbType.ipaddr:
|
|
1154
|
-
col_.render = value => ipaddr2str(value, this.le);
|
|
1184
|
+
col_.render = (value) => ipaddr2str(value, this.le);
|
|
1155
1185
|
break;
|
|
1156
1186
|
}
|
|
1157
1187
|
return col_;
|
|
1158
1188
|
});
|
|
1159
1189
|
}
|
|
1160
1190
|
to_rows() {
|
|
1161
|
-
|
|
1162
|
-
throw new Error('this.form is not DdbForm.table, cannot call to_rows');
|
|
1191
|
+
assert(this.form === DdbForm.table, 'form must be DdbForm.table, otherwise it cannot to_rows');
|
|
1163
1192
|
let rows = new Array(this.rows);
|
|
1164
1193
|
for (let i = 0; i < this.rows; i++) {
|
|
1165
1194
|
let row = {};
|
|
@@ -1191,13 +1220,10 @@ export class DdbObj {
|
|
|
1191
1220
|
return rows;
|
|
1192
1221
|
}
|
|
1193
1222
|
to_dict({ strip, deep, } = {}) {
|
|
1194
|
-
|
|
1195
|
-
throw new Error('this.form is not DdbForm.dict and cannot be converted to js object');
|
|
1223
|
+
assert(this.form === DdbForm.dict, 'this.form must be DdbForm.dict, otherwise to_dict cannot be called to convert to js object');
|
|
1196
1224
|
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');
|
|
1225
|
+
assert(key_type === DdbType.string && value_type === DdbType.any, 'Currently only supports automatic conversion of dict<string, any> to js object');
|
|
1226
|
+
assert(!(deep && !strip), 'strip = true must be set when deep = true');
|
|
1201
1227
|
let obj = {};
|
|
1202
1228
|
for (let i = 0; i < this.rows; i++) {
|
|
1203
1229
|
let value = values[i];
|
|
@@ -1209,8 +1235,23 @@ export class DdbObj {
|
|
|
1209
1235
|
return obj;
|
|
1210
1236
|
}
|
|
1211
1237
|
}
|
|
1238
|
+
/** Integer must use this number formatter, InspectOptions.decimals also use this if not passed */
|
|
1239
|
+
let default_formatter = Intl.NumberFormat('en-US', { maximumFractionDigits: 20 });
|
|
1240
|
+
let _decimals = 20;
|
|
1241
|
+
/** Cache, in order to optimize performance, usually options.decimals are unchanged */
|
|
1242
|
+
let _formatter = Intl.NumberFormat('en-US', { maximumFractionDigits: 20 });
|
|
1212
1243
|
/** Formats a single element (value) as a string according to DdbType, null returns a 'null' string */
|
|
1213
1244
|
export function format(type, value, le, options = {}) {
|
|
1245
|
+
const formatter = (() => {
|
|
1246
|
+
const { decimals } = options;
|
|
1247
|
+
if (decimals === undefined || decimals === null)
|
|
1248
|
+
return default_formatter;
|
|
1249
|
+
if (decimals !== _decimals) {
|
|
1250
|
+
_decimals = decimals;
|
|
1251
|
+
_formatter = Intl.NumberFormat('en-US', { maximumFractionDigits: decimals, minimumFractionDigits: decimals });
|
|
1252
|
+
}
|
|
1253
|
+
return _formatter;
|
|
1254
|
+
})();
|
|
1214
1255
|
switch (type) {
|
|
1215
1256
|
case DdbType.bool:
|
|
1216
1257
|
return String((value === null || value === nulls.int8) ?
|
|
@@ -1228,20 +1269,20 @@ export function format(type, value, le, options = {}) {
|
|
|
1228
1269
|
:
|
|
1229
1270
|
value);
|
|
1230
1271
|
case DdbType.short:
|
|
1231
|
-
return
|
|
1232
|
-
null
|
|
1272
|
+
return (value === null || value === nulls.int16) ?
|
|
1273
|
+
'null'
|
|
1233
1274
|
:
|
|
1234
|
-
value);
|
|
1275
|
+
default_formatter.format(value);
|
|
1235
1276
|
case DdbType.int:
|
|
1236
|
-
return
|
|
1237
|
-
null
|
|
1277
|
+
return (value === null || value === nulls.int32) ?
|
|
1278
|
+
'null'
|
|
1238
1279
|
:
|
|
1239
|
-
value);
|
|
1280
|
+
default_formatter.format(value);
|
|
1240
1281
|
case DdbType.long:
|
|
1241
1282
|
return (value === null || value === nulls.int64) ?
|
|
1242
1283
|
'null'
|
|
1243
1284
|
:
|
|
1244
|
-
|
|
1285
|
+
default_formatter.format(value);
|
|
1245
1286
|
case DdbType.date:
|
|
1246
1287
|
return (value === null || value === nulls.int32) ?
|
|
1247
1288
|
'null'
|
|
@@ -1291,12 +1332,12 @@ export function format(type, value, le, options = {}) {
|
|
|
1291
1332
|
return (value === null || value === nulls.float32) ?
|
|
1292
1333
|
'null'
|
|
1293
1334
|
:
|
|
1294
|
-
|
|
1335
|
+
formatter.format(value);
|
|
1295
1336
|
case DdbType.double:
|
|
1296
1337
|
return (value === null || value === nulls.double) ?
|
|
1297
1338
|
'null'
|
|
1298
1339
|
:
|
|
1299
|
-
|
|
1340
|
+
formatter.format(value);
|
|
1300
1341
|
case DdbType.symbol:
|
|
1301
1342
|
case DdbType.string:
|
|
1302
1343
|
return value.quote('single');
|
|
@@ -1323,30 +1364,32 @@ export function format(type, value, le, options = {}) {
|
|
|
1323
1364
|
:
|
|
1324
1365
|
DdbObj.dec.decode(value)).quote('single');
|
|
1325
1366
|
case DdbType.point: {
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
x = null;
|
|
1329
|
-
if (y === nulls.double)
|
|
1330
|
-
y = null;
|
|
1331
|
-
return `(${String(x)}, ${String(y)})`;
|
|
1367
|
+
const [x, y] = value;
|
|
1368
|
+
return `(${format(DdbType.double, x, le, options)}, ${format(DdbType.double, y, le, options)})`;
|
|
1332
1369
|
}
|
|
1333
1370
|
case DdbType.complex: {
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
x = null;
|
|
1337
|
-
if (y === nulls.double)
|
|
1338
|
-
y = null;
|
|
1339
|
-
return `${String(x)}+${String(y)}i`;
|
|
1371
|
+
const [x, y] = value;
|
|
1372
|
+
return `${format(DdbType.double, x, le, options)}+${format(DdbType.double, y, le, options)}i`;
|
|
1340
1373
|
}
|
|
1341
1374
|
case DdbType.duration: {
|
|
1342
1375
|
const { data, unit } = value;
|
|
1343
1376
|
return `${data}${DdbDurationUnit[unit]}`;
|
|
1344
1377
|
}
|
|
1378
|
+
case DdbType.decimal32:
|
|
1379
|
+
case DdbType.decimal64: {
|
|
1380
|
+
const { scale, data } = value;
|
|
1381
|
+
if (data === null ||
|
|
1382
|
+
(data === nulls.int64 && type === DdbType.decimal64) ||
|
|
1383
|
+
(data === nulls.int32 && type === DdbType.decimal32))
|
|
1384
|
+
return 'null';
|
|
1385
|
+
const s = String(data < 0 ? -data : data).padStart(scale, '0');
|
|
1386
|
+
return (data < 0 ? '-' : '') + (scale ? `${s.slice(0, -scale) || '0'}.${s.slice(-scale)}` : s);
|
|
1387
|
+
}
|
|
1345
1388
|
default:
|
|
1346
1389
|
return String(value);
|
|
1347
1390
|
}
|
|
1348
1391
|
}
|
|
1349
|
-
/**
|
|
1392
|
+
/** formatted vector, the index-th item in the collection is a string, a null value returns a 'null' string */
|
|
1350
1393
|
export function formati(obj, index, options = {}) {
|
|
1351
1394
|
if (64 <= obj.type && obj.type < 128) { // array vector
|
|
1352
1395
|
// 因为 array vector 目前只支持:Logical, Integral(不包括 INT128, COMPRESS 类型), Floating, Temporal
|
|
@@ -1402,6 +1445,16 @@ export function formati(obj, index, options = {}) {
|
|
|
1402
1445
|
case DdbType.complex:
|
|
1403
1446
|
case DdbType.point:
|
|
1404
1447
|
return format(obj.type, obj.value.subarray(2 * index, 2 * (index + 1)), obj.le, options);
|
|
1448
|
+
case DdbType.decimal32:
|
|
1449
|
+
case DdbType.decimal64: {
|
|
1450
|
+
const { scale, data } = obj.value;
|
|
1451
|
+
const x = data[index];
|
|
1452
|
+
if (x === nulls.int64 && obj.type === DdbType.decimal64 ||
|
|
1453
|
+
x === nulls.int32 && obj.type === DdbType.decimal32)
|
|
1454
|
+
return '';
|
|
1455
|
+
const s = String(x < 0 ? -x : x).padStart(scale, '0');
|
|
1456
|
+
return (x < 0 ? '-' : '') + (scale ? `${s.slice(0, -scale) || '0'}.${s.slice(-scale)}` : s);
|
|
1457
|
+
}
|
|
1405
1458
|
default:
|
|
1406
1459
|
return format(obj.type, obj.value[index], obj.le, options);
|
|
1407
1460
|
}
|
|
@@ -1816,8 +1869,7 @@ export function datehour2str(datehour, format = 'YYYY.MM.DDTHH') {
|
|
|
1816
1869
|
export function str2timestamp(str, format = 'YYYY.MM.DD HH:mm:ss.SSS') {
|
|
1817
1870
|
if (!str || str === 'null')
|
|
1818
1871
|
return nulls.int64;
|
|
1819
|
-
|
|
1820
|
-
throw new Error('The length of the timestamp string is not equal to the length of the format string');
|
|
1872
|
+
assert('The length of the timestamp string must be equal to the length of the format string');
|
|
1821
1873
|
return BigInt(-timezone_offset +
|
|
1822
1874
|
dayjs(str, format).valueOf());
|
|
1823
1875
|
}
|
|
@@ -1830,12 +1882,10 @@ export function nanotime2str(nanotime, format = 'HH:mm:ss.SSSSSSSSS') {
|
|
|
1830
1882
|
if (nanotime < 0n)
|
|
1831
1883
|
return String(nanotime);
|
|
1832
1884
|
const i_second_start = format.indexOf('ss');
|
|
1833
|
-
|
|
1834
|
-
throw new Error('格式串必须包含秒的格式 (ss)');
|
|
1885
|
+
assert(i_second_start !== -1, 'The format string must contain the format for seconds (ss)');
|
|
1835
1886
|
const i_second_end = i_second_start + 2;
|
|
1836
1887
|
const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
|
|
1837
|
-
|
|
1838
|
-
throw new Error('格式串必须包含纳秒的格式 (SSSSSSSSS)');
|
|
1888
|
+
assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
|
|
1839
1889
|
return (dayjs(timezone_offset + (Number(nanotime) / 1000000)).format(format.slice(0, i_second_end)) +
|
|
1840
1890
|
format.slice(i_second_end, i_nanosecond_start) +
|
|
1841
1891
|
String(nanotime % 1000000000n).padStart(9, '0'));
|
|
@@ -1863,12 +1913,10 @@ export function nanotimestamp2str(nanotimestamp, format = 'YYYY.MM.DD HH:mm:ss.S
|
|
|
1863
1913
|
if (nanotimestamp === null || nanotimestamp === nulls.int64)
|
|
1864
1914
|
return 'null';
|
|
1865
1915
|
const i_second_start = format.indexOf('ss');
|
|
1866
|
-
|
|
1867
|
-
throw new Error('The format string must contain the format for seconds (ss)');
|
|
1916
|
+
assert(i_second_start !== -1, 'The format string must contain the format for seconds (ss)');
|
|
1868
1917
|
const i_second_end = i_second_start + 2;
|
|
1869
1918
|
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)');
|
|
1919
|
+
assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
|
|
1872
1920
|
const remainder = nanotimestamp % 1000000000n;
|
|
1873
1921
|
const borrow = remainder < 0n;
|
|
1874
1922
|
return (dayjs(timezone_offset +
|
|
@@ -1890,15 +1938,12 @@ export function nanotimestamp2str(nanotimestamp, format = 'YYYY.MM.DD HH:mm:ss.S
|
|
|
1890
1938
|
export function str2nanotimestamp(str, format = 'YYYY.MM.DD HH:mm:ss.SSSSSSSSS') {
|
|
1891
1939
|
if (!str || str === 'null')
|
|
1892
1940
|
return nulls.int64;
|
|
1893
|
-
|
|
1894
|
-
throw new Error('nanotimestamp string length is not equal to format string length');
|
|
1941
|
+
assert(str.length === format.length, 'nanotimestamp string length must be equal to format string length');
|
|
1895
1942
|
const i_second_start = format.indexOf('ss');
|
|
1896
|
-
|
|
1897
|
-
throw new Error('The format string must contain the format for seconds (ss)');
|
|
1943
|
+
assert(i_second_start !== -1, 'The format string must contain the format for seconds (ss)');
|
|
1898
1944
|
const i_second_end = i_second_start + 2;
|
|
1899
1945
|
const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
|
|
1900
|
-
|
|
1901
|
-
throw new Error('Format string must contain nanosecond format (SSSSSSSSS)');
|
|
1946
|
+
assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
|
|
1902
1947
|
return (BigInt(-timezone_offset +
|
|
1903
1948
|
dayjs(str.slice(0, i_second_end), format.slice(0, i_second_end)).valueOf()) * 1000000n
|
|
1904
1949
|
+
|
|
@@ -1926,6 +1971,7 @@ export function int1282str(buffer, le = true) {
|
|
|
1926
1971
|
return [...buf].map(x => x.toString(16)
|
|
1927
1972
|
.padStart(2, '0')).join('');
|
|
1928
1973
|
}
|
|
1974
|
+
export const winsize = 40;
|
|
1929
1975
|
export class DDB {
|
|
1930
1976
|
/** 当前的 session id (http 或 tcp) */
|
|
1931
1977
|
sid = '0';
|
|
@@ -1949,7 +1995,9 @@ export class DDB {
|
|
|
1949
1995
|
password = '123456';
|
|
1950
1996
|
/** python session flag (2048) */
|
|
1951
1997
|
python = false;
|
|
1952
|
-
|
|
1998
|
+
/** Whether it is a streaming data connection, this field is always null for non-streaming data */
|
|
1999
|
+
streaming = null;
|
|
2000
|
+
// --- 内部选项, 状态
|
|
1953
2001
|
print_message_buffer = false;
|
|
1954
2002
|
print_object_buffer = false;
|
|
1955
2003
|
print_message = true;
|
|
@@ -1972,6 +2020,7 @@ export class DDB {
|
|
|
1972
2020
|
- username?: DolphinDB username, default `'admin'`
|
|
1973
2021
|
- password?: DolphinDB password, default `'123456'`
|
|
1974
2022
|
- python?: set python session flag, default `false`
|
|
2023
|
+
- streaming?: When this option is set, the WebSocket connection is only used for streaming data
|
|
1975
2024
|
|
|
1976
2025
|
@example
|
|
1977
2026
|
let ddb = new DDB('ws://127.0.0.1:8848')
|
|
@@ -1989,7 +2038,7 @@ export class DDB {
|
|
|
1989
2038
|
const _url = new URL(location.href);
|
|
1990
2039
|
const hostname = _url.searchParams.get('hostname') || location.hostname;
|
|
1991
2040
|
const port = _url.searchParams.get('port') || location.port;
|
|
1992
|
-
url = `${location.protocol === 'https:' ? 'wss' : 'ws'}://${hostname}
|
|
2041
|
+
url = `${location.protocol === 'https:' ? 'wss' : 'ws'}://${hostname}:${port}/`;
|
|
1993
2042
|
}
|
|
1994
2043
|
this.url = url;
|
|
1995
2044
|
if (options.autologin !== undefined)
|
|
@@ -2000,8 +2049,13 @@ export class DDB {
|
|
|
2000
2049
|
this.password = options.password;
|
|
2001
2050
|
if (options.python !== undefined)
|
|
2002
2051
|
this.python = options.python;
|
|
2052
|
+
if (options.streaming !== undefined)
|
|
2053
|
+
this.streaming = options.streaming;
|
|
2054
|
+
}
|
|
2055
|
+
on_message(event) {
|
|
2056
|
+
console.log('This is the default on_message before calling this.rpc, it should not be called unless the server pushes the message first after the connection is established');
|
|
2057
|
+
console.log(event.data);
|
|
2003
2058
|
}
|
|
2004
|
-
on_message(event) { }
|
|
2005
2059
|
/** Establish the actual WebSocket connection to the DolphinDB corresponding to the URL
|
|
2006
2060
|
- options?:
|
|
2007
2061
|
- url?: DolphinDB WebSocket URL. By default, the WebSocket URL passed in when the instance is initialized is used
|
|
@@ -2009,34 +2063,42 @@ export class DDB {
|
|
|
2009
2063
|
- username?: DolphinDB username, default `'admin'`
|
|
2010
2064
|
- password?: DolphinDB password, default `'123456'`
|
|
2011
2065
|
- python?: set python session flag, default `false`
|
|
2066
|
+
- streaming?: When this option is set, the WebSocket connection is only used for streaming data
|
|
2012
2067
|
*/
|
|
2013
|
-
async connect(
|
|
2014
|
-
if (
|
|
2015
|
-
this.url =
|
|
2016
|
-
if (
|
|
2017
|
-
this.autologin =
|
|
2018
|
-
if (
|
|
2019
|
-
this.username =
|
|
2020
|
-
if (
|
|
2021
|
-
this.password =
|
|
2022
|
-
if (
|
|
2023
|
-
this.python =
|
|
2068
|
+
async connect({ url, autologin, username, password, python, streaming } = {}) {
|
|
2069
|
+
if (url !== undefined)
|
|
2070
|
+
this.url = url;
|
|
2071
|
+
if (autologin !== undefined)
|
|
2072
|
+
this.autologin = autologin;
|
|
2073
|
+
if (username !== undefined)
|
|
2074
|
+
this.username = username;
|
|
2075
|
+
if (password !== undefined)
|
|
2076
|
+
this.password = password;
|
|
2077
|
+
if (python !== undefined)
|
|
2078
|
+
this.python = python;
|
|
2079
|
+
if (streaming !== undefined)
|
|
2080
|
+
this.streaming = streaming;
|
|
2024
2081
|
this.disconnect();
|
|
2025
2082
|
await connect_websocket(this.url, {
|
|
2026
|
-
protocols:
|
|
2083
|
+
protocols: (() => {
|
|
2084
|
+
if (this.streaming)
|
|
2085
|
+
return 'streaming';
|
|
2086
|
+
if (this.python)
|
|
2087
|
+
return 'python';
|
|
2088
|
+
})(),
|
|
2027
2089
|
on_open: async (event, websocket) => {
|
|
2028
2090
|
this.websocket = websocket;
|
|
2029
|
-
|
|
2091
|
+
if (this.streaming)
|
|
2092
|
+
await this.subscribe();
|
|
2093
|
+
else
|
|
2094
|
+
await this.rpc('connect', {});
|
|
2030
2095
|
},
|
|
2031
2096
|
on_message: (event) => {
|
|
2032
2097
|
this.on_message(event);
|
|
2033
2098
|
}
|
|
2034
2099
|
});
|
|
2035
|
-
if (this.autologin)
|
|
2036
|
-
|
|
2037
|
-
await this.eval(`login(${this.username.quote('double')}, ${this.password.quote('double')})`, { urgent: true });
|
|
2038
|
-
else
|
|
2039
|
-
await this.call('login', [this.username, this.password], { urgent: true });
|
|
2100
|
+
if (!this.streaming && this.autologin)
|
|
2101
|
+
await this.call('login', [this.username, this.password], { urgent: true });
|
|
2040
2102
|
}
|
|
2041
2103
|
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
2104
|
let flag = 0;
|
|
@@ -2254,8 +2316,7 @@ export class DDB {
|
|
|
2254
2316
|
*/
|
|
2255
2317
|
async call(func, args = [], { urgent, node, nodes, func_type, add_node_alias, listener, parse_object, } = {}) {
|
|
2256
2318
|
if (node) {
|
|
2257
|
-
|
|
2258
|
-
throw new Error('指定 node 时必须设置 func_type');
|
|
2319
|
+
assert(func_type in DdbFunctionType, 'func_type must be set when specifying node');
|
|
2259
2320
|
args = [
|
|
2260
2321
|
node,
|
|
2261
2322
|
new DdbFunction(func, func_type),
|
|
@@ -2292,10 +2353,21 @@ export class DDB {
|
|
|
2292
2353
|
vars,
|
|
2293
2354
|
/** Uploaded variables' value */
|
|
2294
2355
|
args, { listener, parse_object, } = {}) {
|
|
2295
|
-
|
|
2296
|
-
throw new Error('variable 指令参数为空或参数名为空,或数量不匹配');
|
|
2356
|
+
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
2357
|
return this.rpc('variable', { vars, args, listener, parse_object });
|
|
2298
2358
|
}
|
|
2359
|
+
/** Cancel all console jobs corresponding to the current session id */
|
|
2360
|
+
async cancel() {
|
|
2361
|
+
let ddb = new DDB(this.url, this);
|
|
2362
|
+
try {
|
|
2363
|
+
await ddb.eval(`jobs = exec rootJobId from getConsoleJobs() where sessionId = ${this.sid}\n` +
|
|
2364
|
+
'if (size(jobs))\n' +
|
|
2365
|
+
' cancelConsoleJob(jobs)\n', { urgent: true });
|
|
2366
|
+
}
|
|
2367
|
+
finally {
|
|
2368
|
+
ddb.disconnect();
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
2299
2371
|
/** 解析服务端响应报文,返回去掉 header 的 data buf */
|
|
2300
2372
|
parse_message(buf, parse_object = this.parse_object) {
|
|
2301
2373
|
// MSG\n
|
|
@@ -2310,8 +2382,8 @@ export class DDB {
|
|
|
2310
2382
|
// 'OK\n'
|
|
2311
2383
|
// '\x04\x00\x02\x00\x00\x00'
|
|
2312
2384
|
/** index of line feed 0 */
|
|
2313
|
-
const
|
|
2314
|
-
const parts = this.dec.decode(buf.subarray(0,
|
|
2385
|
+
const ilf0 = buf.indexOf(0x0a); // '\n'
|
|
2386
|
+
const parts = this.dec.decode(buf.subarray(0, ilf0)).split(' ');
|
|
2315
2387
|
/** session id */
|
|
2316
2388
|
const sid = parts[0];
|
|
2317
2389
|
if (sid !== this.sid) {
|
|
@@ -2319,35 +2391,142 @@ export class DDB {
|
|
|
2319
2391
|
this.sid = sid;
|
|
2320
2392
|
}
|
|
2321
2393
|
/** 返回对象的数量 */
|
|
2322
|
-
const
|
|
2394
|
+
const nobj = Number(parts[1]);
|
|
2323
2395
|
/** 大小端: 协议中大端为 0, 小端为 1 */
|
|
2324
2396
|
this.le = Number(parts[2]) !== 0;
|
|
2325
|
-
const
|
|
2326
|
-
const
|
|
2397
|
+
const ils1 = ilf0 + 1;
|
|
2398
|
+
const ilf1 = buf.indexOf(0x0a, ils1); // '\n'
|
|
2327
2399
|
/** 'OK' 表示成功,其它文本表示失败 */
|
|
2328
|
-
const message = this.dec.decode(buf.subarray(
|
|
2400
|
+
const message = this.dec.decode(buf.subarray(ils1, ilf1));
|
|
2329
2401
|
if (message !== 'OK')
|
|
2330
2402
|
return {
|
|
2331
2403
|
type: 'error',
|
|
2332
2404
|
data: new Error(message)
|
|
2333
2405
|
};
|
|
2334
|
-
const
|
|
2406
|
+
const bufobj = buf.subarray(ilf1 + 1);
|
|
2335
2407
|
if (this.print_object_buffer)
|
|
2336
|
-
console.log(
|
|
2408
|
+
console.log(bufobj);
|
|
2337
2409
|
return {
|
|
2338
2410
|
type: 'object',
|
|
2339
2411
|
data: parse_object ?
|
|
2340
|
-
DdbObj.parse(
|
|
2412
|
+
DdbObj.parse(bufobj, this.le)
|
|
2341
2413
|
:
|
|
2342
2414
|
new DdbObj({
|
|
2343
2415
|
form: DdbForm.scalar,
|
|
2344
2416
|
type: DdbType.void,
|
|
2345
2417
|
length: 0,
|
|
2346
2418
|
le: this.le,
|
|
2347
|
-
buffer:
|
|
2419
|
+
buffer: bufobj,
|
|
2348
2420
|
})
|
|
2349
2421
|
};
|
|
2350
2422
|
}
|
|
2423
|
+
/** Internal stream subscription method */
|
|
2424
|
+
async subscribe() {
|
|
2425
|
+
const args = DdbObj.to_ddbobjs([
|
|
2426
|
+
new DdbVectorString(this.autologin ?
|
|
2427
|
+
[this.username, this.password]
|
|
2428
|
+
:
|
|
2429
|
+
// 不自动登录也要传空的 string vector, 否则 server 无法解析
|
|
2430
|
+
[]),
|
|
2431
|
+
// todo: 还有用吗?需要获取客户端 IP 吗
|
|
2432
|
+
'localhost',
|
|
2433
|
+
// todo: 端口还有用吗?
|
|
2434
|
+
new DdbInt(0),
|
|
2435
|
+
this.streaming.table,
|
|
2436
|
+
(this.streaming.action ||= `api_js_${new Date().getTime()}`),
|
|
2437
|
+
// new DdbInt(-1), // offset
|
|
2438
|
+
// filter
|
|
2439
|
+
// allow exists
|
|
2440
|
+
]);
|
|
2441
|
+
return new Promise((resolve, reject) => {
|
|
2442
|
+
let first = true;
|
|
2443
|
+
let second = true;
|
|
2444
|
+
// 先准备好收到 websocket message 的 callback
|
|
2445
|
+
this.on_message = ({ data: buffer }) => {
|
|
2446
|
+
if (first) {
|
|
2447
|
+
first = false;
|
|
2448
|
+
try {
|
|
2449
|
+
const dv = new DataView(buffer);
|
|
2450
|
+
const status = dv.getUint8(1);
|
|
2451
|
+
this.le = Boolean(dv.getUint8(2));
|
|
2452
|
+
const nobjs = dv.getUint32(3, this.le);
|
|
2453
|
+
// 解析返回数据,从 server 实现看其实只有一个 DdbObj
|
|
2454
|
+
let offset = 7;
|
|
2455
|
+
let objs = new Array(nobjs);
|
|
2456
|
+
const buf = new Uint8Array(buffer);
|
|
2457
|
+
for (let i = 0; i < nobjs; i++) {
|
|
2458
|
+
const obj = DdbObj.parse(buf.subarray(offset), this.le);
|
|
2459
|
+
offset += obj.length;
|
|
2460
|
+
objs[i] = obj;
|
|
2461
|
+
console.log(`订阅流表的响应数据: objs[${i}]: ${obj.toString()}`);
|
|
2462
|
+
}
|
|
2463
|
+
this.streaming.colnames = objs[0].value;
|
|
2464
|
+
if (status === StreamingStatusCode.ok)
|
|
2465
|
+
console.log('Subscribed to streaming table:', { status: StreamingStatusCode[status], result: objs[0] });
|
|
2466
|
+
else
|
|
2467
|
+
reject(new Error(`Failed to subscribe to streaming table: { status: ${StreamingStatusCode[status]}, error: ${objs[0]?.value} }`));
|
|
2468
|
+
}
|
|
2469
|
+
catch (error) {
|
|
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
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
};
|
|
2520
|
+
// 发送订阅 websocket message
|
|
2521
|
+
this.websocket.send(concat([
|
|
2522
|
+
Uint8Array.of(1, // from = SubscriberFromType::FROM_API
|
|
2523
|
+
1, // type = SubscriberRPCType::RPC_START
|
|
2524
|
+
Number(DDB.le_client)),
|
|
2525
|
+
Uint32Array.of(args.length),
|
|
2526
|
+
...args.map(obj => obj.pack())
|
|
2527
|
+
]));
|
|
2528
|
+
});
|
|
2529
|
+
}
|
|
2351
2530
|
}
|
|
2352
2531
|
export let ddb = window.ddb = new DDB();
|
|
2353
2532
|
//# sourceMappingURL=browser.js.map
|