dolphindb 2.0.602 → 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/README.md +1 -0
- package/browser.d.ts +109 -17
- package/browser.js +389 -183
- package/browser.js.map +1 -1
- package/docs.en.json +2046 -552
- package/docs.zh.json +1311 -737
- package/index.d.ts +114 -18
- package/index.js +406 -196
- 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');
|
|
@@ -268,7 +280,7 @@ export class DdbObj {
|
|
|
268
280
|
if (form === DdbForm.dict)
|
|
269
281
|
return dict;
|
|
270
282
|
else {
|
|
271
|
-
const { chartType: type, stacking, title: titles, extras, data, ...others } = dict.to_dict();
|
|
283
|
+
const { chartType: type, stacking, binStart: bin_start, binEnd: bin_end, binCount: bin_count, title: titles, extras, data, ...others } = dict.to_dict();
|
|
272
284
|
const [chart, x_axis, y_axis] = titles.value;
|
|
273
285
|
dict.form = DdbForm.chart;
|
|
274
286
|
dict.value = {
|
|
@@ -279,6 +291,8 @@ export class DdbObj {
|
|
|
279
291
|
x_axis,
|
|
280
292
|
y_axis,
|
|
281
293
|
},
|
|
294
|
+
...bin_start ? { bin_start, bin_end, } : {},
|
|
295
|
+
...bin_count ? { bin_count } : {},
|
|
282
296
|
...extras ? (() => {
|
|
283
297
|
const { multiYAxes: multi_y_axes = false, ...extras_others } = extras.to_dict({ strip: true });
|
|
284
298
|
return {
|
|
@@ -330,8 +344,7 @@ export class DdbObj {
|
|
|
330
344
|
col_labels.length += 2;
|
|
331
345
|
offset += col_labels.length;
|
|
332
346
|
}
|
|
333
|
-
|
|
334
|
-
throw new Error('matrix.datatype !== matrix.type');
|
|
347
|
+
assert(buf_data[offset] === type, 'matrix.datatype === matrix.type');
|
|
335
348
|
const rows = dv.getUint32(offset + 2, le);
|
|
336
349
|
const cols = dv.getUint32(offset + 6, le);
|
|
337
350
|
const [len_items, data] = this.parse_vector_items(buf_data.subarray(offset + 10), le, type, rows * cols // 假设小于 2**32
|
|
@@ -442,38 +455,30 @@ export class DdbObj {
|
|
|
442
455
|
case DdbType.uuid:
|
|
443
456
|
case DdbType.ipaddr:
|
|
444
457
|
case DdbType.int128:
|
|
445
|
-
return [
|
|
446
|
-
16,
|
|
447
|
-
buf.slice(0, 16)
|
|
448
|
-
];
|
|
458
|
+
return [16, buf.slice(0, 16)];
|
|
449
459
|
case DdbType.blob: {
|
|
450
460
|
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
451
461
|
const length = dv.getUint32(0, le);
|
|
452
|
-
return [
|
|
453
|
-
4 + length,
|
|
454
|
-
buf.slice(4, 4 + length)
|
|
455
|
-
];
|
|
462
|
+
return [4 + length, buf.slice(4, 4 + length)];
|
|
456
463
|
}
|
|
457
464
|
case DdbType.complex:
|
|
458
465
|
case DdbType.point: {
|
|
459
466
|
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
460
|
-
return [
|
|
461
|
-
16,
|
|
462
|
-
[
|
|
463
|
-
dv.getFloat64(0, le),
|
|
464
|
-
dv.getFloat64(8, le)
|
|
465
|
-
]
|
|
466
|
-
];
|
|
467
|
+
return [16, [dv.getFloat64(0, le), dv.getFloat64(8, le)]];
|
|
467
468
|
}
|
|
468
469
|
case DdbType.duration: {
|
|
469
470
|
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
470
|
-
return [
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
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 }];
|
|
477
482
|
}
|
|
478
483
|
default:
|
|
479
484
|
throw new Error(`${DdbType[type] || type} 暂时不支持解析`);
|
|
@@ -634,7 +639,7 @@ export class DdbObj {
|
|
|
634
639
|
// 02 00 00 00 symbol base size = 2
|
|
635
640
|
// 00 61 61 00 以 \0 分割的字符串
|
|
636
641
|
// 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00>
|
|
637
|
-
const dv = new DataView(buf.buffer, buf.byteOffset
|
|
642
|
+
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
638
643
|
const base_id = dv.getUint32(0, le);
|
|
639
644
|
const base_size = dv.getUint32(4, le);
|
|
640
645
|
let base_length = 0;
|
|
@@ -669,7 +674,7 @@ export class DdbObj {
|
|
|
669
674
|
// 04 00 00 00 61 62 63 64
|
|
670
675
|
// 04 00 00 00 61 62 63 64>
|
|
671
676
|
let value = new Array(length);
|
|
672
|
-
const dv = new DataView(buf.buffer, buf.byteOffset
|
|
677
|
+
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
673
678
|
let i_head = 0;
|
|
674
679
|
for (let i = 0; i < length; i++) {
|
|
675
680
|
const sublen = dv.getUint32(i_head, le);
|
|
@@ -709,6 +714,31 @@ export class DdbObj {
|
|
|
709
714
|
values
|
|
710
715
|
];
|
|
711
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
|
+
}
|
|
712
742
|
// case DdbType.duration: -> 实际会返回一个 any vector
|
|
713
743
|
// [2y, 1M, 3d, 7H, 11m, 12s, 15ms, 16us, 17ns]
|
|
714
744
|
// <Buffer 19 01 type = any, form = vector
|
|
@@ -812,10 +842,7 @@ export class DdbObj {
|
|
|
812
842
|
case DdbType.int128:
|
|
813
843
|
return [this.value];
|
|
814
844
|
case DdbType.blob:
|
|
815
|
-
return [
|
|
816
|
-
Uint32Array.of(this.value.byteLength),
|
|
817
|
-
this.value
|
|
818
|
-
];
|
|
845
|
+
return [Uint32Array.of(this.value.byteLength), this.value];
|
|
819
846
|
case DdbType.complex:
|
|
820
847
|
case DdbType.point:
|
|
821
848
|
return [Float64Array.from(this.value)];
|
|
@@ -823,6 +850,14 @@ export class DdbObj {
|
|
|
823
850
|
const { unit, data } = this.value;
|
|
824
851
|
return [Int32Array.of(data, unit)];
|
|
825
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
|
+
}
|
|
826
861
|
default:
|
|
827
862
|
throw new Error(`${DdbType[type] || type} 暂时不支持序列化`);
|
|
828
863
|
}
|
|
@@ -844,12 +879,7 @@ export class DdbObj {
|
|
|
844
879
|
...DdbObj.pack_vector_body(value, type, this.rows)
|
|
845
880
|
];
|
|
846
881
|
case DdbForm.set:
|
|
847
|
-
return [
|
|
848
|
-
new DdbObj({
|
|
849
|
-
...this,
|
|
850
|
-
form: DdbForm.vector,
|
|
851
|
-
}).pack()
|
|
852
|
-
];
|
|
882
|
+
return [new DdbObj({ ...this, form: DdbForm.vector }).pack()];
|
|
853
883
|
case DdbForm.table:
|
|
854
884
|
return [
|
|
855
885
|
Uint32Array.of(this.rows, this.cols),
|
|
@@ -861,36 +891,27 @@ export class DdbObj {
|
|
|
861
891
|
...this.value.map(col => col.pack())
|
|
862
892
|
];
|
|
863
893
|
case DdbForm.dict:
|
|
864
|
-
return [
|
|
865
|
-
value[0].pack(),
|
|
866
|
-
value[1].pack(),
|
|
867
|
-
];
|
|
894
|
+
return [value[0].pack(), value[1].pack()];
|
|
868
895
|
case DdbForm.chart: {
|
|
869
|
-
const { type, stacking, 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;
|
|
870
897
|
const { value: [keys, values] } = new DdbDict({
|
|
871
898
|
chartType: new DdbInt(type),
|
|
872
899
|
stacking,
|
|
900
|
+
...bin_start ? { binStart: bin_start, binEnd: bin_end } : {},
|
|
901
|
+
...bin_count ? { binCount: bin_count } : {},
|
|
873
902
|
title: new DdbVectorString([chart, x_axis, y_axis]),
|
|
874
903
|
...extras ? (() => {
|
|
875
904
|
const { multi_y_axes, ...extras_other } = extras;
|
|
876
|
-
return {
|
|
877
|
-
extras: new DdbDict({
|
|
878
|
-
multiYAxes: multi_y_axes,
|
|
879
|
-
...extras_other
|
|
880
|
-
})
|
|
881
|
-
};
|
|
905
|
+
return { extras: new DdbDict({ multiYAxes: multi_y_axes, ...extras_other }) };
|
|
882
906
|
})() : {},
|
|
883
907
|
data,
|
|
884
908
|
});
|
|
885
|
-
return [
|
|
886
|
-
keys.pack(),
|
|
887
|
-
values.pack(),
|
|
888
|
-
];
|
|
909
|
+
return [keys.pack(), values.pack()];
|
|
889
910
|
}
|
|
890
911
|
case DdbForm.matrix: {
|
|
891
912
|
const { rows, cols, data } = value;
|
|
892
913
|
return [
|
|
893
|
-
Uint8Array.of((rows
|
|
914
|
+
Uint8Array.of((rows ? 0x01 : 0x00) | (cols ? 0x02 : 0x00)),
|
|
894
915
|
...rows ? [rows.pack()] : [],
|
|
895
916
|
...cols ? [cols.pack()] : [],
|
|
896
917
|
Uint8Array.of(this.type, this.form),
|
|
@@ -899,7 +920,7 @@ export class DdbObj {
|
|
|
899
920
|
];
|
|
900
921
|
}
|
|
901
922
|
default:
|
|
902
|
-
throw new Error(`${DdbForm[form]}
|
|
923
|
+
throw new Error(`${DdbForm[form]} serialization is not currently supported`);
|
|
903
924
|
}
|
|
904
925
|
})();
|
|
905
926
|
if (!body)
|
|
@@ -988,16 +1009,21 @@ export class DdbObj {
|
|
|
988
1009
|
data
|
|
989
1010
|
];
|
|
990
1011
|
}
|
|
1012
|
+
case DdbType.decimal32:
|
|
1013
|
+
case DdbType.decimal64: {
|
|
1014
|
+
const { scale, data } = value;
|
|
1015
|
+
return [Int32Array.of(scale), data];
|
|
1016
|
+
}
|
|
991
1017
|
default:
|
|
992
1018
|
throw new Error(`vector ${DdbType[type]} 暂不支持序列化`);
|
|
993
1019
|
}
|
|
994
1020
|
}
|
|
995
|
-
toString(
|
|
1021
|
+
toString(options) {
|
|
996
1022
|
const type = this.inspect_type();
|
|
997
1023
|
const data = (() => {
|
|
998
1024
|
switch (this.form) {
|
|
999
1025
|
case DdbForm.scalar:
|
|
1000
|
-
return format(this.type, this.value, this.le);
|
|
1026
|
+
return format(this.type, this.value, this.le, options);
|
|
1001
1027
|
case DdbForm.vector:
|
|
1002
1028
|
case DdbForm.pair:
|
|
1003
1029
|
case DdbForm.set: {
|
|
@@ -1028,7 +1054,7 @@ export class DdbObj {
|
|
|
1028
1054
|
for (const length of lengths) {
|
|
1029
1055
|
let _items = new Array(Math.min(limit, length));
|
|
1030
1056
|
for (let i = 0; i < _items.length; i++)
|
|
1031
|
-
_items[i] = format(_type, data[acc_len + i], this.le);
|
|
1057
|
+
_items[i] = format(_type, data[acc_len + i], this.le, options);
|
|
1032
1058
|
items[i_items++] = format_array(_items, length > limit);
|
|
1033
1059
|
acc_len += length;
|
|
1034
1060
|
}
|
|
@@ -1054,7 +1080,7 @@ export class DdbObj {
|
|
|
1054
1080
|
const len_data = value.length / 16;
|
|
1055
1081
|
let items = new Array(Math.min(limit, len_data));
|
|
1056
1082
|
for (let i = 0; i < items.length; i++)
|
|
1057
|
-
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);
|
|
1058
1084
|
return format_array(items, len_data > limit);
|
|
1059
1085
|
}
|
|
1060
1086
|
case DdbType.complex:
|
|
@@ -1064,24 +1090,33 @@ export class DdbObj {
|
|
|
1064
1090
|
const len_data = value.length / 2;
|
|
1065
1091
|
let items = new Array(Math.min(limit, len_data));
|
|
1066
1092
|
for (let i = 0; i < items.length; i++)
|
|
1067
|
-
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);
|
|
1068
1094
|
return format_array(items, len_data > limit);
|
|
1069
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
|
+
}
|
|
1070
1105
|
default: {
|
|
1071
1106
|
const limit = 50;
|
|
1072
1107
|
let items = new Array(Math.min(limit, this.value.length));
|
|
1073
1108
|
for (let i = 0; i < items.length; i++)
|
|
1074
|
-
items[i] = format(this.type, this.value[i], this.le);
|
|
1109
|
+
items[i] = format(this.type, this.value[i], this.le, options);
|
|
1075
1110
|
return format_array(items, this.value.length > limit);
|
|
1076
1111
|
}
|
|
1077
1112
|
}
|
|
1078
1113
|
}
|
|
1079
1114
|
case DdbForm.chart:
|
|
1080
|
-
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);
|
|
1081
1116
|
}
|
|
1082
1117
|
return String(this.value);
|
|
1083
1118
|
})();
|
|
1084
|
-
return `${colors ? blue(type) : type}(${this.name ? `'${this.name}', ` : ''}${data})\n`;
|
|
1119
|
+
return `${options?.colors ? blue(type) : type}(${this.name ? `'${this.name}', ` : ''}${data})\n`;
|
|
1085
1120
|
}
|
|
1086
1121
|
inspect_type() {
|
|
1087
1122
|
const tname = DdbType[this.type];
|
|
@@ -1101,7 +1136,7 @@ export class DdbObj {
|
|
|
1101
1136
|
case DdbForm.table:
|
|
1102
1137
|
return `table[${this.rows}r][${this.cols}c]`;
|
|
1103
1138
|
case DdbForm.dict:
|
|
1104
|
-
return `dict<${DdbType[this.value[0].type]}, ${DdbType[this.value[1].type]}
|
|
1139
|
+
return `dict<${DdbType[this.value[0].type]}, ${DdbType[this.value[1].type]}>[${this.rows}]`;
|
|
1105
1140
|
case DdbForm.chart:
|
|
1106
1141
|
return `chart<${DdbChartType[this.value.type]}>`;
|
|
1107
1142
|
case DdbForm.matrix:
|
|
@@ -1129,28 +1164,31 @@ export class DdbObj {
|
|
|
1129
1164
|
return values.map(value => this.to_ddbobj(value));
|
|
1130
1165
|
}
|
|
1131
1166
|
to_cols() {
|
|
1167
|
+
assert(this.form === DdbForm.table, 'form must be DdbForm.table, otherwise it cannot to_cols');
|
|
1132
1168
|
return this.value.map(col => {
|
|
1133
1169
|
let col_ = {
|
|
1134
1170
|
title: col.name,
|
|
1135
1171
|
dataIndex: col.name,
|
|
1136
1172
|
};
|
|
1137
1173
|
switch (col.type) {
|
|
1174
|
+
case DdbType.char:
|
|
1175
|
+
col_.render = (value) => format(DdbType.char, value, this.le);
|
|
1176
|
+
break;
|
|
1138
1177
|
case DdbType.timestamp:
|
|
1139
|
-
col_.render = value => timestamp2str(value);
|
|
1178
|
+
col_.render = (value) => timestamp2str(value);
|
|
1140
1179
|
break;
|
|
1141
1180
|
case DdbType.date:
|
|
1142
|
-
col_.render = value => date2str(value);
|
|
1181
|
+
col_.render = (value) => date2str(value);
|
|
1143
1182
|
break;
|
|
1144
1183
|
case DdbType.ipaddr:
|
|
1145
|
-
col_.render = value => ipaddr2str(value, this.le);
|
|
1184
|
+
col_.render = (value) => ipaddr2str(value, this.le);
|
|
1146
1185
|
break;
|
|
1147
1186
|
}
|
|
1148
1187
|
return col_;
|
|
1149
1188
|
});
|
|
1150
1189
|
}
|
|
1151
1190
|
to_rows() {
|
|
1152
|
-
|
|
1153
|
-
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');
|
|
1154
1192
|
let rows = new Array(this.rows);
|
|
1155
1193
|
for (let i = 0; i < this.rows; i++) {
|
|
1156
1194
|
let row = {};
|
|
@@ -1182,13 +1220,10 @@ export class DdbObj {
|
|
|
1182
1220
|
return rows;
|
|
1183
1221
|
}
|
|
1184
1222
|
to_dict({ strip, deep, } = {}) {
|
|
1185
|
-
|
|
1186
|
-
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');
|
|
1187
1224
|
const [{ value: keys, type: key_type }, { value: values, type: value_type }] = this.value;
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
if (deep && !strip)
|
|
1191
|
-
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');
|
|
1192
1227
|
let obj = {};
|
|
1193
1228
|
for (let i = 0; i < this.rows; i++) {
|
|
1194
1229
|
let value = values[i];
|
|
@@ -1200,8 +1235,23 @@ export class DdbObj {
|
|
|
1200
1235
|
return obj;
|
|
1201
1236
|
}
|
|
1202
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 });
|
|
1203
1243
|
/** Formats a single element (value) as a string according to DdbType, null returns a 'null' string */
|
|
1204
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
|
+
})();
|
|
1205
1255
|
switch (type) {
|
|
1206
1256
|
case DdbType.bool:
|
|
1207
1257
|
return String((value === null || value === nulls.int8) ?
|
|
@@ -1219,20 +1269,20 @@ export function format(type, value, le, options = {}) {
|
|
|
1219
1269
|
:
|
|
1220
1270
|
value);
|
|
1221
1271
|
case DdbType.short:
|
|
1222
|
-
return
|
|
1223
|
-
null
|
|
1272
|
+
return (value === null || value === nulls.int16) ?
|
|
1273
|
+
'null'
|
|
1224
1274
|
:
|
|
1225
|
-
value);
|
|
1275
|
+
default_formatter.format(value);
|
|
1226
1276
|
case DdbType.int:
|
|
1227
|
-
return
|
|
1228
|
-
null
|
|
1277
|
+
return (value === null || value === nulls.int32) ?
|
|
1278
|
+
'null'
|
|
1229
1279
|
:
|
|
1230
|
-
value);
|
|
1280
|
+
default_formatter.format(value);
|
|
1231
1281
|
case DdbType.long:
|
|
1232
1282
|
return (value === null || value === nulls.int64) ?
|
|
1233
1283
|
'null'
|
|
1234
1284
|
:
|
|
1235
|
-
|
|
1285
|
+
default_formatter.format(value);
|
|
1236
1286
|
case DdbType.date:
|
|
1237
1287
|
return (value === null || value === nulls.int32) ?
|
|
1238
1288
|
'null'
|
|
@@ -1282,12 +1332,12 @@ export function format(type, value, le, options = {}) {
|
|
|
1282
1332
|
return (value === null || value === nulls.float32) ?
|
|
1283
1333
|
'null'
|
|
1284
1334
|
:
|
|
1285
|
-
|
|
1335
|
+
formatter.format(value);
|
|
1286
1336
|
case DdbType.double:
|
|
1287
1337
|
return (value === null || value === nulls.double) ?
|
|
1288
1338
|
'null'
|
|
1289
1339
|
:
|
|
1290
|
-
|
|
1340
|
+
formatter.format(value);
|
|
1291
1341
|
case DdbType.symbol:
|
|
1292
1342
|
case DdbType.string:
|
|
1293
1343
|
return value.quote('single');
|
|
@@ -1314,30 +1364,32 @@ export function format(type, value, le, options = {}) {
|
|
|
1314
1364
|
:
|
|
1315
1365
|
DdbObj.dec.decode(value)).quote('single');
|
|
1316
1366
|
case DdbType.point: {
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
x = null;
|
|
1320
|
-
if (y === nulls.double)
|
|
1321
|
-
y = null;
|
|
1322
|
-
return `(${String(x)}, ${String(y)})`;
|
|
1367
|
+
const [x, y] = value;
|
|
1368
|
+
return `(${format(DdbType.double, x, le, options)}, ${format(DdbType.double, y, le, options)})`;
|
|
1323
1369
|
}
|
|
1324
1370
|
case DdbType.complex: {
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
x = null;
|
|
1328
|
-
if (y === nulls.double)
|
|
1329
|
-
y = null;
|
|
1330
|
-
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`;
|
|
1331
1373
|
}
|
|
1332
1374
|
case DdbType.duration: {
|
|
1333
1375
|
const { data, unit } = value;
|
|
1334
1376
|
return `${data}${DdbDurationUnit[unit]}`;
|
|
1335
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
|
+
}
|
|
1336
1388
|
default:
|
|
1337
1389
|
return String(value);
|
|
1338
1390
|
}
|
|
1339
1391
|
}
|
|
1340
|
-
/**
|
|
1392
|
+
/** formatted vector, the index-th item in the collection is a string, a null value returns a 'null' string */
|
|
1341
1393
|
export function formati(obj, index, options = {}) {
|
|
1342
1394
|
if (64 <= obj.type && obj.type < 128) { // array vector
|
|
1343
1395
|
// 因为 array vector 目前只支持:Logical, Integral(不包括 INT128, COMPRESS 类型), Floating, Temporal
|
|
@@ -1393,6 +1445,16 @@ export function formati(obj, index, options = {}) {
|
|
|
1393
1445
|
case DdbType.complex:
|
|
1394
1446
|
case DdbType.point:
|
|
1395
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
|
+
}
|
|
1396
1458
|
default:
|
|
1397
1459
|
return format(obj.type, obj.value[index], obj.le, options);
|
|
1398
1460
|
}
|
|
@@ -1807,8 +1869,7 @@ export function datehour2str(datehour, format = 'YYYY.MM.DDTHH') {
|
|
|
1807
1869
|
export function str2timestamp(str, format = 'YYYY.MM.DD HH:mm:ss.SSS') {
|
|
1808
1870
|
if (!str || str === 'null')
|
|
1809
1871
|
return nulls.int64;
|
|
1810
|
-
|
|
1811
|
-
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');
|
|
1812
1873
|
return BigInt(-timezone_offset +
|
|
1813
1874
|
dayjs(str, format).valueOf());
|
|
1814
1875
|
}
|
|
@@ -1821,12 +1882,10 @@ export function nanotime2str(nanotime, format = 'HH:mm:ss.SSSSSSSSS') {
|
|
|
1821
1882
|
if (nanotime < 0n)
|
|
1822
1883
|
return String(nanotime);
|
|
1823
1884
|
const i_second_start = format.indexOf('ss');
|
|
1824
|
-
|
|
1825
|
-
throw new Error('格式串必须包含秒的格式 (ss)');
|
|
1885
|
+
assert(i_second_start !== -1, 'The format string must contain the format for seconds (ss)');
|
|
1826
1886
|
const i_second_end = i_second_start + 2;
|
|
1827
1887
|
const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
|
|
1828
|
-
|
|
1829
|
-
throw new Error('格式串必须包含纳秒的格式 (SSSSSSSSS)');
|
|
1888
|
+
assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
|
|
1830
1889
|
return (dayjs(timezone_offset + (Number(nanotime) / 1000000)).format(format.slice(0, i_second_end)) +
|
|
1831
1890
|
format.slice(i_second_end, i_nanosecond_start) +
|
|
1832
1891
|
String(nanotime % 1000000000n).padStart(9, '0'));
|
|
@@ -1854,12 +1913,10 @@ export function nanotimestamp2str(nanotimestamp, format = 'YYYY.MM.DD HH:mm:ss.S
|
|
|
1854
1913
|
if (nanotimestamp === null || nanotimestamp === nulls.int64)
|
|
1855
1914
|
return 'null';
|
|
1856
1915
|
const i_second_start = format.indexOf('ss');
|
|
1857
|
-
|
|
1858
|
-
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)');
|
|
1859
1917
|
const i_second_end = i_second_start + 2;
|
|
1860
1918
|
const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
|
|
1861
|
-
|
|
1862
|
-
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)');
|
|
1863
1920
|
const remainder = nanotimestamp % 1000000000n;
|
|
1864
1921
|
const borrow = remainder < 0n;
|
|
1865
1922
|
return (dayjs(timezone_offset +
|
|
@@ -1881,15 +1938,12 @@ export function nanotimestamp2str(nanotimestamp, format = 'YYYY.MM.DD HH:mm:ss.S
|
|
|
1881
1938
|
export function str2nanotimestamp(str, format = 'YYYY.MM.DD HH:mm:ss.SSSSSSSSS') {
|
|
1882
1939
|
if (!str || str === 'null')
|
|
1883
1940
|
return nulls.int64;
|
|
1884
|
-
|
|
1885
|
-
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');
|
|
1886
1942
|
const i_second_start = format.indexOf('ss');
|
|
1887
|
-
|
|
1888
|
-
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)');
|
|
1889
1944
|
const i_second_end = i_second_start + 2;
|
|
1890
1945
|
const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
|
|
1891
|
-
|
|
1892
|
-
throw new Error('Format string must contain nanosecond format (SSSSSSSSS)');
|
|
1946
|
+
assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
|
|
1893
1947
|
return (BigInt(-timezone_offset +
|
|
1894
1948
|
dayjs(str.slice(0, i_second_end), format.slice(0, i_second_end)).valueOf()) * 1000000n
|
|
1895
1949
|
+
|
|
@@ -1917,6 +1971,7 @@ export function int1282str(buffer, le = true) {
|
|
|
1917
1971
|
return [...buf].map(x => x.toString(16)
|
|
1918
1972
|
.padStart(2, '0')).join('');
|
|
1919
1973
|
}
|
|
1974
|
+
export const winsize = 40;
|
|
1920
1975
|
export class DDB {
|
|
1921
1976
|
/** 当前的 session id (http 或 tcp) */
|
|
1922
1977
|
sid = '0';
|
|
@@ -1940,13 +1995,18 @@ export class DDB {
|
|
|
1940
1995
|
password = '123456';
|
|
1941
1996
|
/** python session flag (2048) */
|
|
1942
1997
|
python = false;
|
|
1998
|
+
/** Whether it is a streaming data connection, this field is always null for non-streaming data */
|
|
1999
|
+
streaming = null;
|
|
2000
|
+
// --- 内部选项, 状态
|
|
1943
2001
|
print_message_buffer = false;
|
|
1944
2002
|
print_object_buffer = false;
|
|
1945
2003
|
print_message = true;
|
|
1946
2004
|
parse_object = true;
|
|
2005
|
+
pnode_run_defined = false;
|
|
1947
2006
|
/** DdbMessage listeners */
|
|
1948
2007
|
listeners = [];
|
|
1949
2008
|
pconnect = Promise.resolve();
|
|
2009
|
+
ppnoderun = Promise.resolve();
|
|
1950
2010
|
presult = Promise.resolve(null);
|
|
1951
2011
|
get connected() {
|
|
1952
2012
|
return this.websocket?.readyState === WebSocket.OPEN;
|
|
@@ -1960,6 +2020,7 @@ export class DDB {
|
|
|
1960
2020
|
- username?: DolphinDB username, default `'admin'`
|
|
1961
2021
|
- password?: DolphinDB password, default `'123456'`
|
|
1962
2022
|
- python?: set python session flag, default `false`
|
|
2023
|
+
- streaming?: When this option is set, the WebSocket connection is only used for streaming data
|
|
1963
2024
|
|
|
1964
2025
|
@example
|
|
1965
2026
|
let ddb = new DDB('ws://127.0.0.1:8848')
|
|
@@ -1977,7 +2038,7 @@ export class DDB {
|
|
|
1977
2038
|
const _url = new URL(location.href);
|
|
1978
2039
|
const hostname = _url.searchParams.get('hostname') || location.hostname;
|
|
1979
2040
|
const port = _url.searchParams.get('port') || location.port;
|
|
1980
|
-
url = `${location.protocol === 'https:' ? 'wss' : 'ws'}://${hostname}
|
|
2041
|
+
url = `${location.protocol === 'https:' ? 'wss' : 'ws'}://${hostname}:${port}/`;
|
|
1981
2042
|
}
|
|
1982
2043
|
this.url = url;
|
|
1983
2044
|
if (options.autologin !== undefined)
|
|
@@ -1988,8 +2049,13 @@ export class DDB {
|
|
|
1988
2049
|
this.password = options.password;
|
|
1989
2050
|
if (options.python !== undefined)
|
|
1990
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);
|
|
1991
2058
|
}
|
|
1992
|
-
on_message(event) { }
|
|
1993
2059
|
/** Establish the actual WebSocket connection to the DolphinDB corresponding to the URL
|
|
1994
2060
|
- options?:
|
|
1995
2061
|
- url?: DolphinDB WebSocket URL. By default, the WebSocket URL passed in when the instance is initialized is used
|
|
@@ -1997,69 +2063,42 @@ export class DDB {
|
|
|
1997
2063
|
- username?: DolphinDB username, default `'admin'`
|
|
1998
2064
|
- password?: DolphinDB password, default `'123456'`
|
|
1999
2065
|
- python?: set python session flag, default `false`
|
|
2066
|
+
- streaming?: When this option is set, the WebSocket connection is only used for streaming data
|
|
2000
2067
|
*/
|
|
2001
|
-
async connect(
|
|
2002
|
-
if (
|
|
2003
|
-
this.url =
|
|
2004
|
-
if (
|
|
2005
|
-
this.autologin =
|
|
2006
|
-
if (
|
|
2007
|
-
this.username =
|
|
2008
|
-
if (
|
|
2009
|
-
this.password =
|
|
2010
|
-
if (
|
|
2011
|
-
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;
|
|
2012
2081
|
this.disconnect();
|
|
2013
2082
|
await connect_websocket(this.url, {
|
|
2014
|
-
protocols:
|
|
2083
|
+
protocols: (() => {
|
|
2084
|
+
if (this.streaming)
|
|
2085
|
+
return 'streaming';
|
|
2086
|
+
if (this.python)
|
|
2087
|
+
return 'python';
|
|
2088
|
+
})(),
|
|
2015
2089
|
on_open: async (event, websocket) => {
|
|
2016
2090
|
this.websocket = websocket;
|
|
2017
|
-
|
|
2091
|
+
if (this.streaming)
|
|
2092
|
+
await this.subscribe();
|
|
2093
|
+
else
|
|
2094
|
+
await this.rpc('connect', {});
|
|
2018
2095
|
},
|
|
2019
2096
|
on_message: (event) => {
|
|
2020
2097
|
this.on_message(event);
|
|
2021
2098
|
}
|
|
2022
2099
|
});
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
' nargs = size(args)\n' +
|
|
2026
|
-
' func = funcByName(func_name)\n' +
|
|
2027
|
-
' \n' +
|
|
2028
|
-
' if not nargs:\n' +
|
|
2029
|
-
' return pnodeRun(func, nodes, add_node_alias)\n' +
|
|
2030
|
-
' \n' +
|
|
2031
|
-
' args_partial = [ ]\n' +
|
|
2032
|
-
' args_partial.append(func)\n' +
|
|
2033
|
-
' for a in args:\n' +
|
|
2034
|
-
' args_partial.append(a)\n' +
|
|
2035
|
-
' \n' +
|
|
2036
|
-
' return pnodeRun(\n' +
|
|
2037
|
-
' unifiedCall(partial, args_partial),\n' +
|
|
2038
|
-
' nodes,\n' +
|
|
2039
|
-
' add_node_alias\n' +
|
|
2040
|
-
' )\n'
|
|
2041
|
-
:
|
|
2042
|
-
'def pnode_run (nodes, func_name, args, add_node_alias = true) {\n' +
|
|
2043
|
-
' nargs = size(args)\n' +
|
|
2044
|
-
' func = funcByName(func_name)\n' +
|
|
2045
|
-
' \n' +
|
|
2046
|
-
' if (!nargs)\n' +
|
|
2047
|
-
' return pnodeRun(func, nodes, add_node_alias)\n' +
|
|
2048
|
-
' \n' +
|
|
2049
|
-
' args_partial = array(any, 1 + nargs, 1 + nargs)\n' +
|
|
2050
|
-
' args_partial[0] = func\n' +
|
|
2051
|
-
' args_partial[1:] = args\n' +
|
|
2052
|
-
' return pnodeRun(\n' +
|
|
2053
|
-
' unifiedCall(partial, args_partial),\n' +
|
|
2054
|
-
' nodes,\n' +
|
|
2055
|
-
' add_node_alias\n' +
|
|
2056
|
-
' )\n' +
|
|
2057
|
-
'}\n', { urgent: true });
|
|
2058
|
-
if (this.autologin)
|
|
2059
|
-
if (this.python)
|
|
2060
|
-
await this.eval(`login(${this.username.quote('double')}, ${this.password.quote('double')})`, { urgent: true });
|
|
2061
|
-
else
|
|
2062
|
-
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 });
|
|
2063
2102
|
}
|
|
2064
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, } = {}) {
|
|
2065
2104
|
let flag = 0;
|
|
@@ -2098,6 +2137,7 @@ export class DDB {
|
|
|
2098
2137
|
this.on_message = () => { };
|
|
2099
2138
|
this.presult = Promise.resolve(null);
|
|
2100
2139
|
this.pconnect = Promise.resolve();
|
|
2140
|
+
this.pnode_run_defined = false;
|
|
2101
2141
|
}
|
|
2102
2142
|
/** rpc through websocket (function/script/variable command)
|
|
2103
2143
|
- type: API 类型: 'script' | 'function' | 'variable'
|
|
@@ -2126,6 +2166,55 @@ export class DDB {
|
|
|
2126
2166
|
}
|
|
2127
2167
|
if (!this.connected)
|
|
2128
2168
|
throw new Error(`${this.url} is already disconnected`);
|
|
2169
|
+
if (func === 'pnode_run' && !this.pnode_run_defined) {
|
|
2170
|
+
const ptail = this.ppnoderun;
|
|
2171
|
+
let resolve;
|
|
2172
|
+
this.ppnoderun = new Promise((_resolve, _reject) => {
|
|
2173
|
+
resolve = _resolve;
|
|
2174
|
+
});
|
|
2175
|
+
await ptail;
|
|
2176
|
+
try {
|
|
2177
|
+
if (!this.pnode_run_defined)
|
|
2178
|
+
await this.eval(this.python ?
|
|
2179
|
+
'def pnode_run (nodes, func_name, args, add_node_alias):\n' +
|
|
2180
|
+
' nargs = size(args)\n' +
|
|
2181
|
+
' func = funcByName(func_name)\n' +
|
|
2182
|
+
' \n' +
|
|
2183
|
+
' if not nargs:\n' +
|
|
2184
|
+
' return pnodeRun(func, nodes, add_node_alias)\n' +
|
|
2185
|
+
' \n' +
|
|
2186
|
+
' args_partial = [ ]\n' +
|
|
2187
|
+
' args_partial.append(func)\n' +
|
|
2188
|
+
' for a in args:\n' +
|
|
2189
|
+
' args_partial.append(a)\n' +
|
|
2190
|
+
' \n' +
|
|
2191
|
+
' return pnodeRun(\n' +
|
|
2192
|
+
' unifiedCall(partial, args_partial),\n' +
|
|
2193
|
+
' nodes,\n' +
|
|
2194
|
+
' add_node_alias\n' +
|
|
2195
|
+
' )\n'
|
|
2196
|
+
:
|
|
2197
|
+
'def pnode_run (nodes, func_name, args, add_node_alias = true) {\n' +
|
|
2198
|
+
' nargs = size(args)\n' +
|
|
2199
|
+
' func = funcByName(func_name)\n' +
|
|
2200
|
+
' \n' +
|
|
2201
|
+
' if (!nargs)\n' +
|
|
2202
|
+
' return pnodeRun(func, nodes, add_node_alias)\n' +
|
|
2203
|
+
' \n' +
|
|
2204
|
+
' args_partial = array(any, 1 + nargs, 1 + nargs)\n' +
|
|
2205
|
+
' args_partial[0] = func\n' +
|
|
2206
|
+
' args_partial[1:] = args\n' +
|
|
2207
|
+
' return pnodeRun(\n' +
|
|
2208
|
+
' unifiedCall(partial, args_partial),\n' +
|
|
2209
|
+
' nodes,\n' +
|
|
2210
|
+
' add_node_alias\n' +
|
|
2211
|
+
' )\n' +
|
|
2212
|
+
'}\n', { urgent: true });
|
|
2213
|
+
}
|
|
2214
|
+
finally {
|
|
2215
|
+
resolve();
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2129
2218
|
// this 上的当前配置需要在 message 到达后使用,先保存起来
|
|
2130
2219
|
const _handlers = [...this.listeners].reverse();
|
|
2131
2220
|
// 临界区:保证多个 rpc 并发时形成 promise 链
|
|
@@ -2227,8 +2316,7 @@ export class DDB {
|
|
|
2227
2316
|
*/
|
|
2228
2317
|
async call(func, args = [], { urgent, node, nodes, func_type, add_node_alias, listener, parse_object, } = {}) {
|
|
2229
2318
|
if (node) {
|
|
2230
|
-
|
|
2231
|
-
throw new Error('指定 node 时必须设置 func_type');
|
|
2319
|
+
assert(func_type in DdbFunctionType, 'func_type must be set when specifying node');
|
|
2232
2320
|
args = [
|
|
2233
2321
|
node,
|
|
2234
2322
|
new DdbFunction(func, func_type),
|
|
@@ -2265,10 +2353,21 @@ export class DDB {
|
|
|
2265
2353
|
vars,
|
|
2266
2354
|
/** Uploaded variables' value */
|
|
2267
2355
|
args, { listener, parse_object, } = {}) {
|
|
2268
|
-
|
|
2269
|
-
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');
|
|
2270
2357
|
return this.rpc('variable', { vars, args, listener, parse_object });
|
|
2271
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
|
+
}
|
|
2272
2371
|
/** 解析服务端响应报文,返回去掉 header 的 data buf */
|
|
2273
2372
|
parse_message(buf, parse_object = this.parse_object) {
|
|
2274
2373
|
// MSG\n
|
|
@@ -2283,8 +2382,8 @@ export class DDB {
|
|
|
2283
2382
|
// 'OK\n'
|
|
2284
2383
|
// '\x04\x00\x02\x00\x00\x00'
|
|
2285
2384
|
/** index of line feed 0 */
|
|
2286
|
-
const
|
|
2287
|
-
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(' ');
|
|
2288
2387
|
/** session id */
|
|
2289
2388
|
const sid = parts[0];
|
|
2290
2389
|
if (sid !== this.sid) {
|
|
@@ -2292,35 +2391,142 @@ export class DDB {
|
|
|
2292
2391
|
this.sid = sid;
|
|
2293
2392
|
}
|
|
2294
2393
|
/** 返回对象的数量 */
|
|
2295
|
-
const
|
|
2394
|
+
const nobj = Number(parts[1]);
|
|
2296
2395
|
/** 大小端: 协议中大端为 0, 小端为 1 */
|
|
2297
2396
|
this.le = Number(parts[2]) !== 0;
|
|
2298
|
-
const
|
|
2299
|
-
const
|
|
2397
|
+
const ils1 = ilf0 + 1;
|
|
2398
|
+
const ilf1 = buf.indexOf(0x0a, ils1); // '\n'
|
|
2300
2399
|
/** 'OK' 表示成功,其它文本表示失败 */
|
|
2301
|
-
const message = this.dec.decode(buf.subarray(
|
|
2400
|
+
const message = this.dec.decode(buf.subarray(ils1, ilf1));
|
|
2302
2401
|
if (message !== 'OK')
|
|
2303
2402
|
return {
|
|
2304
2403
|
type: 'error',
|
|
2305
2404
|
data: new Error(message)
|
|
2306
2405
|
};
|
|
2307
|
-
const
|
|
2406
|
+
const bufobj = buf.subarray(ilf1 + 1);
|
|
2308
2407
|
if (this.print_object_buffer)
|
|
2309
|
-
console.log(
|
|
2408
|
+
console.log(bufobj);
|
|
2310
2409
|
return {
|
|
2311
2410
|
type: 'object',
|
|
2312
2411
|
data: parse_object ?
|
|
2313
|
-
DdbObj.parse(
|
|
2412
|
+
DdbObj.parse(bufobj, this.le)
|
|
2314
2413
|
:
|
|
2315
2414
|
new DdbObj({
|
|
2316
2415
|
form: DdbForm.scalar,
|
|
2317
2416
|
type: DdbType.void,
|
|
2318
2417
|
length: 0,
|
|
2319
2418
|
le: this.le,
|
|
2320
|
-
buffer:
|
|
2419
|
+
buffer: bufobj,
|
|
2321
2420
|
})
|
|
2322
2421
|
};
|
|
2323
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
|
+
}
|
|
2324
2530
|
}
|
|
2325
2531
|
export let ddb = window.ddb = new DDB();
|
|
2326
2532
|
//# sourceMappingURL=browser.js.map
|