@duckdb/node-api 1.4.2-r.1 → 1.4.3-r.2

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 CHANGED
@@ -925,6 +925,17 @@ for (let stmtIndex = 0; stmtIndex < statementCount; stmtIndex++) {
925
925
 
926
926
  ### Control Evaluation of Tasks
927
927
 
928
+ DuckDB splits work into relatively short tasks. By controlling the
929
+ evaluation of these tasks explicitly, better cooperative multithreading
930
+ can be accomplished. This is especially important for maximum throughput
931
+ in the Node environment, which has a small, fixed number of threads for
932
+ running asynchronous work.
933
+ (See https://docs.libuv.org/en/stable/threadpool.html)
934
+
935
+ Below is a low-level way of controlling the evaluation of processing.
936
+ See the "startThenRead" and "startStreamThenRead" methods for helpers
937
+ that do much of this for you.
938
+
928
939
  ```ts
929
940
  import { DuckDBPendingResultState } from '@duckdb/node-api';
930
941
 
@@ -935,7 +946,7 @@ async function sleep(ms) {
935
946
  }
936
947
 
937
948
  const prepared = await connection.prepare('from range(10_000_000)');
938
- const pending = prepared.start();
949
+ const pending = prepared.startStream();
939
950
  while (pending.runTask() !== DuckDBPendingResultState.RESULT_READY) {
940
951
  console.log('not ready');
941
952
  await sleep(1);
@@ -945,6 +956,12 @@ const result = await pending.getResult();
945
956
  // ...
946
957
  ```
947
958
 
959
+ Equivalently, using `startStreamThenRead`:
960
+
961
+ ```ts
962
+ const reader = await connection.startStreamThenRead(sql);
963
+ ```
964
+
948
965
  ### Ways to run SQL
949
966
 
950
967
  ```ts
@@ -1030,11 +1047,51 @@ const pending = await connection.start(sql);
1030
1047
  const pending = await connection.start(sql, values);
1031
1048
  const pending = await connection.start(sql, values, types);
1032
1049
 
1050
+ // The methods beginning with "startThenRead" provide some, but not full,
1051
+ // cooperative multithreading. They use pending results to split processing
1052
+ // into short tasks, but they fully materialize the result, which can
1053
+ // take some time (and memory). For full cooperative multithreading,
1054
+ // see the "startStreamThenRead" methods below.
1055
+ const reader = await connection.startThenRead(sql);
1056
+ const reader = await connection.startThenRead(sql, values);
1057
+ const reader = await connection.startThenRead(sql, values, types);
1058
+
1059
+ const reader = await connection.startThenReadAll(sql);
1060
+ const reader = await connection.startThenReadAll(sql, values);
1061
+ const reader = await connection.startThenReadAll(sql, values, types);
1062
+
1063
+ const reader = await connection.startThenReadUntil(sql, targetRowCount);
1064
+ const reader =
1065
+ await connection.startThenReadUntil(sql, targetRowCount, values);
1066
+ const reader =
1067
+ await connection.startThenReadUntil(sql, targetRowCount, values, types);
1068
+
1033
1069
  // Create a pending, streaming result.
1034
1070
  const pending = await connection.startStream(sql);
1035
1071
  const pending = await connection.startStream(sql, values);
1036
1072
  const pending = await connection.startStream(sql, values, types);
1037
1073
 
1074
+ // The methods beginning with "startStreamThenRead" are the best options
1075
+ // for cooperative multithreading. By creating a streaming result, they
1076
+ // prevent the result from being fully materialized. By using a pending
1077
+ // result, they split processing into short tasks, preventing any single
1078
+ // task from occupying a thread for too long.
1079
+ const reader = await connection.startStreamThenRead(sql);
1080
+ const reader = await connection.startStreamThenRead(sql, values);
1081
+ const reader = await connection.startStreamThenRead(sql, values, types);
1082
+
1083
+ const reader = await connection.startStreamThenReadAll(sql);
1084
+ const reader = await connection.startStreamThenReadAll(sql, values);
1085
+ const reader =
1086
+ await connection.startStreamThenReadAll(sql, values, types);
1087
+
1088
+ const reader =
1089
+ await connection.startStreamThenReadUntil(sql, targetRowCount);
1090
+ const reader =
1091
+ await connection.startStreamThenReadUntil(sql, targetRowCount, values);
1092
+ const reader = await connection.startStreamThenReadUntil(
1093
+ sql, targetRowCount, values, types);
1094
+
1038
1095
  // Create a pending result from a prepared statement.
1039
1096
  const pending = await prepared.start();
1040
1097
  const pending = await prepared.startStream();
@@ -1043,6 +1100,9 @@ while (pending.runTask() !== DuckDBPendingResultState.RESULT_READY) {
1043
1100
  // optionally sleep or do other work between tasks
1044
1101
  }
1045
1102
 
1103
+ // Or, run tasks (cooperatively) until the result is ready.
1104
+ await pending.runAllTasks();
1105
+
1046
1106
  // Retrieve the result. If not yet READY, will run until it is.
1047
1107
  const result = await pending.getResult();
1048
1108
 
@@ -1058,14 +1118,30 @@ const reader = await pending.readUntil(targetRowCount);
1058
1118
 
1059
1119
  // Asynchronously retrieve data for all rows:
1060
1120
  const columns = await result.getColumns();
1121
+ const columnsJS = await result.getColumnsJS();
1061
1122
  const columnsJson = await result.getColumnsJson();
1062
1123
  const columnsObject = await result.getColumnsObject();
1124
+ const columnsObjectJS = await result.getColumnsObjectJS();
1063
1125
  const columnsObjectJson = await result.getColumnsObjectJson();
1064
1126
  const rows = await result.getRows();
1127
+ const rowsJS = await result.getRowsJS();
1065
1128
  const rowsJson = await result.getRowsJson();
1066
1129
  const rowObjects = await result.getRowObjects();
1130
+ const rowObjectsJS = await result.getRowObjectsJS();
1067
1131
  const rowObjectsJson = await result.getRowObjectsJson();
1068
1132
 
1133
+ // Asynchronous iterators can be used to retrieve partial data:
1134
+ for await (const chunk of result) {
1135
+ // ...
1136
+ }
1137
+
1138
+ // Each chunk can be converted to rows:
1139
+ for await (const rows of result.yieldRows()) {
1140
+ // ...
1141
+ }
1142
+ // See also variations of "yieldRow" for returning row objects
1143
+ // (instead of row arrays), and with JS, JSON, or custom conversion.
1144
+
1069
1145
  // From a reader
1070
1146
 
1071
1147
  // First, (asynchronously) read some rows:
@@ -1075,12 +1151,16 @@ await reader.readUntil(targetRowCount);
1075
1151
 
1076
1152
  // Then, (synchronously) get result data for the rows read:
1077
1153
  const columns = reader.getColumns();
1154
+ const columnsJS = reader.getColumnsJS();
1078
1155
  const columnsJson = reader.getColumnsJson();
1079
1156
  const columnsObject = reader.getColumnsObject();
1157
+ const columnsObjectJS = reader.getColumnsObjectJS();
1080
1158
  const columnsObjectJson = reader.getColumnsObjectJson();
1081
1159
  const rows = reader.getRows();
1160
+ const rowsJS = reader.getRowsJS();
1082
1161
  const rowsJson = reader.getRowsJson();
1083
1162
  const rowObjects = reader.getRowObjects();
1163
+ const rowObjectsJS = reader.getRowObjectsJS();
1084
1164
  const rowObjectsJson = reader.getRowObjectsJson();
1085
1165
 
1086
1166
  // Individual values can also be read directly:
@@ -31,7 +31,13 @@ export declare class DuckDBConnection {
31
31
  streamAndReadAll(sql: string, values?: DuckDBValue[] | Record<string, DuckDBValue>, types?: DuckDBType[] | Record<string, DuckDBType | undefined>): Promise<DuckDBResultReader>;
32
32
  streamAndReadUntil(sql: string, targetRowCount: number, values?: DuckDBValue[] | Record<string, DuckDBValue>, types?: DuckDBType[] | Record<string, DuckDBType | undefined>): Promise<DuckDBResultReader>;
33
33
  start(sql: string, values?: DuckDBValue[] | Record<string, DuckDBValue>, types?: DuckDBType[] | Record<string, DuckDBType | undefined>): Promise<DuckDBPendingResult>;
34
+ startThenRead(sql: string, values?: DuckDBValue[] | Record<string, DuckDBValue>, types?: DuckDBType[] | Record<string, DuckDBType | undefined>): Promise<DuckDBResultReader>;
35
+ startThenReadAll(sql: string, values?: DuckDBValue[] | Record<string, DuckDBValue>, types?: DuckDBType[] | Record<string, DuckDBType | undefined>): Promise<DuckDBResultReader>;
36
+ startThenReadUntil(sql: string, targetRowCount: number, values?: DuckDBValue[] | Record<string, DuckDBValue>, types?: DuckDBType[] | Record<string, DuckDBType | undefined>): Promise<DuckDBResultReader>;
34
37
  startStream(sql: string, values?: DuckDBValue[] | Record<string, DuckDBValue>, types?: DuckDBType[] | Record<string, DuckDBType | undefined>): Promise<DuckDBPendingResult>;
38
+ startStreamThenRead(sql: string, values?: DuckDBValue[] | Record<string, DuckDBValue>, types?: DuckDBType[] | Record<string, DuckDBType | undefined>): Promise<DuckDBResultReader>;
39
+ startStreamThenReadAll(sql: string, values?: DuckDBValue[] | Record<string, DuckDBValue>, types?: DuckDBType[] | Record<string, DuckDBType | undefined>): Promise<DuckDBResultReader>;
40
+ startStreamThenReadUntil(sql: string, targetRowCount: number, values?: DuckDBValue[] | Record<string, DuckDBValue>, types?: DuckDBType[] | Record<string, DuckDBType | undefined>): Promise<DuckDBResultReader>;
35
41
  prepare(sql: string): Promise<DuckDBPreparedStatement>;
36
42
  private createPrepared;
37
43
  extractStatements(sql: string): Promise<DuckDBExtractedStatements>;
@@ -63,12 +63,12 @@ class DuckDBConnection {
63
63
  return new DuckDBResultReader_1.DuckDBResultReader(await this.run(sql, values, types));
64
64
  }
65
65
  async runAndReadAll(sql, values, types) {
66
- const reader = new DuckDBResultReader_1.DuckDBResultReader(await this.run(sql, values, types));
66
+ const reader = await this.runAndRead(sql, values, types);
67
67
  await reader.readAll();
68
68
  return reader;
69
69
  }
70
70
  async runAndReadUntil(sql, targetRowCount, values, types) {
71
- const reader = new DuckDBResultReader_1.DuckDBResultReader(await this.run(sql, values, types));
71
+ const reader = await this.runAndRead(sql, values, types);
72
72
  await reader.readUntil(targetRowCount);
73
73
  return reader;
74
74
  }
@@ -89,12 +89,12 @@ class DuckDBConnection {
89
89
  return new DuckDBResultReader_1.DuckDBResultReader(await this.stream(sql, values, types));
90
90
  }
91
91
  async streamAndReadAll(sql, values, types) {
92
- const reader = new DuckDBResultReader_1.DuckDBResultReader(await this.stream(sql, values, types));
92
+ const reader = await this.streamAndRead(sql, values, types);
93
93
  await reader.readAll();
94
94
  return reader;
95
95
  }
96
96
  async streamAndReadUntil(sql, targetRowCount, values, types) {
97
- const reader = new DuckDBResultReader_1.DuckDBResultReader(await this.stream(sql, values, types));
97
+ const reader = await this.streamAndRead(sql, values, types);
98
98
  await reader.readUntil(targetRowCount);
99
99
  return reader;
100
100
  }
@@ -110,6 +110,21 @@ class DuckDBConnection {
110
110
  prepared.destroySync();
111
111
  }
112
112
  }
113
+ async startThenRead(sql, values, types) {
114
+ const pending = await this.start(sql, values, types);
115
+ await pending.runAllTasks();
116
+ return new DuckDBResultReader_1.DuckDBResultReader(await pending.getResult());
117
+ }
118
+ async startThenReadAll(sql, values, types) {
119
+ const reader = await this.startThenRead(sql, values, types);
120
+ reader.readAll();
121
+ return reader;
122
+ }
123
+ async startThenReadUntil(sql, targetRowCount, values, types) {
124
+ const reader = await this.startThenRead(sql, values, types);
125
+ reader.readUntil(targetRowCount);
126
+ return reader;
127
+ }
113
128
  async startStream(sql, values, types) {
114
129
  const prepared = await this.runUntilLast(sql);
115
130
  try {
@@ -122,6 +137,21 @@ class DuckDBConnection {
122
137
  prepared.destroySync();
123
138
  }
124
139
  }
140
+ async startStreamThenRead(sql, values, types) {
141
+ const pending = await this.startStream(sql, values, types);
142
+ await pending.runAllTasks();
143
+ return new DuckDBResultReader_1.DuckDBResultReader(await pending.getResult());
144
+ }
145
+ async startStreamThenReadAll(sql, values, types) {
146
+ const reader = await this.startStreamThenRead(sql, values, types);
147
+ reader.readAll();
148
+ return reader;
149
+ }
150
+ async startStreamThenReadUntil(sql, targetRowCount, values, types) {
151
+ const reader = await this.startStreamThenRead(sql, values, types);
152
+ reader.readUntil(targetRowCount);
153
+ return reader;
154
+ }
125
155
  async prepare(sql) {
126
156
  const prepared = await this.createPrepared(sql);
127
157
  this.preparedStatements.add(prepared);
@@ -10,6 +10,7 @@ export declare class DuckDBPendingResult {
10
10
  private readonly pending_result;
11
11
  constructor(pending_result: duckdb.PendingResult);
12
12
  runTask(): DuckDBPendingResultState;
13
+ runAllTasks(): Promise<void>;
13
14
  getResult(): Promise<DuckDBResult>;
14
15
  read(): Promise<DuckDBResultReader>;
15
16
  readAll(): Promise<DuckDBResultReader>;
@@ -7,6 +7,7 @@ exports.DuckDBPendingResult = exports.DuckDBPendingResultState = void 0;
7
7
  const node_bindings_1 = __importDefault(require("@duckdb/node-bindings"));
8
8
  const createResult_1 = require("./createResult");
9
9
  const DuckDBResultReader_1 = require("./DuckDBResultReader");
10
+ const sleep_1 = require("./sleep");
10
11
  // Values match similar enum in C API.
11
12
  var DuckDBPendingResultState;
12
13
  (function (DuckDBPendingResultState) {
@@ -34,6 +35,11 @@ class DuckDBPendingResult {
34
35
  throw new Error(`Unexpected pending state: ${pending_state}`);
35
36
  }
36
37
  }
38
+ async runAllTasks() {
39
+ while (this.runTask() !== DuckDBPendingResultState.RESULT_READY) {
40
+ await (0, sleep_1.sleep)(1);
41
+ }
42
+ }
37
43
  async getResult() {
38
44
  return (0, createResult_1.createResult)(await node_bindings_1.default.execute_pending(this.pending_result));
39
45
  }
package/lib/sleep.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function sleep(ms: number): Promise<unknown>;
package/lib/sleep.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sleep = sleep;
4
+ async function sleep(ms) {
5
+ return new Promise((resolve) => {
6
+ setTimeout(resolve, ms);
7
+ });
8
+ }
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@duckdb/node-api",
3
- "version": "1.4.2-r.1",
3
+ "version": "1.4.3-r.2",
4
4
  "license": "MIT",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./lib/index.d.ts",
7
7
  "dependencies": {
8
- "@duckdb/node-bindings": "1.4.2-r.1"
8
+ "@duckdb/node-bindings": "1.4.3-r.2"
9
9
  },
10
10
  "repository": {
11
11
  "type": "git",