dolphindb 2.0.800 → 2.0.900

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -117,15 +117,6 @@ export const nulls = {
117
117
  double: -Number.MAX_VALUE,
118
118
  bytes16: Uint8Array.from(new Array(16).fill(0))
119
119
  };
120
- export const timezone_offset = 1000 * 60 * new Date().getTimezoneOffset();
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
120
  /** Can represent data types in all DolphinDB databases */
130
121
  export class DdbObj {
131
122
  static dec = new TextDecoder('utf-8');
@@ -521,8 +512,37 @@ export class DdbObj {
521
512
  // 00 reserved
522
513
  // 70 11 01 00 block.lengths = [70000 (0x00011170)]
523
514
  // 01 00 00 00 02 00 00 00 ... 279992 more bytes> block.data
515
+ // decimal array vector
516
+ // a = array(DECIMAL32(2)[], 0, 10)
517
+ // append!(a, [
518
+ // [1, 2, 3],
519
+ // [4, 5],
520
+ // [6, 7, 8],
521
+ // [9, 10]
522
+ // ])
523
+ // print(a)
524
+ // a
525
+ // [[1.00,2.00,3.00],[4.00,5.00],[6.00,7.00,8.00],[9.00,10.00]]
526
+ // <Buffer 65 01 type = 101 = 64 + 37 (decimal32) , form = vector
527
+ // 04 00 00 00 0a 00 00 00 rows = 4, cols = 10
528
+ // scale (只有 decimal32 才有)
529
+ // 02 00 00 00 scale = 2
530
+ // block 0
531
+ // 04 00 block.rows = 4
532
+ // 01 block.unit = 1
533
+ // 00 reserved
534
+ // 03 02 03 02 block.lengths = [3, 2, 3, 2]
535
+ // 64 00 00 00 c8 00 00 00 2c 01 00 00 90 01 00 00 f4 01 00 00 58 02 00 00 bc 02 00 00 ... 12 more bytes> [100, 200, 300, ...]
536
+ // block 1
537
+ // ...
538
+ const type_ = type - 64;
524
539
  const cols = dv.getUint32(4, le);
525
540
  let blocks = [];
541
+ // decimal32/64 会在所有 blocks 之前多一个 scale
542
+ if (type_ === DdbType.decimal32 || type_ === DdbType.decimal64) {
543
+ blocks.scale = dv.getInt32(i_items_start, le);
544
+ i_items_start += 4;
545
+ }
526
546
  let i_block_start = i_items_start;
527
547
  // 解析一个 block
528
548
  for (let i_row = 0; i_row < rows;) {
@@ -551,7 +571,20 @@ export class DdbObj {
551
571
  let total_length = 0;
552
572
  for (const x of lengths)
553
573
  total_length += x;
554
- const [len_items, data] = this.parse_vector_items(buf.subarray(i_data_start), le, type - 64, total_length);
574
+ let len_items;
575
+ let data;
576
+ switch (type_) {
577
+ case DdbType.decimal32:
578
+ len_items = total_length * 4;
579
+ data = new Int32Array(buf.buffer.slice(buf.byteOffset + i_data_start, buf.byteOffset + i_data_start + len_items));
580
+ break;
581
+ case DdbType.decimal64:
582
+ len_items = total_length * 8;
583
+ data = new BigInt64Array(buf.buffer.slice(buf.byteOffset + i_data_start, buf.byteOffset + i_data_start + len_items));
584
+ break;
585
+ default:
586
+ [len_items, data] = this.parse_vector_items(buf.subarray(i_data_start), le, type - 64, total_length);
587
+ }
555
588
  blocks.push({
556
589
  unit,
557
590
  rows,
@@ -866,6 +899,10 @@ export class DdbObj {
866
899
  if (form === DdbForm.vector && 64 <= type && type < 128)
867
900
  return [
868
901
  Uint32Array.of(this.rows, this.cols),
902
+ ...(type - 64 === DdbType.decimal32 || type - 64 === DdbType.decimal64) ?
903
+ [Int32Array.of(this.value.scale)]
904
+ :
905
+ [],
869
906
  ...this.value.map(block => ([
870
907
  Uint16Array.of(block.rows),
871
908
  Uint8Array.of(block.unit, 0),
@@ -924,10 +961,7 @@ export class DdbObj {
924
961
  })();
925
962
  if (!body)
926
963
  return new Uint8Array(0);
927
- return concat([
928
- header,
929
- ...body
930
- ]);
964
+ return concat([header, ...body]);
931
965
  }
932
966
  static pack_vector_body(value, type, length) {
933
967
  switch (type) {
@@ -1017,7 +1051,8 @@ export class DdbObj {
1017
1051
  throw new Error(`vector ${DdbType[type]} 暂不支持序列化`);
1018
1052
  }
1019
1053
  }
1020
- [inspect.custom](depth, options, _inspect) {
1054
+ [inspect.custom](depth, _options, _inspect) {
1055
+ const options = { nullstr: true, quote: true, ..._options };
1021
1056
  const type = this.inspect_type();
1022
1057
  const data = (() => {
1023
1058
  switch (this.form) {
@@ -1053,7 +1088,18 @@ export class DdbObj {
1053
1088
  for (const length of lengths) {
1054
1089
  let _items = new Array(Math.min(limit, length));
1055
1090
  for (let i = 0; i < _items.length; i++)
1056
- _items[i] = format(_type, data[acc_len + i], this.le, options);
1091
+ if (_type === DdbType.decimal32 || _type === DdbType.decimal64) {
1092
+ const x = data[acc_len + i];
1093
+ if (x === nulls.int64 && _type === DdbType.decimal64 ||
1094
+ x === nulls.int32 && _type === DdbType.decimal32)
1095
+ return '';
1096
+ const { scale } = this.value;
1097
+ const s = String(x < 0 ? -x : x).padStart(scale, '0');
1098
+ const str = (x < 0 ? '-' : '') + (scale ? `${s.slice(0, -scale) || '0'}.${s.slice(-scale)}` : s);
1099
+ _items[i] = options.colors ? str.green : str;
1100
+ }
1101
+ else
1102
+ _items[i] = format(_type, data[acc_len + i], this.le, options);
1057
1103
  items[i_items++] = format_array(_items, length > limit);
1058
1104
  acc_len += length;
1059
1105
  }
@@ -1217,8 +1263,8 @@ let _decimals = 20;
1217
1263
  let _formatter = Intl.NumberFormat('en-US', { maximumFractionDigits: 20 });
1218
1264
  /** Formats a single element (value) as a string according to DdbType, null returns a 'null' string */
1219
1265
  export function format(type, value, le, options = {}) {
1266
+ const { decimals } = options;
1220
1267
  const formatter = (() => {
1221
- const { decimals } = options;
1222
1268
  if (decimals === undefined || decimals === null)
1223
1269
  return default_formatter;
1224
1270
  if (decimals !== _decimals) {
@@ -1272,7 +1318,7 @@ export function format(type, value, le, options = {}) {
1272
1318
  inspect(null, options)
1273
1319
  :
1274
1320
  options.colors ?
1275
- date2str(value).green
1321
+ date2str(value).magenta
1276
1322
  :
1277
1323
  date2str(value);
1278
1324
  case DdbType.month:
@@ -1280,7 +1326,7 @@ export function format(type, value, le, options = {}) {
1280
1326
  inspect(null, options)
1281
1327
  :
1282
1328
  options.colors ?
1283
- month2str(value).green
1329
+ month2str(value).magenta
1284
1330
  :
1285
1331
  month2str(value);
1286
1332
  case DdbType.time:
@@ -1288,7 +1334,7 @@ export function format(type, value, le, options = {}) {
1288
1334
  inspect(null, options)
1289
1335
  :
1290
1336
  options.colors ?
1291
- time2str(value).green
1337
+ time2str(value).magenta
1292
1338
  :
1293
1339
  time2str(value);
1294
1340
  case DdbType.minute:
@@ -1296,7 +1342,7 @@ export function format(type, value, le, options = {}) {
1296
1342
  inspect(null, options)
1297
1343
  :
1298
1344
  options.colors ?
1299
- minute2str(value).green
1345
+ minute2str(value).magenta
1300
1346
  :
1301
1347
  minute2str(value);
1302
1348
  case DdbType.second:
@@ -1304,7 +1350,7 @@ export function format(type, value, le, options = {}) {
1304
1350
  inspect(null, options)
1305
1351
  :
1306
1352
  options.colors ?
1307
- second2str(value).green
1353
+ second2str(value).magenta
1308
1354
  :
1309
1355
  second2str(value);
1310
1356
  case DdbType.datetime:
@@ -1312,7 +1358,7 @@ export function format(type, value, le, options = {}) {
1312
1358
  inspect(null, options)
1313
1359
  :
1314
1360
  options.colors ?
1315
- datetime2str(value).green
1361
+ datetime2str(value).magenta
1316
1362
  :
1317
1363
  datetime2str(value);
1318
1364
  case DdbType.timestamp:
@@ -1320,7 +1366,7 @@ export function format(type, value, le, options = {}) {
1320
1366
  inspect(null, options)
1321
1367
  :
1322
1368
  options.colors ?
1323
- timestamp2str(value).green
1369
+ timestamp2str(value).magenta
1324
1370
  :
1325
1371
  timestamp2str(value);
1326
1372
  case DdbType.nanotime:
@@ -1328,7 +1374,7 @@ export function format(type, value, le, options = {}) {
1328
1374
  inspect(null, options)
1329
1375
  :
1330
1376
  options.colors ?
1331
- nanotime2str(value).green
1377
+ nanotime2str(value).magenta
1332
1378
  :
1333
1379
  nanotime2str(value);
1334
1380
  case DdbType.nanotimestamp:
@@ -1336,7 +1382,7 @@ export function format(type, value, le, options = {}) {
1336
1382
  inspect(null, options)
1337
1383
  :
1338
1384
  options.colors ?
1339
- nanotimestamp2str(value).green
1385
+ nanotimestamp2str(value).magenta
1340
1386
  :
1341
1387
  nanotimestamp2str(value);
1342
1388
  case DdbType.float:
@@ -1360,7 +1406,7 @@ export function format(type, value, le, options = {}) {
1360
1406
  return inspect(value, options);
1361
1407
  case DdbType.uuid:
1362
1408
  return options.colors ?
1363
- uuid2str(value, le).green
1409
+ uuid2str(value, le).cyan
1364
1410
  :
1365
1411
  uuid2str(value, le);
1366
1412
  case DdbType.functiondef:
@@ -1374,12 +1420,12 @@ export function format(type, value, le, options = {}) {
1374
1420
  'null'
1375
1421
  :
1376
1422
  options.colors ?
1377
- datehour2str(value).green
1423
+ datehour2str(value).magenta
1378
1424
  :
1379
1425
  datehour2str(value);
1380
1426
  case DdbType.ipaddr:
1381
1427
  return options.colors ?
1382
- ipaddr2str(value, le).green
1428
+ ipaddr2str(value, le).cyan
1383
1429
  :
1384
1430
  ipaddr2str(value, le);
1385
1431
  case DdbType.int128:
@@ -1403,7 +1449,7 @@ export function format(type, value, le, options = {}) {
1403
1449
  case DdbType.duration: {
1404
1450
  const { data, unit } = value;
1405
1451
  const str = `${data}${DdbDurationUnit[unit]}`;
1406
- return options.colors ? str.green : str;
1452
+ return options.colors ? str.magenta : str;
1407
1453
  }
1408
1454
  case DdbType.decimal32:
1409
1455
  case DdbType.decimal64: {
@@ -1422,6 +1468,7 @@ export function format(type, value, le, options = {}) {
1422
1468
  }
1423
1469
  /** formatted vector, the index-th item in the collection is a string, a null value returns a 'null' string */
1424
1470
  export function formati(obj, index, options = {}) {
1471
+ assert(index < obj.rows, 'index < obj.rows');
1425
1472
  if (64 <= obj.type && obj.type < 128) { // array vector
1426
1473
  // 因为 array vector 目前只支持:Logical, Integral(不包括 INT128, COMPRESS 类型), Floating, Temporal
1427
1474
  // 都对应 TypedArray 中的一格,所以 lengths.length 等于 block 中的 row 的个数
@@ -1449,18 +1496,26 @@ export function formati(obj, index, options = {}) {
1449
1496
  const limit = 10;
1450
1497
  let items = new Array(Math.min(limit, length));
1451
1498
  for (let i = 0; i < items.length; i++)
1452
- items[i] = format(_type, data[acc_len + i], obj.le, options);
1499
+ if (_type === DdbType.decimal32 || _type === DdbType.decimal64) {
1500
+ const x = data[acc_len + i];
1501
+ if (x === nulls.int64 && _type === DdbType.decimal64 ||
1502
+ x === nulls.int32 && _type === DdbType.decimal32)
1503
+ return '';
1504
+ const { scale } = obj.value;
1505
+ const s = String(x < 0 ? -x : x).padStart(scale, '0');
1506
+ const str = (x < 0 ? '-' : '') + (scale ? `${s.slice(0, -scale) || '0'}.${s.slice(-scale)}` : s);
1507
+ items[i] = options.colors ? str.green : str;
1508
+ }
1509
+ else
1510
+ items[i] = format(_type, data[acc_len + i], obj.le, options);
1453
1511
  return (items.join(', ') + (length > limit ? ', ...' : '')).bracket('square');
1454
1512
  }
1455
1513
  }
1456
1514
  }
1457
1515
  switch (obj.type) {
1458
- case DdbType.string:
1459
- case DdbType.symbol:
1460
- return obj.value[index];
1461
1516
  case DdbType.symbol_extended: {
1462
1517
  const { base, data } = obj.value;
1463
- return base[data[index]];
1518
+ return format(DdbType.string, base[data[index]], obj.le, options);
1464
1519
  }
1465
1520
  case DdbType.uuid:
1466
1521
  case DdbType.int128:
@@ -1788,10 +1843,14 @@ export class DdbTable extends DdbObj {
1788
1843
  }
1789
1844
  }
1790
1845
  export function date2ms(date) {
1846
+ // 将 server 的本地时间 (以 ms 为单位,1970.01.01 00:00:00 作为零点) 当成是客户端这里的本地的时间,然后根据本地的时区信息转换为 utc 时间
1847
+ // 本地的时区与实际的时间值相关,timezone offset 可能会受到夏令时 (DST) 的影响
1848
+ // 得到的 utc 毫秒数交给 js date 或者 dayjs 去格式化
1849
+ const ms = 1000 * 3600 * 24 * date;
1791
1850
  return (date === null || date === nulls.int32) ?
1792
1851
  null
1793
1852
  :
1794
- timezone_offset + 1000 * 3600 * 24 * date;
1853
+ 1000 * 60 * new Date(ms).getTimezoneOffset() + ms;
1795
1854
  }
1796
1855
  export function date2str(date, format = 'YYYY.MM.DD') {
1797
1856
  return (date === null || date === nulls.int32) ?
@@ -1818,7 +1877,7 @@ export function time2ms(time) {
1818
1877
  return (time === null || time === nulls.int32) ?
1819
1878
  null
1820
1879
  :
1821
- timezone_offset + time;
1880
+ 1000 * 60 * new Date(time).getTimezoneOffset() + time;
1822
1881
  }
1823
1882
  export function time2str(time, format = 'HH:mm:ss.SSS') {
1824
1883
  return (time === null || time === nulls.int32) ?
@@ -1827,10 +1886,11 @@ export function time2str(time, format = 'HH:mm:ss.SSS') {
1827
1886
  dayjs(time2ms(time)).format(format);
1828
1887
  }
1829
1888
  export function minute2ms(minute) {
1889
+ const ms = 60 * 1000 * minute;
1830
1890
  return (minute === null || minute === nulls.int32) ?
1831
1891
  null
1832
1892
  :
1833
- timezone_offset + 60 * 1000 * minute;
1893
+ 1000 * 60 * new Date(ms).getTimezoneOffset() + ms;
1834
1894
  }
1835
1895
  export function minute2str(minute, format = 'HH:mm[m]') {
1836
1896
  return (minute === null || minute === nulls.int32) ?
@@ -1839,10 +1899,11 @@ export function minute2str(minute, format = 'HH:mm[m]') {
1839
1899
  dayjs(minute2ms(minute)).format(format);
1840
1900
  }
1841
1901
  export function second2ms(second) {
1902
+ const ms = 1000 * second;
1842
1903
  return (second === null || second === nulls.int32) ?
1843
1904
  null
1844
1905
  :
1845
- timezone_offset + 1000 * second;
1906
+ 1000 * 60 * new Date(ms).getTimezoneOffset() + ms;
1846
1907
  }
1847
1908
  export function second2str(second, format = 'HH:mm:ss') {
1848
1909
  return (second === null || second === nulls.int32) ?
@@ -1851,10 +1912,11 @@ export function second2str(second, format = 'HH:mm:ss') {
1851
1912
  dayjs(second2ms(second)).format(format);
1852
1913
  }
1853
1914
  export function datetime2ms(datetime) {
1915
+ const ms = 1000 * datetime;
1854
1916
  return (datetime === null || datetime === nulls.int32) ?
1855
1917
  null
1856
1918
  :
1857
- timezone_offset + 1000 * datetime;
1919
+ 1000 * 60 * new Date(ms).getTimezoneOffset() + ms;
1858
1920
  }
1859
1921
  export function datetime2str(datetime, format = 'YYYY.MM.DD HH:mm:ss') {
1860
1922
  return (datetime === null || datetime === nulls.int32) ?
@@ -1863,10 +1925,11 @@ export function datetime2str(datetime, format = 'YYYY.MM.DD HH:mm:ss') {
1863
1925
  dayjs(datetime2ms(datetime)).format(format);
1864
1926
  }
1865
1927
  export function timestamp2ms(timestamp) {
1928
+ const ms = Number(timestamp);
1866
1929
  return (timestamp === null || timestamp === nulls.int64) ?
1867
1930
  null
1868
1931
  :
1869
- timezone_offset + Number(timestamp);
1932
+ 1000 * 60 * new Date(ms).getTimezoneOffset() + ms;
1870
1933
  }
1871
1934
  /** format timestamp (bigint) to string
1872
1935
  - timestamp: bigint value
@@ -1881,16 +1944,18 @@ export function timestamp2str(timestamp, format = 'YYYY.MM.DD HH:mm:ss.SSS') {
1881
1944
  dayjs(timestamp2ms(timestamp)).format(format);
1882
1945
  }
1883
1946
  export function datehour2ms(datehour) {
1947
+ const ms = 1000 * 3600 * datehour;
1884
1948
  return (datehour === null || datehour === nulls.int32) ?
1885
1949
  null
1886
1950
  :
1887
- timezone_offset + 1000 * 3600 * datehour;
1951
+ 1000 * 60 * new Date(ms).getTimezoneOffset() + ms;
1888
1952
  }
1889
1953
  export function datehour2str(datehour, format = 'YYYY.MM.DDTHH') {
1954
+ const ms = 1000 * 3600 * datehour;
1890
1955
  return (datehour === null || datehour === nulls.int32) ?
1891
1956
  'null'
1892
1957
  :
1893
- dayjs(timezone_offset + 1000 * 3600 * datehour).format(format);
1958
+ dayjs(1000 * 60 * new Date(ms).getTimezoneOffset() + ms).format(format);
1894
1959
  }
1895
1960
  /** parse timestamp string to bigint value
1896
1961
  - str: timestamp string, If it is an empty string or 'null', it will return the corresponding empty value (nulls.int64)
@@ -1901,9 +1966,10 @@ export function datehour2str(datehour, format = 'YYYY.MM.DDTHH') {
1901
1966
  export function str2timestamp(str, format = 'YYYY.MM.DD HH:mm:ss.SSS') {
1902
1967
  if (!str || str === 'null')
1903
1968
  return nulls.int64;
1904
- assert('The length of the timestamp string must be equal to the length of the format string');
1905
- return BigInt(-timezone_offset +
1906
- dayjs(str, format).valueOf());
1969
+ assert(str.length === format.length, 'The length of the timestamp string must be equal to the length of the format string');
1970
+ const ms = dayjs(str, format).valueOf();
1971
+ return BigInt(-(1000 * 60 * new Date(ms).getTimezoneOffset()) +
1972
+ ms);
1907
1973
  }
1908
1974
  export function nanotime2ns(nanotime) {
1909
1975
  return nanotimestamp2ns(nanotime);
@@ -1918,15 +1984,17 @@ export function nanotime2str(nanotime, format = 'HH:mm:ss.SSSSSSSSS') {
1918
1984
  const i_second_end = i_second_start + 2;
1919
1985
  const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
1920
1986
  assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
1921
- return (dayjs(timezone_offset + (Number(nanotime) / 1000000)).format(format.slice(0, i_second_end)) +
1987
+ const ms = Number(nanotime) / 1000000;
1988
+ return (dayjs(1000 * 60 * new Date(ms).getTimezoneOffset() + ms).format(format.slice(0, i_second_end)) +
1922
1989
  format.slice(i_second_end, i_nanosecond_start) +
1923
1990
  String(nanotime % 1000000000n).padStart(9, '0'));
1924
1991
  }
1925
1992
  export function nanotimestamp2ns(nanotimestamp) {
1993
+ const ms = Number(nanotimestamp / 1000000n);
1926
1994
  return (nanotimestamp === null || nanotimestamp === nulls.int64) ?
1927
1995
  null
1928
1996
  :
1929
- BigInt(timezone_offset) * 1000000n + nanotimestamp;
1997
+ BigInt(1000 * 60 * new Date(ms).getTimezoneOffset()) * 1000000n + nanotimestamp;
1930
1998
  }
1931
1999
  /** format nanotimestamp value (bigint) to string
1932
2000
  - nanotimestamp: bigint value
@@ -1951,7 +2019,8 @@ export function nanotimestamp2str(nanotimestamp, format = 'YYYY.MM.DD HH:mm:ss.S
1951
2019
  assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
1952
2020
  const remainder = nanotimestamp % 1000000000n;
1953
2021
  const borrow = remainder < 0n;
1954
- return (dayjs(timezone_offset +
2022
+ const ms = Number(nanotimestamp / 1000000n);
2023
+ return (dayjs(1000 * 60 * new Date(ms).getTimezoneOffset() +
1955
2024
  // 去掉 9 位的纳秒部分,转化为毫秒
1956
2025
  Number((nanotimestamp - remainder + (borrow ? -1000000000n : 0n)) / 1000000n)).format(format.slice(0, i_second_end)) +
1957
2026
  format.slice(i_second_end, i_nanosecond_start) +
@@ -1976,8 +2045,9 @@ export function str2nanotimestamp(str, format = 'YYYY.MM.DD HH:mm:ss.SSSSSSSSS')
1976
2045
  const i_second_end = i_second_start + 2;
1977
2046
  const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
1978
2047
  assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
1979
- return (BigInt(-timezone_offset +
1980
- dayjs(str.slice(0, i_second_end), format.slice(0, i_second_end)).valueOf()) * 1000000n
2048
+ const ms = dayjs(str.slice(0, i_second_end), format.slice(0, i_second_end)).valueOf();
2049
+ return (BigInt(-(1000 * 60 * new Date(ms).getTimezoneOffset()) +
2050
+ ms) * 1000000n
1981
2051
  +
1982
2052
  BigInt(str.slice(i_nanosecond_start, i_nanosecond_start + 9)));
1983
2053
  }
@@ -2003,7 +2073,14 @@ export function int1282str(buffer, le = true) {
2003
2073
  return [...buf].map(x => x.toString(16)
2004
2074
  .padStart(2, '0')).join('');
2005
2075
  }
2006
- export const winsize = 40;
2076
+ export const winsize = 10_0000;
2077
+ export class ConnectionError extends Error {
2078
+ ddb;
2079
+ constructor(ddb) {
2080
+ super(`websocket is disconnected: ${ddb.url}`);
2081
+ this.ddb = ddb;
2082
+ }
2083
+ }
2007
2084
  export class DDB {
2008
2085
  /** 当前的 session id (http 或 tcp) */
2009
2086
  sid = '0';
@@ -2079,52 +2156,50 @@ export class DDB {
2079
2156
  this.streaming = options.streaming;
2080
2157
  }
2081
2158
  on_message(event) {
2082
- 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');
2083
- console.log(event.data);
2159
+ // 这里的实现一定会被 connect 中的实现覆盖
2084
2160
  }
2085
- /** Establish the actual WebSocket connection to the DolphinDB corresponding to the URL
2086
- - options?:
2087
- - url?: DolphinDB WebSocket URL. By default, the WebSocket URL passed in when the instance is initialized is used
2088
- - autologin?: Whether to log in automatically after establishing a connection, default `true`
2089
- - username?: DolphinDB username, default `'admin'`
2090
- - password?: DolphinDB password, default `'123456'`
2091
- - python?: set python session flag, default `false`
2092
- - streaming?: When this option is set, the WebSocket connection is only used for streaming data
2093
- */
2094
- async connect({ url, autologin, username, password, python, streaming } = {}) {
2095
- if (url !== undefined)
2096
- this.url = url;
2097
- if (autologin !== undefined)
2098
- this.autologin = autologin;
2099
- if (username !== undefined)
2100
- this.username = username;
2101
- if (password !== undefined)
2102
- this.password = password;
2103
- if (python !== undefined)
2104
- this.python = python;
2105
- if (streaming !== undefined)
2106
- this.streaming = streaming;
2107
- this.disconnect();
2108
- await connect_websocket(this.url, {
2109
- protocols: (() => {
2110
- if (this.streaming)
2111
- return 'streaming';
2112
- if (this.python)
2113
- return 'python';
2114
- })(),
2115
- on_open: async (event, websocket) => {
2116
- this.websocket = websocket;
2161
+ /** Establish a actual websocket connection to the DolphindB corresponding to the URL
2162
+ 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
2163
+ this.autologin automatically log in when it is true */
2164
+ async connect() {
2165
+ if (this.connected)
2166
+ return;
2167
+ const ptail = this.pconnect;
2168
+ let resolve;
2169
+ this.pconnect = new Promise((_resolve, _reject) => {
2170
+ resolve = _resolve;
2171
+ });
2172
+ await ptail;
2173
+ try {
2174
+ if (!this.connected) {
2175
+ this.on_message = (event) => {
2176
+ assert(false, '这是在调用 this.rpc 之前默认的 on_message, 不应该被调用到,除非建立连接后 server 先推送了 message');
2177
+ };
2178
+ this.presult = Promise.resolve(null);
2179
+ this.pnode_run_defined = false;
2180
+ this.websocket = await connect_websocket(this.url, {
2181
+ protocols: (() => {
2182
+ if (this.streaming)
2183
+ return 'streaming';
2184
+ if (this.python)
2185
+ return 'python';
2186
+ })(),
2187
+ on_message: (event) => {
2188
+ this.on_message(event);
2189
+ }
2190
+ });
2117
2191
  if (this.streaming)
2118
2192
  await this.subscribe();
2119
- else
2193
+ else {
2120
2194
  await this.rpc('connect', {});
2121
- },
2122
- on_message: (event) => {
2123
- this.on_message(event);
2195
+ if (this.autologin)
2196
+ await this.call('login', [this.username, this.password], { urgent: true });
2197
+ }
2124
2198
  }
2125
- });
2126
- if (!this.streaming && this.autologin)
2127
- await this.call('login', [this.username, this.password], { urgent: true });
2199
+ }
2200
+ finally {
2201
+ resolve();
2202
+ }
2128
2203
  }
2129
2204
  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, } = {}) {
2130
2205
  let flag = 0;
@@ -2160,12 +2235,9 @@ export class DDB {
2160
2235
  disconnect() {
2161
2236
  if (this.connected)
2162
2237
  this.websocket.close(1000);
2163
- this.on_message = () => { };
2164
- this.presult = Promise.resolve(null);
2165
- this.pconnect = Promise.resolve();
2166
- this.pnode_run_defined = false;
2167
2238
  }
2168
2239
  /** rpc through websocket (function/script/variable command)
2240
+ When the DDB is not connected, the call will be automatically connected. When the connection is disconnected, the call will throw the Connectionerror
2169
2241
  - type: API 类型: 'script' | 'function' | 'variable'
2170
2242
  - options:
2171
2243
  - urgent?: 决定 `行为标识` 那一行字符串的取值(只适用于 script 和 function)
@@ -2175,24 +2247,12 @@ export class DDB {
2175
2247
  为 false 时返回的 DdbObj 仅含有 buffer 和 le,不做解析,以便后续转发、序列化
2176
2248
  */
2177
2249
  async rpc(type, { script, func, args = [], vars = [], urgent, listener, parse_object, }) {
2178
- if (!this.websocket) {
2179
- const ptail = this.pconnect;
2180
- let resolve;
2181
- this.pconnect = new Promise((_resolve, _reject) => {
2182
- resolve = _resolve;
2183
- });
2184
- await ptail;
2185
- try {
2186
- if (!this.websocket)
2187
- await this.connect();
2188
- }
2189
- finally {
2190
- resolve();
2191
- }
2192
- }
2250
+ if (!this.websocket)
2251
+ await this.connect();
2193
2252
  if (!this.connected)
2194
- throw new Error(`${this.url} is already disconnected`);
2253
+ throw new ConnectionError(this);
2195
2254
  if (func === 'pnode_run' && !this.pnode_run_defined) {
2255
+ // 保证并发调用 rpc 时只定义一次 pnode_run
2196
2256
  const ptail = this.ppnoderun;
2197
2257
  let resolve;
2198
2258
  this.ppnoderun = new Promise((_resolve, _reject) => {
@@ -2386,8 +2446,9 @@ export class DDB {
2386
2446
  async cancel() {
2387
2447
  let ddb = new DDB(this.url, this);
2388
2448
  try {
2449
+ // 因为是新建的连接,而且执行完脚本之后马上就关闭了,所以不用考虑变量泄漏的问题
2389
2450
  await ddb.eval(`jobs = exec rootJobId from getConsoleJobs() where sessionId = ${this.sid}\n` +
2390
- 'if (size(jobs))\n' +
2451
+ (this.python ? 'if size(jobs):\n' : 'if (size(jobs))\n') +
2391
2452
  ' cancelConsoleJob(jobs)\n', { urgent: true });
2392
2453
  }
2393
2454
  finally {
@@ -2448,110 +2509,58 @@ export class DDB {
2448
2509
  }
2449
2510
  /** Internal stream subscription method */
2450
2511
  async subscribe() {
2451
- const args = DdbObj.to_ddbobjs([
2452
- new DdbVectorString(this.autologin ?
2453
- [this.username, this.password]
2454
- :
2455
- // 不自动登录也要传空的 string vector, 否则 server 无法解析
2456
- []),
2457
- // todo: 还有用吗?需要获取客户端 IP 吗
2512
+ await this.rpc('connect', {});
2513
+ if (this.autologin)
2514
+ await this.call('login', [this.username, this.password]);
2515
+ console.log('Subscribed to streaming table, colnames:',
2516
+ // string[3](['time', 'stock', 'price'])
2517
+ this.streaming.colnames = (await this.call('publishTable', [
2458
2518
  'localhost',
2459
- // todo: 端口还有用吗?
2460
2519
  new DdbInt(0),
2461
2520
  this.streaming.table,
2462
2521
  (this.streaming.action ||= `api_js_${new Date().getTime()}`),
2463
2522
  // new DdbInt(-1), // offset
2464
2523
  // filter
2465
2524
  // allow exists
2466
- ]);
2467
- return new Promise((resolve, reject) => {
2468
- let first = true;
2469
- let second = true;
2470
- // 先准备好收到 websocket message 的 callback
2471
- this.on_message = ({ data: buffer }) => {
2472
- if (first) {
2473
- first = false;
2474
- try {
2475
- const dv = new DataView(buffer);
2476
- const status = dv.getUint8(1);
2477
- this.le = Boolean(dv.getUint8(2));
2478
- const nobjs = dv.getUint32(3, this.le);
2479
- // 解析返回数据,从 server 实现看其实只有一个 DdbObj
2480
- let offset = 7;
2481
- let objs = new Array(nobjs);
2482
- const buf = new Uint8Array(buffer);
2483
- for (let i = 0; i < nobjs; i++) {
2484
- const obj = DdbObj.parse(buf.subarray(offset), this.le);
2485
- offset += obj.length;
2486
- objs[i] = obj;
2487
- console.log(`订阅流表的响应数据: objs[${i}]:`, obj);
2488
- }
2489
- this.streaming.colnames = objs[0].value;
2490
- if (status === StreamingStatusCode.ok)
2491
- console.log('Subscribed to streaming table:', { status: StreamingStatusCode[status], result: objs[0] });
2492
- else
2493
- reject(new Error(`Failed to subscribe to streaming table: { status: ${StreamingStatusCode[status]}, error: ${objs[0]?.value} }`));
2494
- }
2495
- catch (error) {
2496
- reject(error);
2497
- }
2498
- }
2499
- else {
2500
- try {
2501
- const dv = new DataView(buffer);
2502
- const buf = new Uint8Array(buffer);
2503
- let { streaming } = this;
2504
- streaming.time = dv.getBigInt64(1, this.le);
2505
- streaming.id = dv.getBigInt64(9, this.le);
2506
- const i_topic_end = buf.indexOf(0, 17);
2507
- streaming.topic = this.dec.decode(buf.subarray(17, i_topic_end));
2508
- if (second) {
2509
- second = false;
2510
- // 是 table
2511
- streaming.schema = DdbObj.parse(buf.subarray(i_topic_end + 1), this.le);
2512
- console.log('Received streaming table schema:', streaming.schema);
2513
- streaming.window = {
2514
- offset: 0,
2515
- rows: 0,
2516
- segments: [],
2517
- };
2518
- assert(streaming.schema.rows === 0, 'schema.rows === 0');
2519
- resolve(streaming.schema);
2520
- }
2521
- else {
2522
- // 是 column 片段组成的 any vector
2523
- const data = streaming.data = DdbObj.parse(buf.subarray(i_topic_end + 1), this.le);
2524
- let { window: win } = streaming;
2525
- win.rows += data.rows;
2526
- win.segments.push(data);
2527
- if (win.rows >= winsize * 2 && win.segments.length >= 2) {
2528
- let winsize_ = 0;
2529
- let i = win.segments.length - 1;
2530
- // 往前移动至首个累计 winsize_ 超过 winsize 的位置
2531
- for (; winsize_ < winsize; i--)
2532
- winsize_ += win.segments[i].rows;
2533
- win.segments = win.segments.slice(i);
2534
- win.offset += win.rows - winsize_;
2535
- win.rows = winsize_;
2536
- console.log('exceeds winsize, shrink window.segments', win);
2537
- }
2538
- streaming.handler(streaming);
2539
- }
2540
- }
2541
- catch (error) {
2542
- reject(error);
2543
- }
2525
+ ])).value);
2526
+ this.streaming.window = {
2527
+ offset: 0,
2528
+ rows: 0,
2529
+ segments: [],
2530
+ };
2531
+ // 先准备好收到 websocket message 的 callback
2532
+ this.on_message = ({ data: buffer }) => {
2533
+ let { streaming } = this;
2534
+ try {
2535
+ const dv = new DataView(buffer);
2536
+ const buf = new Uint8Array(buffer);
2537
+ streaming.time = dv.getBigInt64(1, this.le);
2538
+ streaming.id = dv.getBigInt64(9, this.le);
2539
+ const i_topic_end = buf.indexOf(0, 17);
2540
+ streaming.topic = this.dec.decode(buf.subarray(17, i_topic_end));
2541
+ // 是 column 片段组成的 any vector
2542
+ const data = streaming.data = DdbObj.parse(buf.subarray(i_topic_end + 1), this.le);
2543
+ let { window: win } = streaming;
2544
+ win.rows += streaming.rows = data.value[0].rows;
2545
+ win.segments.push(data);
2546
+ if (win.rows >= winsize * 2 && win.segments.length >= 2) {
2547
+ let winsize_ = 0;
2548
+ let i = win.segments.length - 1;
2549
+ // 往前移动至首个累计 winsize_ 超过 winsize 的位置
2550
+ for (; winsize_ < winsize; i--)
2551
+ winsize_ += win.segments[i].value[0].rows;
2552
+ win.segments = win.segments.slice(i);
2553
+ win.offset += win.rows - winsize_;
2554
+ win.rows = winsize_;
2544
2555
  }
2545
- };
2546
- // 发送订阅 websocket message
2547
- this.websocket.send(concat([
2548
- Uint8Array.of(1, // from = SubscriberFromType::FROM_API
2549
- 1, // type = SubscriberRPCType::RPC_START
2550
- Number(DDB.le_client)),
2551
- Uint32Array.of(args.length),
2552
- ...args.map(obj => obj.pack())
2553
- ]));
2554
- });
2556
+ }
2557
+ catch (error) {
2558
+ // 将 error 交给 handler 处理
2559
+ streaming.error = error;
2560
+ }
2561
+ this.streaming.handler(streaming);
2562
+ };
2563
+ return this.streaming;
2555
2564
  }
2556
2565
  }
2557
2566
  //# sourceMappingURL=index.js.map