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/index.js
CHANGED
|
@@ -4,8 +4,8 @@ import DayjsCustomParseFormat from 'dayjs/plugin/customParseFormat.js';
|
|
|
4
4
|
dayjs.extend(DayjsCustomParseFormat);
|
|
5
5
|
import ipaddrjs from 'ipaddr.js';
|
|
6
6
|
const { fromByteArray: buf2ipaddr } = ipaddrjs;
|
|
7
|
+
import { concat, assert, inspect, typed_array_to_buffer } from 'xshell/utils.js';
|
|
7
8
|
import { connect_websocket } from 'xshell/net.js';
|
|
8
|
-
import { concat, inspect, typed_array_to_buffer } from 'xshell/utils.js';
|
|
9
9
|
export var DdbForm;
|
|
10
10
|
(function (DdbForm) {
|
|
11
11
|
DdbForm[DdbForm["scalar"] = 0] = "scalar";
|
|
@@ -62,7 +62,11 @@ export var DdbType;
|
|
|
62
62
|
DdbType[DdbType["complex"] = 34] = "complex";
|
|
63
63
|
DdbType[DdbType["point"] = 35] = "point";
|
|
64
64
|
DdbType[DdbType["duration"] = 36] = "duration";
|
|
65
|
-
DdbType[DdbType["
|
|
65
|
+
DdbType[DdbType["decimal32"] = 37] = "decimal32";
|
|
66
|
+
DdbType[DdbType["decimal64"] = 38] = "decimal64";
|
|
67
|
+
DdbType[DdbType["decimal128"] = 39] = "decimal128";
|
|
68
|
+
DdbType[DdbType["object"] = 40] = "object";
|
|
69
|
+
DdbType[DdbType["pynone"] = 41] = "pynone";
|
|
66
70
|
DdbType[DdbType["symbol_extended"] = 145] = "symbol_extended";
|
|
67
71
|
})(DdbType = DdbType || (DdbType = {}));
|
|
68
72
|
export var DdbFunctionType;
|
|
@@ -114,7 +118,15 @@ export const nulls = {
|
|
|
114
118
|
bytes16: Uint8Array.from(new Array(16).fill(0))
|
|
115
119
|
};
|
|
116
120
|
export const timezone_offset = 1000 * 60 * new Date().getTimezoneOffset();
|
|
117
|
-
|
|
121
|
+
export var StreamingStatusCode;
|
|
122
|
+
(function (StreamingStatusCode) {
|
|
123
|
+
StreamingStatusCode[StreamingStatusCode["ok"] = 0] = "ok";
|
|
124
|
+
StreamingStatusCode[StreamingStatusCode["connection_existed"] = 1] = "connection_existed";
|
|
125
|
+
StreamingStatusCode[StreamingStatusCode["not_leader"] = 2] = "not_leader";
|
|
126
|
+
StreamingStatusCode[StreamingStatusCode["other"] = 3] = "other";
|
|
127
|
+
StreamingStatusCode[StreamingStatusCode["error"] = 4] = "error";
|
|
128
|
+
})(StreamingStatusCode = StreamingStatusCode || (StreamingStatusCode = {}));
|
|
129
|
+
/** Can represent data types in all DolphinDB databases */
|
|
118
130
|
export class DdbObj {
|
|
119
131
|
static dec = new TextDecoder('utf-8');
|
|
120
132
|
static enc = new TextEncoder();
|
|
@@ -146,7 +158,7 @@ export class DdbObj {
|
|
|
146
158
|
rows;
|
|
147
159
|
/** 第 2 维 */
|
|
148
160
|
cols;
|
|
149
|
-
/**
|
|
161
|
+
/** The actual data. Different DdbForm, DdbType use different types in DdbValue to represent actual data */
|
|
150
162
|
value;
|
|
151
163
|
/** 原始二进制数据,仅在 parse_object 为 false 时通过 parse_message 生成的顶层对象有这个属性 */
|
|
152
164
|
buffer;
|
|
@@ -331,8 +343,7 @@ export class DdbObj {
|
|
|
331
343
|
col_labels.length += 2;
|
|
332
344
|
offset += col_labels.length;
|
|
333
345
|
}
|
|
334
|
-
|
|
335
|
-
throw new Error('matrix.datatype !== matrix.type');
|
|
346
|
+
assert(buf_data[offset] === type, 'matrix.datatype === matrix.type');
|
|
336
347
|
const rows = dv.getUint32(offset + 2, le);
|
|
337
348
|
const cols = dv.getUint32(offset + 6, le);
|
|
338
349
|
const [len_items, data] = this.parse_vector_items(buf_data.subarray(offset + 10), le, type, rows * cols // 假设小于 2**32
|
|
@@ -443,38 +454,30 @@ export class DdbObj {
|
|
|
443
454
|
case DdbType.uuid:
|
|
444
455
|
case DdbType.ipaddr:
|
|
445
456
|
case DdbType.int128:
|
|
446
|
-
return [
|
|
447
|
-
16,
|
|
448
|
-
buf.slice(0, 16)
|
|
449
|
-
];
|
|
457
|
+
return [16, buf.slice(0, 16)];
|
|
450
458
|
case DdbType.blob: {
|
|
451
459
|
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
452
460
|
const length = dv.getUint32(0, le);
|
|
453
|
-
return [
|
|
454
|
-
4 + length,
|
|
455
|
-
buf.slice(4, 4 + length)
|
|
456
|
-
];
|
|
461
|
+
return [4 + length, buf.slice(4, 4 + length)];
|
|
457
462
|
}
|
|
458
463
|
case DdbType.complex:
|
|
459
464
|
case DdbType.point: {
|
|
460
465
|
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
461
|
-
return [
|
|
462
|
-
16,
|
|
463
|
-
[
|
|
464
|
-
dv.getFloat64(0, le),
|
|
465
|
-
dv.getFloat64(8, le)
|
|
466
|
-
]
|
|
467
|
-
];
|
|
466
|
+
return [16, [dv.getFloat64(0, le), dv.getFloat64(8, le)]];
|
|
468
467
|
}
|
|
469
468
|
case DdbType.duration: {
|
|
470
469
|
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
471
|
-
return [
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
470
|
+
return [8, { unit: dv.getUint32(4, le), data: dv.getInt32(0, le) }];
|
|
471
|
+
}
|
|
472
|
+
case DdbType.decimal32: {
|
|
473
|
+
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
474
|
+
const data = dv.getInt32(4, le);
|
|
475
|
+
return [8, { scale: dv.getInt32(0, le), data: data === nulls.int32 ? null : data }];
|
|
476
|
+
}
|
|
477
|
+
case DdbType.decimal64: {
|
|
478
|
+
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
479
|
+
const data = dv.getBigInt64(4, le);
|
|
480
|
+
return [12, { scale: dv.getInt32(0, le), data: data === nulls.int64 ? null : data }];
|
|
478
481
|
}
|
|
479
482
|
default:
|
|
480
483
|
throw new Error(`${DdbType[type] || type} 暂时不支持解析`);
|
|
@@ -635,7 +638,7 @@ export class DdbObj {
|
|
|
635
638
|
// 02 00 00 00 symbol base size = 2
|
|
636
639
|
// 00 61 61 00 以 \0 分割的字符串
|
|
637
640
|
// 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00>
|
|
638
|
-
const dv = new DataView(buf.buffer, buf.byteOffset
|
|
641
|
+
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
639
642
|
const base_id = dv.getUint32(0, le);
|
|
640
643
|
const base_size = dv.getUint32(4, le);
|
|
641
644
|
let base_length = 0;
|
|
@@ -670,7 +673,7 @@ export class DdbObj {
|
|
|
670
673
|
// 04 00 00 00 61 62 63 64
|
|
671
674
|
// 04 00 00 00 61 62 63 64>
|
|
672
675
|
let value = new Array(length);
|
|
673
|
-
const dv = new DataView(buf.buffer, buf.byteOffset
|
|
676
|
+
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
674
677
|
let i_head = 0;
|
|
675
678
|
for (let i = 0; i < length; i++) {
|
|
676
679
|
const sublen = dv.getUint32(i_head, le);
|
|
@@ -710,6 +713,31 @@ export class DdbObj {
|
|
|
710
713
|
values
|
|
711
714
|
];
|
|
712
715
|
}
|
|
716
|
+
// 25 01 type = decimal32, form = vector
|
|
717
|
+
// 02 00 00 00 01 00 00 00
|
|
718
|
+
// 00 00 00 00 scale = 0
|
|
719
|
+
// 01 00 00 00 data[0] = 1
|
|
720
|
+
// 3a 01 00 00 data[1] = 0x013a = 314
|
|
721
|
+
case DdbType.decimal32: {
|
|
722
|
+
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
723
|
+
return [
|
|
724
|
+
4 + 4 * length,
|
|
725
|
+
{
|
|
726
|
+
scale: dv.getInt32(0, le),
|
|
727
|
+
data: new Int32Array(buf.buffer.slice(buf.byteOffset + 4, buf.byteOffset + 4 + 4 * length))
|
|
728
|
+
}
|
|
729
|
+
];
|
|
730
|
+
}
|
|
731
|
+
case DdbType.decimal64: {
|
|
732
|
+
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
733
|
+
return [
|
|
734
|
+
4 + 8 * length,
|
|
735
|
+
{
|
|
736
|
+
scale: dv.getInt32(0, le),
|
|
737
|
+
data: new BigInt64Array(buf.buffer.slice(buf.byteOffset + 4, buf.byteOffset + 4 + 8 * length))
|
|
738
|
+
}
|
|
739
|
+
];
|
|
740
|
+
}
|
|
713
741
|
// case DdbType.duration: -> 实际会返回一个 any vector
|
|
714
742
|
// [2y, 1M, 3d, 7H, 11m, 12s, 15ms, 16us, 17ns]
|
|
715
743
|
// <Buffer 19 01 type = any, form = vector
|
|
@@ -813,10 +841,7 @@ export class DdbObj {
|
|
|
813
841
|
case DdbType.int128:
|
|
814
842
|
return [this.value];
|
|
815
843
|
case DdbType.blob:
|
|
816
|
-
return [
|
|
817
|
-
Uint32Array.of(this.value.byteLength),
|
|
818
|
-
this.value
|
|
819
|
-
];
|
|
844
|
+
return [Uint32Array.of(this.value.byteLength), this.value];
|
|
820
845
|
case DdbType.complex:
|
|
821
846
|
case DdbType.point:
|
|
822
847
|
return [Float64Array.from(this.value)];
|
|
@@ -824,8 +849,16 @@ export class DdbObj {
|
|
|
824
849
|
const { unit, data } = this.value;
|
|
825
850
|
return [Int32Array.of(data, unit)];
|
|
826
851
|
}
|
|
852
|
+
case DdbType.decimal32: {
|
|
853
|
+
const { scale, data } = this.value;
|
|
854
|
+
return [Int32Array.of(scale, data === null ? nulls.int32 : data)];
|
|
855
|
+
}
|
|
856
|
+
case DdbType.decimal64: {
|
|
857
|
+
const { scale, data } = this.value;
|
|
858
|
+
return [Int32Array.of(scale), BigInt64Array.of(data === null ? nulls.int64 : data)];
|
|
859
|
+
}
|
|
827
860
|
default:
|
|
828
|
-
throw new Error(`${DdbType[type] || type}
|
|
861
|
+
throw new Error(`${DdbType[type] || type} serialization is not currently supported`);
|
|
829
862
|
}
|
|
830
863
|
case DdbForm.vector:
|
|
831
864
|
case DdbForm.pair:
|
|
@@ -845,12 +878,7 @@ export class DdbObj {
|
|
|
845
878
|
...DdbObj.pack_vector_body(value, type, this.rows)
|
|
846
879
|
];
|
|
847
880
|
case DdbForm.set:
|
|
848
|
-
return [
|
|
849
|
-
new DdbObj({
|
|
850
|
-
...this,
|
|
851
|
-
form: DdbForm.vector,
|
|
852
|
-
}).pack()
|
|
853
|
-
];
|
|
881
|
+
return [new DdbObj({ ...this, form: DdbForm.vector }).pack()];
|
|
854
882
|
case DdbForm.table:
|
|
855
883
|
return [
|
|
856
884
|
Uint32Array.of(this.rows, this.cols),
|
|
@@ -862,43 +890,27 @@ export class DdbObj {
|
|
|
862
890
|
...this.value.map(col => col.pack())
|
|
863
891
|
];
|
|
864
892
|
case DdbForm.dict:
|
|
865
|
-
return [
|
|
866
|
-
value[0].pack(),
|
|
867
|
-
value[1].pack(),
|
|
868
|
-
];
|
|
893
|
+
return [value[0].pack(), value[1].pack()];
|
|
869
894
|
case DdbForm.chart: {
|
|
870
|
-
const { type, stacking, bin_start, bin_end, bin_count, titles: { chart, x_axis, y_axis
|
|
895
|
+
const { type, stacking, bin_start, bin_end, bin_count, titles: { chart, x_axis, y_axis }, extras, data } = this.value;
|
|
871
896
|
const { value: [keys, values] } = new DdbDict({
|
|
872
897
|
chartType: new DdbInt(type),
|
|
873
898
|
stacking,
|
|
874
|
-
...bin_start ? {
|
|
875
|
-
|
|
876
|
-
binEnd: bin_end
|
|
877
|
-
} : {},
|
|
878
|
-
...bin_count ? {
|
|
879
|
-
binCount: bin_count
|
|
880
|
-
} : {},
|
|
899
|
+
...bin_start ? { binStart: bin_start, binEnd: bin_end } : {},
|
|
900
|
+
...bin_count ? { binCount: bin_count } : {},
|
|
881
901
|
title: new DdbVectorString([chart, x_axis, y_axis]),
|
|
882
902
|
...extras ? (() => {
|
|
883
903
|
const { multi_y_axes, ...extras_other } = extras;
|
|
884
|
-
return {
|
|
885
|
-
extras: new DdbDict({
|
|
886
|
-
multiYAxes: multi_y_axes,
|
|
887
|
-
...extras_other
|
|
888
|
-
})
|
|
889
|
-
};
|
|
904
|
+
return { extras: new DdbDict({ multiYAxes: multi_y_axes, ...extras_other }) };
|
|
890
905
|
})() : {},
|
|
891
906
|
data,
|
|
892
907
|
});
|
|
893
|
-
return [
|
|
894
|
-
keys.pack(),
|
|
895
|
-
values.pack(),
|
|
896
|
-
];
|
|
908
|
+
return [keys.pack(), values.pack()];
|
|
897
909
|
}
|
|
898
910
|
case DdbForm.matrix: {
|
|
899
911
|
const { rows, cols, data } = value;
|
|
900
912
|
return [
|
|
901
|
-
Uint8Array.of((rows
|
|
913
|
+
Uint8Array.of((rows ? 0x01 : 0x00) | (cols ? 0x02 : 0x00)),
|
|
902
914
|
...rows ? [rows.pack()] : [],
|
|
903
915
|
...cols ? [cols.pack()] : [],
|
|
904
916
|
Uint8Array.of(this.type, this.form),
|
|
@@ -907,7 +919,7 @@ export class DdbObj {
|
|
|
907
919
|
];
|
|
908
920
|
}
|
|
909
921
|
default:
|
|
910
|
-
throw new Error(`${DdbForm[form]}
|
|
922
|
+
throw new Error(`${DdbForm[form]} serialization is not currently supported`);
|
|
911
923
|
}
|
|
912
924
|
})();
|
|
913
925
|
if (!body)
|
|
@@ -996,11 +1008,17 @@ export class DdbObj {
|
|
|
996
1008
|
data
|
|
997
1009
|
];
|
|
998
1010
|
}
|
|
1011
|
+
case DdbType.decimal32:
|
|
1012
|
+
case DdbType.decimal64: {
|
|
1013
|
+
const { scale, data } = value;
|
|
1014
|
+
return [Int32Array.of(scale), data];
|
|
1015
|
+
}
|
|
999
1016
|
default:
|
|
1000
1017
|
throw new Error(`vector ${DdbType[type]} 暂不支持序列化`);
|
|
1001
1018
|
}
|
|
1002
1019
|
}
|
|
1003
|
-
[inspect.custom](depth,
|
|
1020
|
+
[inspect.custom](depth, _options, _inspect) {
|
|
1021
|
+
const options = { nullstr: true, quote: true, ..._options };
|
|
1004
1022
|
const type = this.inspect_type();
|
|
1005
1023
|
const data = (() => {
|
|
1006
1024
|
switch (this.form) {
|
|
@@ -1075,6 +1093,15 @@ export class DdbObj {
|
|
|
1075
1093
|
items[i] = format(this.type, value.subarray(2 * i, 2 * (i + 1)), this.le, options);
|
|
1076
1094
|
return format_array(items, len_data > limit);
|
|
1077
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
|
+
}
|
|
1078
1105
|
default: {
|
|
1079
1106
|
const limit = 50;
|
|
1080
1107
|
let items = new Array(Math.min(limit, this.value.length));
|
|
@@ -1137,8 +1164,7 @@ export class DdbObj {
|
|
|
1137
1164
|
return values.map(value => this.to_ddbobj(value));
|
|
1138
1165
|
}
|
|
1139
1166
|
to_rows() {
|
|
1140
|
-
|
|
1141
|
-
throw new Error('this.form is not DdbForm.table, cannot to_rows');
|
|
1167
|
+
assert(this.form === DdbForm.table, 'form must be DdbForm.table, otherwise it cannot to_rows');
|
|
1142
1168
|
let rows = new Array(this.rows);
|
|
1143
1169
|
for (let i = 0; i < this.rows; i++) {
|
|
1144
1170
|
let row = {};
|
|
@@ -1170,13 +1196,10 @@ export class DdbObj {
|
|
|
1170
1196
|
return rows;
|
|
1171
1197
|
}
|
|
1172
1198
|
to_dict({ strip, deep, } = {}) {
|
|
1173
|
-
|
|
1174
|
-
throw new Error('this.form is not DdbForm.dict and cannot be converted to js object');
|
|
1199
|
+
assert(this.form === DdbForm.dict, 'this.form must be DdbForm.dict, otherwise to_dict cannot be called to convert to js object');
|
|
1175
1200
|
const [{ value: keys, type: key_type }, { value: values, type: value_type }] = this.value;
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
if (deep && !strip)
|
|
1179
|
-
throw new Error('strip = true must be set when deep = true');
|
|
1201
|
+
assert(key_type === DdbType.string && value_type === DdbType.any, 'Currently only supports automatic conversion of dict<string, any> to js object');
|
|
1202
|
+
assert(!(deep && !strip), 'strip = true must be set when deep = true');
|
|
1180
1203
|
let obj = {};
|
|
1181
1204
|
for (let i = 0; i < this.rows; i++) {
|
|
1182
1205
|
let value = values[i];
|
|
@@ -1188,8 +1211,23 @@ export class DdbObj {
|
|
|
1188
1211
|
return obj;
|
|
1189
1212
|
}
|
|
1190
1213
|
}
|
|
1214
|
+
/** 整数一定用这个 number formatter, InspectOptions.decimals 不传也用这个 */
|
|
1215
|
+
let default_formatter = Intl.NumberFormat('en-US', { maximumFractionDigits: 20 });
|
|
1216
|
+
let _decimals = 20;
|
|
1217
|
+
/** 缓存,为了优化性能,通常 options.decimals 都是不变的 */
|
|
1218
|
+
let _formatter = Intl.NumberFormat('en-US', { maximumFractionDigits: 20 });
|
|
1191
1219
|
/** Formats a single element (value) as a string according to DdbType, null returns a 'null' string */
|
|
1192
|
-
export function format(type, value, le, options) {
|
|
1220
|
+
export function format(type, value, le, options = {}) {
|
|
1221
|
+
const { decimals } = options;
|
|
1222
|
+
const formatter = (() => {
|
|
1223
|
+
if (decimals === undefined || decimals === null)
|
|
1224
|
+
return default_formatter;
|
|
1225
|
+
if (decimals !== _decimals) {
|
|
1226
|
+
_decimals = decimals;
|
|
1227
|
+
_formatter = Intl.NumberFormat('en-US', { maximumFractionDigits: decimals, minimumFractionDigits: decimals });
|
|
1228
|
+
}
|
|
1229
|
+
return _formatter;
|
|
1230
|
+
})();
|
|
1193
1231
|
switch (type) {
|
|
1194
1232
|
case DdbType.bool:
|
|
1195
1233
|
return inspect((value === null || value === nulls.int8) ?
|
|
@@ -1207,111 +1245,123 @@ export function format(type, value, le, options) {
|
|
|
1207
1245
|
:
|
|
1208
1246
|
value, options);
|
|
1209
1247
|
case DdbType.short:
|
|
1210
|
-
return
|
|
1211
|
-
null
|
|
1248
|
+
return value === null || value === nulls.int16 ?
|
|
1249
|
+
inspect(null, options)
|
|
1212
1250
|
:
|
|
1213
|
-
|
|
1251
|
+
options.colors ?
|
|
1252
|
+
default_formatter.format(value).green
|
|
1253
|
+
:
|
|
1254
|
+
default_formatter.format(value);
|
|
1214
1255
|
case DdbType.int:
|
|
1215
|
-
return
|
|
1216
|
-
null
|
|
1256
|
+
return value === null || value === nulls.int32 ?
|
|
1257
|
+
inspect(null, options)
|
|
1217
1258
|
:
|
|
1218
|
-
|
|
1259
|
+
options.colors ?
|
|
1260
|
+
default_formatter.format(value).green
|
|
1261
|
+
:
|
|
1262
|
+
default_formatter.format(value);
|
|
1219
1263
|
case DdbType.long:
|
|
1220
|
-
return
|
|
1221
|
-
|
|
1264
|
+
return value === null || value === nulls.int64 ?
|
|
1265
|
+
inspect(null, options)
|
|
1222
1266
|
:
|
|
1223
1267
|
options.colors ?
|
|
1224
|
-
|
|
1268
|
+
default_formatter.format(value).green
|
|
1225
1269
|
:
|
|
1226
|
-
|
|
1270
|
+
default_formatter.format(value);
|
|
1227
1271
|
case DdbType.date:
|
|
1228
1272
|
return (value === null || value === nulls.int32) ?
|
|
1229
|
-
|
|
1273
|
+
inspect(null, options)
|
|
1230
1274
|
:
|
|
1231
1275
|
options.colors ?
|
|
1232
|
-
date2str(value).
|
|
1276
|
+
date2str(value).magenta
|
|
1233
1277
|
:
|
|
1234
1278
|
date2str(value);
|
|
1235
1279
|
case DdbType.month:
|
|
1236
1280
|
return (value === null || value === nulls.int32) ?
|
|
1237
|
-
|
|
1281
|
+
inspect(null, options)
|
|
1238
1282
|
:
|
|
1239
1283
|
options.colors ?
|
|
1240
|
-
month2str(value).
|
|
1284
|
+
month2str(value).magenta
|
|
1241
1285
|
:
|
|
1242
1286
|
month2str(value);
|
|
1243
1287
|
case DdbType.time:
|
|
1244
1288
|
return (value === null || value === nulls.int32) ?
|
|
1245
|
-
|
|
1289
|
+
inspect(null, options)
|
|
1246
1290
|
:
|
|
1247
1291
|
options.colors ?
|
|
1248
|
-
time2str(value).
|
|
1292
|
+
time2str(value).magenta
|
|
1249
1293
|
:
|
|
1250
1294
|
time2str(value);
|
|
1251
1295
|
case DdbType.minute:
|
|
1252
1296
|
return (value === null || value === nulls.int32) ?
|
|
1253
|
-
|
|
1297
|
+
inspect(null, options)
|
|
1254
1298
|
:
|
|
1255
1299
|
options.colors ?
|
|
1256
|
-
minute2str(value).
|
|
1300
|
+
minute2str(value).magenta
|
|
1257
1301
|
:
|
|
1258
1302
|
minute2str(value);
|
|
1259
1303
|
case DdbType.second:
|
|
1260
1304
|
return (value === null || value === nulls.int32) ?
|
|
1261
|
-
|
|
1305
|
+
inspect(null, options)
|
|
1262
1306
|
:
|
|
1263
1307
|
options.colors ?
|
|
1264
|
-
second2str(value).
|
|
1308
|
+
second2str(value).magenta
|
|
1265
1309
|
:
|
|
1266
1310
|
second2str(value);
|
|
1267
1311
|
case DdbType.datetime:
|
|
1268
1312
|
return (value === null || value === nulls.int32) ?
|
|
1269
|
-
|
|
1313
|
+
inspect(null, options)
|
|
1270
1314
|
:
|
|
1271
1315
|
options.colors ?
|
|
1272
|
-
datetime2str(value).
|
|
1316
|
+
datetime2str(value).magenta
|
|
1273
1317
|
:
|
|
1274
1318
|
datetime2str(value);
|
|
1275
1319
|
case DdbType.timestamp:
|
|
1276
1320
|
return (value === null || value === nulls.int64) ?
|
|
1277
|
-
|
|
1321
|
+
inspect(null, options)
|
|
1278
1322
|
:
|
|
1279
1323
|
options.colors ?
|
|
1280
|
-
timestamp2str(value).
|
|
1324
|
+
timestamp2str(value).magenta
|
|
1281
1325
|
:
|
|
1282
1326
|
timestamp2str(value);
|
|
1283
1327
|
case DdbType.nanotime:
|
|
1284
1328
|
return (value === null || value === nulls.int64) ?
|
|
1285
|
-
|
|
1329
|
+
inspect(null, options)
|
|
1286
1330
|
:
|
|
1287
1331
|
options.colors ?
|
|
1288
|
-
nanotime2str(value).
|
|
1332
|
+
nanotime2str(value).magenta
|
|
1289
1333
|
:
|
|
1290
1334
|
nanotime2str(value);
|
|
1291
1335
|
case DdbType.nanotimestamp:
|
|
1292
1336
|
return (value === null || value === nulls.int64) ?
|
|
1293
|
-
|
|
1337
|
+
inspect(null, options)
|
|
1294
1338
|
:
|
|
1295
1339
|
options.colors ?
|
|
1296
|
-
nanotimestamp2str(value).
|
|
1340
|
+
nanotimestamp2str(value).magenta
|
|
1297
1341
|
:
|
|
1298
1342
|
nanotimestamp2str(value);
|
|
1299
1343
|
case DdbType.float:
|
|
1300
1344
|
return (value === null || value === nulls.float32) ?
|
|
1301
|
-
|
|
1345
|
+
inspect(null, options)
|
|
1302
1346
|
:
|
|
1303
|
-
|
|
1347
|
+
options.colors ?
|
|
1348
|
+
formatter.format(value).green
|
|
1349
|
+
:
|
|
1350
|
+
formatter.format(value);
|
|
1304
1351
|
case DdbType.double:
|
|
1305
1352
|
return (value === null || value === nulls.double) ?
|
|
1306
|
-
|
|
1353
|
+
inspect(null, options)
|
|
1307
1354
|
:
|
|
1308
|
-
|
|
1355
|
+
options.colors ?
|
|
1356
|
+
formatter.format(value).green
|
|
1357
|
+
:
|
|
1358
|
+
formatter.format(value);
|
|
1309
1359
|
case DdbType.symbol:
|
|
1310
1360
|
case DdbType.string:
|
|
1311
1361
|
return inspect(value, options);
|
|
1312
1362
|
case DdbType.uuid:
|
|
1313
1363
|
return options.colors ?
|
|
1314
|
-
uuid2str(value, le).
|
|
1364
|
+
uuid2str(value, le).cyan
|
|
1315
1365
|
:
|
|
1316
1366
|
uuid2str(value, le);
|
|
1317
1367
|
case DdbType.functiondef:
|
|
@@ -1325,12 +1375,12 @@ export function format(type, value, le, options) {
|
|
|
1325
1375
|
'null'
|
|
1326
1376
|
:
|
|
1327
1377
|
options.colors ?
|
|
1328
|
-
datehour2str(value).
|
|
1378
|
+
datehour2str(value).magenta
|
|
1329
1379
|
:
|
|
1330
1380
|
datehour2str(value);
|
|
1331
1381
|
case DdbType.ipaddr:
|
|
1332
1382
|
return options.colors ?
|
|
1333
|
-
ipaddr2str(value, le).
|
|
1383
|
+
ipaddr2str(value, le).cyan
|
|
1334
1384
|
:
|
|
1335
1385
|
ipaddr2str(value, le);
|
|
1336
1386
|
case DdbType.int128:
|
|
@@ -1344,30 +1394,27 @@ export function format(type, value, le, options) {
|
|
|
1344
1394
|
:
|
|
1345
1395
|
DdbObj.dec.decode(value), options);
|
|
1346
1396
|
case DdbType.point: {
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
x = null;
|
|
1350
|
-
if (y === nulls.double)
|
|
1351
|
-
y = null;
|
|
1352
|
-
return options.colors ?
|
|
1353
|
-
`(${String(x).green}, ${String(y).green})`
|
|
1354
|
-
:
|
|
1355
|
-
`(${String(x)}, ${String(y)})`;
|
|
1397
|
+
const [x, y] = value;
|
|
1398
|
+
return `(${format(DdbType.double, x, le, options)}, ${format(DdbType.double, y, le, options)})`;
|
|
1356
1399
|
}
|
|
1357
1400
|
case DdbType.complex: {
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
x = null;
|
|
1361
|
-
if (y === nulls.double)
|
|
1362
|
-
y = null;
|
|
1363
|
-
return options.colors ?
|
|
1364
|
-
`${String(x).green}+${`${String(y)}i`.green}`
|
|
1365
|
-
:
|
|
1366
|
-
`${String(x)}+${String(y)}i`;
|
|
1401
|
+
const [x, y] = value;
|
|
1402
|
+
return `${format(DdbType.double, x, le, options)}+${format(DdbType.double, y, le, options)}i`;
|
|
1367
1403
|
}
|
|
1368
1404
|
case DdbType.duration: {
|
|
1369
1405
|
const { data, unit } = value;
|
|
1370
1406
|
const str = `${data}${DdbDurationUnit[unit]}`;
|
|
1407
|
+
return options.colors ? str.magenta : str;
|
|
1408
|
+
}
|
|
1409
|
+
case DdbType.decimal32:
|
|
1410
|
+
case DdbType.decimal64: {
|
|
1411
|
+
const { scale, data } = value;
|
|
1412
|
+
if (data === null ||
|
|
1413
|
+
(data === nulls.int64 && type === DdbType.decimal64) ||
|
|
1414
|
+
(data === nulls.int32 && type === DdbType.decimal32))
|
|
1415
|
+
return 'null';
|
|
1416
|
+
const s = String(data < 0 ? -data : data).padStart(scale, '0');
|
|
1417
|
+
const str = (data < 0 ? '-' : '') + (scale ? `${s.slice(0, -scale) || '0'}.${s.slice(-scale)}` : s);
|
|
1371
1418
|
return options.colors ? str.green : str;
|
|
1372
1419
|
}
|
|
1373
1420
|
default:
|
|
@@ -1375,7 +1422,7 @@ export function format(type, value, le, options) {
|
|
|
1375
1422
|
}
|
|
1376
1423
|
}
|
|
1377
1424
|
/** formatted vector, the index-th item in the collection is a string, a null value returns a 'null' string */
|
|
1378
|
-
export function formati(obj, index, options) {
|
|
1425
|
+
export function formati(obj, index, options = {}) {
|
|
1379
1426
|
if (64 <= obj.type && obj.type < 128) { // array vector
|
|
1380
1427
|
// 因为 array vector 目前只支持:Logical, Integral(不包括 INT128, COMPRESS 类型), Floating, Temporal
|
|
1381
1428
|
// 都对应 TypedArray 中的一格,所以 lengths.length 等于 block 中的 row 的个数
|
|
@@ -1409,12 +1456,9 @@ export function formati(obj, index, options) {
|
|
|
1409
1456
|
}
|
|
1410
1457
|
}
|
|
1411
1458
|
switch (obj.type) {
|
|
1412
|
-
case DdbType.string:
|
|
1413
|
-
case DdbType.symbol:
|
|
1414
|
-
return obj.value[index];
|
|
1415
1459
|
case DdbType.symbol_extended: {
|
|
1416
1460
|
const { base, data } = obj.value;
|
|
1417
|
-
return base[data[index]];
|
|
1461
|
+
return format(DdbType.string, base[data[index]], obj.le, options);
|
|
1418
1462
|
}
|
|
1419
1463
|
case DdbType.uuid:
|
|
1420
1464
|
case DdbType.int128:
|
|
@@ -1430,6 +1474,17 @@ export function formati(obj, index, options) {
|
|
|
1430
1474
|
case DdbType.complex:
|
|
1431
1475
|
case DdbType.point:
|
|
1432
1476
|
return format(obj.type, obj.value.subarray(2 * index, 2 * (index + 1)), obj.le, options);
|
|
1477
|
+
case DdbType.decimal32:
|
|
1478
|
+
case DdbType.decimal64: {
|
|
1479
|
+
const { scale, data } = obj.value;
|
|
1480
|
+
const x = data[index];
|
|
1481
|
+
if (x === nulls.int64 && obj.type === DdbType.decimal64 ||
|
|
1482
|
+
x === nulls.int32 && obj.type === DdbType.decimal32)
|
|
1483
|
+
return '';
|
|
1484
|
+
const s = String(x < 0 ? -x : x).padStart(scale, '0');
|
|
1485
|
+
const str = (x < 0 ? '-' : '') + (scale ? `${s.slice(0, -scale) || '0'}.${s.slice(-scale)}` : s);
|
|
1486
|
+
return options.colors ? str.green : str;
|
|
1487
|
+
}
|
|
1433
1488
|
default:
|
|
1434
1489
|
return format(obj.type, obj.value[index], obj.le, options);
|
|
1435
1490
|
}
|
|
@@ -1844,8 +1899,7 @@ export function datehour2str(datehour, format = 'YYYY.MM.DDTHH') {
|
|
|
1844
1899
|
export function str2timestamp(str, format = 'YYYY.MM.DD HH:mm:ss.SSS') {
|
|
1845
1900
|
if (!str || str === 'null')
|
|
1846
1901
|
return nulls.int64;
|
|
1847
|
-
|
|
1848
|
-
throw new Error('The length of the timestamp string is not equal to the length of the format string');
|
|
1902
|
+
assert('The length of the timestamp string must be equal to the length of the format string');
|
|
1849
1903
|
return BigInt(-timezone_offset +
|
|
1850
1904
|
dayjs(str, format).valueOf());
|
|
1851
1905
|
}
|
|
@@ -1858,12 +1912,10 @@ export function nanotime2str(nanotime, format = 'HH:mm:ss.SSSSSSSSS') {
|
|
|
1858
1912
|
if (nanotime < 0n)
|
|
1859
1913
|
return String(nanotime);
|
|
1860
1914
|
const i_second_start = format.indexOf('ss');
|
|
1861
|
-
|
|
1862
|
-
throw new Error('The format string must contain the format for seconds (ss)');
|
|
1915
|
+
assert(i_second_start !== -1, 'The format string must contain the format for seconds (ss)');
|
|
1863
1916
|
const i_second_end = i_second_start + 2;
|
|
1864
1917
|
const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
|
|
1865
|
-
|
|
1866
|
-
throw new Error('The format string must contain the format for nanoseconds (SSSSSSSSS)');
|
|
1918
|
+
assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
|
|
1867
1919
|
return (dayjs(timezone_offset + (Number(nanotime) / 1000000)).format(format.slice(0, i_second_end)) +
|
|
1868
1920
|
format.slice(i_second_end, i_nanosecond_start) +
|
|
1869
1921
|
String(nanotime % 1000000000n).padStart(9, '0'));
|
|
@@ -1891,12 +1943,10 @@ export function nanotimestamp2str(nanotimestamp, format = 'YYYY.MM.DD HH:mm:ss.S
|
|
|
1891
1943
|
if (nanotimestamp === null || nanotimestamp === nulls.int64)
|
|
1892
1944
|
return 'null';
|
|
1893
1945
|
const i_second_start = format.indexOf('ss');
|
|
1894
|
-
|
|
1895
|
-
throw new Error('The format string must contain the format for seconds (ss)');
|
|
1946
|
+
assert(i_second_start !== -1, 'The format string must contain the format for seconds (ss)');
|
|
1896
1947
|
const i_second_end = i_second_start + 2;
|
|
1897
1948
|
const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
|
|
1898
|
-
|
|
1899
|
-
throw new Error('The format string must contain the format for nanoseconds (SSSSSSSSS)');
|
|
1949
|
+
assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
|
|
1900
1950
|
const remainder = nanotimestamp % 1000000000n;
|
|
1901
1951
|
const borrow = remainder < 0n;
|
|
1902
1952
|
return (dayjs(timezone_offset +
|
|
@@ -1918,15 +1968,12 @@ export function nanotimestamp2str(nanotimestamp, format = 'YYYY.MM.DD HH:mm:ss.S
|
|
|
1918
1968
|
export function str2nanotimestamp(str, format = 'YYYY.MM.DD HH:mm:ss.SSSSSSSSS') {
|
|
1919
1969
|
if (!str || str === 'null')
|
|
1920
1970
|
return nulls.int64;
|
|
1921
|
-
|
|
1922
|
-
throw new Error('nanotimestamp string length is not equal to format string length');
|
|
1971
|
+
assert(str.length === format.length, 'nanotimestamp string length must be equal to format string length');
|
|
1923
1972
|
const i_second_start = format.indexOf('ss');
|
|
1924
|
-
|
|
1925
|
-
throw new Error('The format string must contain the format for seconds (ss)');
|
|
1973
|
+
assert(i_second_start !== -1, 'The format string must contain the format for seconds (ss)');
|
|
1926
1974
|
const i_second_end = i_second_start + 2;
|
|
1927
1975
|
const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
|
|
1928
|
-
|
|
1929
|
-
throw new Error('Format string must contain nanosecond format (SSSSSSSSS)');
|
|
1976
|
+
assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
|
|
1930
1977
|
return (BigInt(-timezone_offset +
|
|
1931
1978
|
dayjs(str.slice(0, i_second_end), format.slice(0, i_second_end)).valueOf()) * 1000000n
|
|
1932
1979
|
+
|
|
@@ -1954,6 +2001,14 @@ export function int1282str(buffer, le = true) {
|
|
|
1954
2001
|
return [...buf].map(x => x.toString(16)
|
|
1955
2002
|
.padStart(2, '0')).join('');
|
|
1956
2003
|
}
|
|
2004
|
+
export const winsize = 40;
|
|
2005
|
+
export class ConnectionError extends Error {
|
|
2006
|
+
ddb;
|
|
2007
|
+
constructor(ddb) {
|
|
2008
|
+
super(`websocket is disconnected: ${ddb.url}`);
|
|
2009
|
+
this.ddb = ddb;
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
1957
2012
|
export class DDB {
|
|
1958
2013
|
/** 当前的 session id (http 或 tcp) */
|
|
1959
2014
|
sid = '0';
|
|
@@ -1977,6 +2032,8 @@ export class DDB {
|
|
|
1977
2032
|
password = '123456';
|
|
1978
2033
|
/** python session flag (2048) */
|
|
1979
2034
|
python = false;
|
|
2035
|
+
/** Whether it is a streaming data connection, this field is always null for non-streaming data */
|
|
2036
|
+
streaming = null;
|
|
1980
2037
|
// --- 内部选项, 状态
|
|
1981
2038
|
print_message_buffer = false;
|
|
1982
2039
|
print_object_buffer = false;
|
|
@@ -2000,11 +2057,12 @@ export class DDB {
|
|
|
2000
2057
|
- username?: DolphinDB username, default `'admin'`
|
|
2001
2058
|
- password?: DolphinDB password, default `'123456'`
|
|
2002
2059
|
- python?: set python session flag, default `false`
|
|
2060
|
+
- streaming?: When this option is set, the WebSocket connection is only used for streaming data
|
|
2003
2061
|
|
|
2004
2062
|
@example
|
|
2005
2063
|
let ddb = new DDB('ws://127.0.0.1:8848')
|
|
2006
2064
|
|
|
2007
|
-
//
|
|
2065
|
+
// Encrypt with HTTPS
|
|
2008
2066
|
let ddbsecure = new DDB('wss://dolphindb.com', {
|
|
2009
2067
|
autologin: true,
|
|
2010
2068
|
username: 'admin',
|
|
@@ -2022,43 +2080,54 @@ export class DDB {
|
|
|
2022
2080
|
this.password = options.password;
|
|
2023
2081
|
if (options.python !== undefined)
|
|
2024
2082
|
this.python = options.python;
|
|
2083
|
+
if (options.streaming !== undefined)
|
|
2084
|
+
this.streaming = options.streaming;
|
|
2025
2085
|
}
|
|
2026
|
-
on_message(event) {
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
this.autologin = options.autologin;
|
|
2040
|
-
if (options.username !== undefined)
|
|
2041
|
-
this.username = options.username;
|
|
2042
|
-
if (options.password !== undefined)
|
|
2043
|
-
this.password = options.password;
|
|
2044
|
-
if (options.python !== undefined)
|
|
2045
|
-
this.python = options.python;
|
|
2046
|
-
this.disconnect();
|
|
2047
|
-
await connect_websocket(this.url, {
|
|
2048
|
-
protocols: this.python ? ['python'] : [],
|
|
2049
|
-
on_open: async (event, websocket) => {
|
|
2050
|
-
this.websocket = websocket;
|
|
2051
|
-
await this.rpc('connect', {});
|
|
2052
|
-
},
|
|
2053
|
-
on_message: (event) => {
|
|
2054
|
-
this.on_message(event);
|
|
2055
|
-
}
|
|
2086
|
+
on_message(event) {
|
|
2087
|
+
// 这里的实现一定会被 connect 中的实现覆盖
|
|
2088
|
+
}
|
|
2089
|
+
/** Establish a actual websocket connection to the DolphindB corresponding to the URL
|
|
2090
|
+
It is a power, and 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
|
|
2091
|
+
this.autologin automatically log in when it is true */
|
|
2092
|
+
async connect() {
|
|
2093
|
+
if (this.connected)
|
|
2094
|
+
return;
|
|
2095
|
+
const ptail = this.pconnect;
|
|
2096
|
+
let resolve;
|
|
2097
|
+
this.pconnect = new Promise((_resolve, _reject) => {
|
|
2098
|
+
resolve = _resolve;
|
|
2056
2099
|
});
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2100
|
+
await ptail;
|
|
2101
|
+
try {
|
|
2102
|
+
if (!this.connected) {
|
|
2103
|
+
this.on_message = (event) => {
|
|
2104
|
+
assert(false, '这是在调用 this.rpc 之前默认的 on_message, 不应该被调用到,除非建立连接后 server 先推送了 message');
|
|
2105
|
+
};
|
|
2106
|
+
this.presult = Promise.resolve(null);
|
|
2107
|
+
this.pnode_run_defined = false;
|
|
2108
|
+
this.websocket = await connect_websocket(this.url, {
|
|
2109
|
+
protocols: (() => {
|
|
2110
|
+
if (this.streaming)
|
|
2111
|
+
return 'streaming';
|
|
2112
|
+
if (this.python)
|
|
2113
|
+
return 'python';
|
|
2114
|
+
})(),
|
|
2115
|
+
on_message: (event) => {
|
|
2116
|
+
this.on_message(event);
|
|
2117
|
+
}
|
|
2118
|
+
});
|
|
2119
|
+
if (this.streaming)
|
|
2120
|
+
await this.subscribe();
|
|
2121
|
+
else {
|
|
2122
|
+
await this.rpc('connect', {});
|
|
2123
|
+
if (this.autologin)
|
|
2124
|
+
await this.call('login', [this.username, this.password], { urgent: true });
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
finally {
|
|
2129
|
+
resolve();
|
|
2130
|
+
}
|
|
2062
2131
|
}
|
|
2063
2132
|
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, } = {}) {
|
|
2064
2133
|
let flag = 0;
|
|
@@ -2094,11 +2163,9 @@ export class DDB {
|
|
|
2094
2163
|
disconnect() {
|
|
2095
2164
|
if (this.connected)
|
|
2096
2165
|
this.websocket.close(1000);
|
|
2097
|
-
this.on_message = () => { };
|
|
2098
|
-
this.presult = Promise.resolve(null);
|
|
2099
|
-
this.pconnect = Promise.resolve();
|
|
2100
2166
|
}
|
|
2101
2167
|
/** rpc through websocket (function/script/variable command)
|
|
2168
|
+
When the DDB is not connected, the call will be automatically connected. When the connection is disconnected, the call will throw the Connectionerror
|
|
2102
2169
|
- type: API 类型: 'script' | 'function' | 'variable'
|
|
2103
2170
|
- options:
|
|
2104
2171
|
- urgent?: 决定 `行为标识` 那一行字符串的取值(只适用于 script 和 function)
|
|
@@ -2108,24 +2175,12 @@ export class DDB {
|
|
|
2108
2175
|
为 false 时返回的 DdbObj 仅含有 buffer 和 le,不做解析,以便后续转发、序列化
|
|
2109
2176
|
*/
|
|
2110
2177
|
async rpc(type, { script, func, args = [], vars = [], urgent, listener, parse_object, }) {
|
|
2111
|
-
if (!this.websocket)
|
|
2112
|
-
|
|
2113
|
-
let resolve;
|
|
2114
|
-
this.pconnect = new Promise((_resolve, _reject) => {
|
|
2115
|
-
resolve = _resolve;
|
|
2116
|
-
});
|
|
2117
|
-
await ptail;
|
|
2118
|
-
try {
|
|
2119
|
-
if (!this.websocket)
|
|
2120
|
-
await this.connect();
|
|
2121
|
-
}
|
|
2122
|
-
finally {
|
|
2123
|
-
resolve();
|
|
2124
|
-
}
|
|
2125
|
-
}
|
|
2178
|
+
if (!this.websocket)
|
|
2179
|
+
await this.connect();
|
|
2126
2180
|
if (!this.connected)
|
|
2127
|
-
throw new
|
|
2181
|
+
throw new ConnectionError(this);
|
|
2128
2182
|
if (func === 'pnode_run' && !this.pnode_run_defined) {
|
|
2183
|
+
// 保证并发调用 rpc 时只定义一次 pnode_run
|
|
2129
2184
|
const ptail = this.ppnoderun;
|
|
2130
2185
|
let resolve;
|
|
2131
2186
|
this.ppnoderun = new Promise((_resolve, _reject) => {
|
|
@@ -2275,8 +2330,7 @@ export class DDB {
|
|
|
2275
2330
|
*/
|
|
2276
2331
|
async call(func, args = [], { urgent, node, nodes, func_type, add_node_alias, listener, parse_object, } = {}) {
|
|
2277
2332
|
if (node) {
|
|
2278
|
-
|
|
2279
|
-
throw new Error('指定 node 时必须设置 func_type');
|
|
2333
|
+
assert(func_type in DdbFunctionType, 'func_type must be set when specifying node');
|
|
2280
2334
|
args = [
|
|
2281
2335
|
node,
|
|
2282
2336
|
new DdbFunction(func, func_type),
|
|
@@ -2313,10 +2367,21 @@ export class DDB {
|
|
|
2313
2367
|
vars,
|
|
2314
2368
|
/** Uploaded variables' value */
|
|
2315
2369
|
args, { listener, parse_object, } = {}) {
|
|
2316
|
-
|
|
2317
|
-
throw new Error('variable command parameter is empty or parameter name is empty, or the number does not match');
|
|
2370
|
+
assert(args.length && args.length === vars.length, 'variable command parameter cannot be empty and parameter name cannot be empty, and the number should match');
|
|
2318
2371
|
return this.rpc('variable', { vars, args, listener, parse_object });
|
|
2319
2372
|
}
|
|
2373
|
+
/** Cancel all console jobs corresponding to the current session id */
|
|
2374
|
+
async cancel() {
|
|
2375
|
+
let ddb = new DDB(this.url, this);
|
|
2376
|
+
try {
|
|
2377
|
+
await ddb.eval(`jobs = exec rootJobId from getConsoleJobs() where sessionId = ${this.sid}\n` +
|
|
2378
|
+
'if (size(jobs))\n' +
|
|
2379
|
+
' cancelConsoleJob(jobs)\n', { urgent: true });
|
|
2380
|
+
}
|
|
2381
|
+
finally {
|
|
2382
|
+
ddb.disconnect();
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2320
2385
|
/** 解析服务端响应报文,返回去掉 header 的 data buf */
|
|
2321
2386
|
parse_message(buf, parse_object = this.parse_object) {
|
|
2322
2387
|
// MSG\n
|
|
@@ -2331,8 +2396,8 @@ export class DDB {
|
|
|
2331
2396
|
// 'OK\n'
|
|
2332
2397
|
// '\x04\x00\x02\x00\x00\x00'
|
|
2333
2398
|
/** index of line feed 0 */
|
|
2334
|
-
const
|
|
2335
|
-
const parts = this.dec.decode(buf.subarray(0,
|
|
2399
|
+
const ilf0 = buf.indexOf(0x0a); // '\n'
|
|
2400
|
+
const parts = this.dec.decode(buf.subarray(0, ilf0)).split(' ');
|
|
2336
2401
|
/** session id */
|
|
2337
2402
|
const sid = parts[0];
|
|
2338
2403
|
if (sid !== this.sid) {
|
|
@@ -2340,34 +2405,141 @@ export class DDB {
|
|
|
2340
2405
|
this.sid = sid;
|
|
2341
2406
|
}
|
|
2342
2407
|
/** 返回对象的数量 */
|
|
2343
|
-
const
|
|
2408
|
+
const nobj = Number(parts[1]);
|
|
2344
2409
|
/** 大小端: 协议中大端为 0, 小端为 1 */
|
|
2345
2410
|
this.le = Number(parts[2]) !== 0;
|
|
2346
|
-
const
|
|
2347
|
-
const
|
|
2411
|
+
const ils1 = ilf0 + 1;
|
|
2412
|
+
const ilf1 = buf.indexOf(0x0a, ils1); // '\n'
|
|
2348
2413
|
/** 'OK' 表示成功,其它文本表示失败 */
|
|
2349
|
-
const message = this.dec.decode(buf.subarray(
|
|
2414
|
+
const message = this.dec.decode(buf.subarray(ils1, ilf1));
|
|
2350
2415
|
if (message !== 'OK')
|
|
2351
2416
|
return {
|
|
2352
2417
|
type: 'error',
|
|
2353
2418
|
data: new Error(message)
|
|
2354
2419
|
};
|
|
2355
|
-
const
|
|
2420
|
+
const bufobj = buf.subarray(ilf1 + 1);
|
|
2356
2421
|
if (this.print_object_buffer)
|
|
2357
|
-
console.log(typed_array_to_buffer(
|
|
2422
|
+
console.log(typed_array_to_buffer(bufobj));
|
|
2358
2423
|
return {
|
|
2359
2424
|
type: 'object',
|
|
2360
2425
|
data: parse_object ?
|
|
2361
|
-
DdbObj.parse(
|
|
2426
|
+
DdbObj.parse(bufobj, this.le)
|
|
2362
2427
|
:
|
|
2363
2428
|
new DdbObj({
|
|
2364
2429
|
form: DdbForm.scalar,
|
|
2365
2430
|
type: DdbType.void,
|
|
2366
2431
|
length: 0,
|
|
2367
2432
|
le: this.le,
|
|
2368
|
-
buffer:
|
|
2433
|
+
buffer: bufobj,
|
|
2369
2434
|
})
|
|
2370
2435
|
};
|
|
2371
2436
|
}
|
|
2437
|
+
/** Internal stream subscription method */
|
|
2438
|
+
async subscribe() {
|
|
2439
|
+
const args = DdbObj.to_ddbobjs([
|
|
2440
|
+
new DdbVectorString(this.autologin ?
|
|
2441
|
+
[this.username, this.password]
|
|
2442
|
+
:
|
|
2443
|
+
// 不自动登录也要传空的 string vector, 否则 server 无法解析
|
|
2444
|
+
[]),
|
|
2445
|
+
// todo: 还有用吗?需要获取客户端 IP 吗
|
|
2446
|
+
'localhost',
|
|
2447
|
+
// todo: 端口还有用吗?
|
|
2448
|
+
new DdbInt(0),
|
|
2449
|
+
this.streaming.table,
|
|
2450
|
+
(this.streaming.action ||= `api_js_${new Date().getTime()}`),
|
|
2451
|
+
// new DdbInt(-1), // offset
|
|
2452
|
+
// filter
|
|
2453
|
+
// allow exists
|
|
2454
|
+
]);
|
|
2455
|
+
return new Promise((resolve, reject) => {
|
|
2456
|
+
let first = true;
|
|
2457
|
+
let second = true;
|
|
2458
|
+
// 先准备好收到 websocket message 的 callback
|
|
2459
|
+
this.on_message = ({ data: buffer }) => {
|
|
2460
|
+
if (first) {
|
|
2461
|
+
first = false;
|
|
2462
|
+
try {
|
|
2463
|
+
const dv = new DataView(buffer);
|
|
2464
|
+
const status = dv.getUint8(1);
|
|
2465
|
+
this.le = Boolean(dv.getUint8(2));
|
|
2466
|
+
const nobjs = dv.getUint32(3, this.le);
|
|
2467
|
+
// 解析返回数据,从 server 实现看其实只有一个 DdbObj
|
|
2468
|
+
let offset = 7;
|
|
2469
|
+
let objs = new Array(nobjs);
|
|
2470
|
+
const buf = new Uint8Array(buffer);
|
|
2471
|
+
for (let i = 0; i < nobjs; i++) {
|
|
2472
|
+
const obj = DdbObj.parse(buf.subarray(offset), this.le);
|
|
2473
|
+
offset += obj.length;
|
|
2474
|
+
objs[i] = obj;
|
|
2475
|
+
console.log(`订阅流表的响应数据: objs[${i}]:`, obj);
|
|
2476
|
+
}
|
|
2477
|
+
this.streaming.colnames = objs[0].value;
|
|
2478
|
+
if (status === StreamingStatusCode.ok)
|
|
2479
|
+
console.log('Subscribed to streaming table:', { status: StreamingStatusCode[status], result: objs[0] });
|
|
2480
|
+
else
|
|
2481
|
+
reject(new Error(`Failed to subscribe to streaming table: { status: ${StreamingStatusCode[status]}, error: ${objs[0]?.value} }`));
|
|
2482
|
+
}
|
|
2483
|
+
catch (error) {
|
|
2484
|
+
reject(error);
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
else {
|
|
2488
|
+
try {
|
|
2489
|
+
const dv = new DataView(buffer);
|
|
2490
|
+
const buf = new Uint8Array(buffer);
|
|
2491
|
+
let { streaming } = this;
|
|
2492
|
+
streaming.time = dv.getBigInt64(1, this.le);
|
|
2493
|
+
streaming.id = dv.getBigInt64(9, this.le);
|
|
2494
|
+
const i_topic_end = buf.indexOf(0, 17);
|
|
2495
|
+
streaming.topic = this.dec.decode(buf.subarray(17, i_topic_end));
|
|
2496
|
+
if (second) {
|
|
2497
|
+
second = false;
|
|
2498
|
+
// 是 table
|
|
2499
|
+
streaming.schema = DdbObj.parse(buf.subarray(i_topic_end + 1), this.le);
|
|
2500
|
+
console.log('Received streaming table schema:', streaming.schema);
|
|
2501
|
+
streaming.window = {
|
|
2502
|
+
offset: 0,
|
|
2503
|
+
rows: 0,
|
|
2504
|
+
segments: [],
|
|
2505
|
+
};
|
|
2506
|
+
assert(streaming.schema.rows === 0, 'schema.rows === 0');
|
|
2507
|
+
resolve(streaming.schema);
|
|
2508
|
+
}
|
|
2509
|
+
else {
|
|
2510
|
+
// 是 column 片段组成的 any vector
|
|
2511
|
+
const data = streaming.data = DdbObj.parse(buf.subarray(i_topic_end + 1), this.le);
|
|
2512
|
+
let { window: win } = streaming;
|
|
2513
|
+
win.rows += data.value[0].rows;
|
|
2514
|
+
win.segments.push(data);
|
|
2515
|
+
if (win.rows >= winsize * 2 && win.segments.length >= 2) {
|
|
2516
|
+
let winsize_ = 0;
|
|
2517
|
+
let i = win.segments.length - 1;
|
|
2518
|
+
// 往前移动至首个累计 winsize_ 超过 winsize 的位置
|
|
2519
|
+
for (; winsize_ < winsize; i--)
|
|
2520
|
+
winsize_ += win.segments[i].rows;
|
|
2521
|
+
win.segments = win.segments.slice(i);
|
|
2522
|
+
win.offset += win.rows - winsize_;
|
|
2523
|
+
win.rows = winsize_;
|
|
2524
|
+
console.log('exceeds winsize, shrink window.segments', win);
|
|
2525
|
+
}
|
|
2526
|
+
streaming.handler(streaming);
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
catch (error) {
|
|
2530
|
+
reject(error);
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
};
|
|
2534
|
+
// 发送订阅 websocket message
|
|
2535
|
+
this.websocket.send(concat([
|
|
2536
|
+
Uint8Array.of(1, // from = SubscriberFromType::FROM_API
|
|
2537
|
+
1, // type = SubscriberRPCType::RPC_START
|
|
2538
|
+
Number(DDB.le_client)),
|
|
2539
|
+
Uint32Array.of(args.length),
|
|
2540
|
+
...args.map(obj => obj.pack())
|
|
2541
|
+
]));
|
|
2542
|
+
});
|
|
2543
|
+
}
|
|
2372
2544
|
}
|
|
2373
2545
|
//# sourceMappingURL=index.js.map
|