@duckdb/node-api 1.1.3-alpha.8 → 1.1.3-alpha.9

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/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2018-2023 Stichting DuckDB Foundation
1
+ Copyright 2018-2025 Stichting DuckDB Foundation
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
4
 
package/README.md CHANGED
@@ -18,8 +18,8 @@ available separately as [@duckdb/duckdb-bindings](https://www.npmjs.com/package/
18
18
  ### Roadmap
19
19
 
20
20
  Some features are not yet complete:
21
- - Appending and binding advanced data types. (Additional DuckDB C API support needed.)
22
- - Writing to data chunk vectors. (Needs special handling in Node.)
21
+ - Binding advanced data types. (Additional DuckDB C API support needed.)
22
+ - Appending advanced data types row-by-row. Appending data chunks recommended instead.
23
23
  - User-defined types & functions. (Support for this was added to the DuckDB C API in v1.1.0.)
24
24
  - Profiling info (Added in v1.1.0)
25
25
  - Table description (Added in v1.1.0)
@@ -103,12 +103,43 @@ const result = await connection.run('from test_all_types()');
103
103
  ### Parameterize SQL
104
104
 
105
105
  ```ts
106
- const prepared = await connection.prepare('select $1, $2');
106
+ const prepared = await connection.prepare('select $1, $2, $3');
107
107
  prepared.bindVarchar(1, 'duck');
108
108
  prepared.bindInteger(2, 42);
109
+ prepared.bindList(3, listValue([10, 11, 12]), LIST(INTEGER));
109
110
  const result = await prepared.run();
110
111
  ```
111
112
 
113
+ or:
114
+
115
+ ```ts
116
+ const prepared = await connection.prepare('select $a, $b, $c');
117
+ prepared.bind({
118
+ 'a': 'duck',
119
+ 'b': 42,
120
+ 'c': listValue([10, 11, 12]),
121
+ }, {
122
+ 'a': VARCHAR,
123
+ 'b': INTEGER,
124
+ 'c': LIST(INTEGER),
125
+ });
126
+ const result = await prepared.run();
127
+ ```
128
+
129
+ or even:
130
+
131
+ ```ts
132
+ const result = await connection.run('select $a, $b, $c', {
133
+ 'a': 'duck',
134
+ 'b': 42,
135
+ 'c': listValue([10, 11, 12]),
136
+ }, {
137
+ 'a': VARCHAR,
138
+ 'b': INTEGER,
139
+ 'c': LIST(INTEGER),
140
+ });
141
+ ```
142
+
112
143
  ### Stream Results
113
144
 
114
145
  Streaming results evaluate lazily when rows are read.
@@ -412,6 +443,31 @@ appender.endRow();
412
443
  appender.close(); // also flushes
413
444
  ```
414
445
 
446
+ ### Append Data Chunk
447
+
448
+ ```ts
449
+ await connection.run(
450
+ `create or replace table target_table(i integer, v varchar)`
451
+ );
452
+
453
+ const appender = await connection.createAppender('main', 'target_table');
454
+
455
+ const chunk = DuckDBDataChunk.create([INTEGER, VARCHAR]);
456
+ chunk.setColumns([
457
+ [42, 123, 17],
458
+ ['duck', 'mallad', 'goose'],
459
+ ]);
460
+ // OR:
461
+ // chunk.setRows([
462
+ // [42, 'duck'],
463
+ // [123, 'mallard'],
464
+ // [17, 'goose'],
465
+ // ]);
466
+
467
+ appender.appendDataChunk(chunk);
468
+ appender.flush();
469
+ ```
470
+
415
471
  ### Extract Statements
416
472
 
417
473
  ```ts
@@ -3,10 +3,12 @@ import { DuckDBAppender } from './DuckDBAppender';
3
3
  import { DuckDBExtractedStatements } from './DuckDBExtractedStatements';
4
4
  import { DuckDBInstance } from './DuckDBInstance';
5
5
  import { DuckDBMaterializedResult } from './DuckDBMaterializedResult';
6
+ import { DuckDBPendingResult } from './DuckDBPendingResult';
6
7
  import { DuckDBPreparedStatement } from './DuckDBPreparedStatement';
7
8
  import { DuckDBResult } from './DuckDBResult';
8
9
  import { DuckDBResultReader } from './DuckDBResultReader';
9
- import { DuckDBPendingResult } from './DuckDBPendingResult';
10
+ import { DuckDBType } from './DuckDBType';
11
+ import { DuckDBValue } from './values';
10
12
  export declare class DuckDBConnection {
11
13
  private readonly connection;
12
14
  constructor(connection: duckdb.Connection);
@@ -16,16 +18,16 @@ export declare class DuckDBConnection {
16
18
  disconnect(): void;
17
19
  interrupt(): void;
18
20
  get progress(): duckdb.QueryProgress;
19
- run(sql: string): Promise<DuckDBMaterializedResult>;
20
- runAndRead(sql: string): Promise<DuckDBResultReader>;
21
- runAndReadAll(sql: string): Promise<DuckDBResultReader>;
22
- runAndReadUntil(sql: string, targetRowCount: number): Promise<DuckDBResultReader>;
23
- stream(sql: string): Promise<DuckDBResult>;
24
- streamAndRead(sql: string): Promise<DuckDBResultReader>;
25
- streamAndReadAll(sql: string): Promise<DuckDBResultReader>;
26
- streamAndReadUntil(sql: string, targetRowCount: number): Promise<DuckDBResultReader>;
27
- start(sql: string): Promise<DuckDBPendingResult>;
28
- startStream(sql: string): Promise<DuckDBPendingResult>;
21
+ run(sql: string, values?: DuckDBValue[] | Record<string, DuckDBValue>, types?: DuckDBType[] | Record<string, DuckDBType>): Promise<DuckDBMaterializedResult>;
22
+ runAndRead(sql: string, values?: DuckDBValue[] | Record<string, DuckDBValue>, types?: DuckDBType[] | Record<string, DuckDBType>): Promise<DuckDBResultReader>;
23
+ runAndReadAll(sql: string, values?: DuckDBValue[] | Record<string, DuckDBValue>, types?: DuckDBType[] | Record<string, DuckDBType>): Promise<DuckDBResultReader>;
24
+ runAndReadUntil(sql: string, targetRowCount: number, values?: DuckDBValue[] | Record<string, DuckDBValue>, types?: DuckDBType[] | Record<string, DuckDBType>): Promise<DuckDBResultReader>;
25
+ stream(sql: string, values?: DuckDBValue[] | Record<string, DuckDBValue>, types?: DuckDBType[] | Record<string, DuckDBType>): Promise<DuckDBResult>;
26
+ streamAndRead(sql: string, values?: DuckDBValue[] | Record<string, DuckDBValue>, types?: DuckDBType[] | Record<string, DuckDBType>): Promise<DuckDBResultReader>;
27
+ streamAndReadAll(sql: string, values?: DuckDBValue[] | Record<string, DuckDBValue>, types?: DuckDBType[] | Record<string, DuckDBType>): Promise<DuckDBResultReader>;
28
+ streamAndReadUntil(sql: string, targetRowCount: number, values?: DuckDBValue[] | Record<string, DuckDBValue>, types?: DuckDBType[] | Record<string, DuckDBType>): Promise<DuckDBResultReader>;
29
+ start(sql: string, values?: DuckDBValue[] | Record<string, DuckDBValue>, types?: DuckDBType[] | Record<string, DuckDBType>): Promise<DuckDBPendingResult>;
30
+ startStream(sql: string, values?: DuckDBValue[] | Record<string, DuckDBValue>, types?: DuckDBType[] | Record<string, DuckDBType>): Promise<DuckDBPendingResult>;
29
31
  prepare(sql: string): Promise<DuckDBPreparedStatement>;
30
32
  extractStatements(sql: string): Promise<DuckDBExtractedStatements>;
31
33
  createAppender(schema: string, table: string): Promise<DuckDBAppender>;
@@ -31,45 +31,61 @@ class DuckDBConnection {
31
31
  get progress() {
32
32
  return node_bindings_1.default.query_progress(this.connection);
33
33
  }
34
- async run(sql) {
35
- return new DuckDBMaterializedResult_1.DuckDBMaterializedResult(await node_bindings_1.default.query(this.connection, sql));
34
+ async run(sql, values, types) {
35
+ if (values) {
36
+ const prepared = await this.prepare(sql);
37
+ prepared.bind(values, types);
38
+ return prepared.run();
39
+ }
40
+ else {
41
+ return new DuckDBMaterializedResult_1.DuckDBMaterializedResult(await node_bindings_1.default.query(this.connection, sql));
42
+ }
36
43
  }
37
- async runAndRead(sql) {
38
- return new DuckDBResultReader_1.DuckDBResultReader(await this.run(sql));
44
+ async runAndRead(sql, values, types) {
45
+ return new DuckDBResultReader_1.DuckDBResultReader(await this.run(sql, values, types));
39
46
  }
40
- async runAndReadAll(sql) {
41
- const reader = new DuckDBResultReader_1.DuckDBResultReader(await this.run(sql));
47
+ async runAndReadAll(sql, values, types) {
48
+ const reader = new DuckDBResultReader_1.DuckDBResultReader(await this.run(sql, values, types));
42
49
  await reader.readAll();
43
50
  return reader;
44
51
  }
45
- async runAndReadUntil(sql, targetRowCount) {
46
- const reader = new DuckDBResultReader_1.DuckDBResultReader(await this.run(sql));
52
+ async runAndReadUntil(sql, targetRowCount, values, types) {
53
+ const reader = new DuckDBResultReader_1.DuckDBResultReader(await this.run(sql, values, types));
47
54
  await reader.readUntil(targetRowCount);
48
55
  return reader;
49
56
  }
50
- async stream(sql) {
57
+ async stream(sql, values, types) {
51
58
  const prepared = await this.prepare(sql);
59
+ if (values) {
60
+ prepared.bind(values, types);
61
+ }
52
62
  return prepared.stream();
53
63
  }
54
- async streamAndRead(sql) {
55
- return new DuckDBResultReader_1.DuckDBResultReader(await this.stream(sql));
64
+ async streamAndRead(sql, values, types) {
65
+ return new DuckDBResultReader_1.DuckDBResultReader(await this.stream(sql, values, types));
56
66
  }
57
- async streamAndReadAll(sql) {
58
- const reader = new DuckDBResultReader_1.DuckDBResultReader(await this.stream(sql));
67
+ async streamAndReadAll(sql, values, types) {
68
+ const reader = new DuckDBResultReader_1.DuckDBResultReader(await this.stream(sql, values, types));
59
69
  await reader.readAll();
60
70
  return reader;
61
71
  }
62
- async streamAndReadUntil(sql, targetRowCount) {
63
- const reader = new DuckDBResultReader_1.DuckDBResultReader(await this.stream(sql));
72
+ async streamAndReadUntil(sql, targetRowCount, values, types) {
73
+ const reader = new DuckDBResultReader_1.DuckDBResultReader(await this.stream(sql, values, types));
64
74
  await reader.readUntil(targetRowCount);
65
75
  return reader;
66
76
  }
67
- async start(sql) {
77
+ async start(sql, values, types) {
68
78
  const prepared = await this.prepare(sql);
79
+ if (values) {
80
+ prepared.bind(values, types);
81
+ }
69
82
  return prepared.start();
70
83
  }
71
- async startStream(sql) {
84
+ async startStream(sql, values, types) {
72
85
  const prepared = await this.prepare(sql);
86
+ if (values) {
87
+ prepared.bind(values, types);
88
+ }
73
89
  return prepared.startStream();
74
90
  }
75
91
  async prepare(sql) {
@@ -1,17 +1,21 @@
1
1
  import duckdb from '@duckdb/node-bindings';
2
+ import { DuckDBType } from './DuckDBType';
2
3
  import { DuckDBVector } from './DuckDBVector';
3
4
  import { DuckDBValue } from './values';
4
5
  export declare class DuckDBDataChunk {
5
6
  readonly chunk: duckdb.DataChunk;
6
7
  private readonly vectors;
7
8
  constructor(chunk: duckdb.DataChunk);
8
- static create(logical_types: duckdb.LogicalType[]): DuckDBDataChunk;
9
+ static create(types: readonly DuckDBType[], rowCount?: number): DuckDBDataChunk;
9
10
  reset(): void;
10
11
  get columnCount(): number;
11
12
  getColumnVector(columnIndex: number): DuckDBVector;
12
13
  getColumnValues(columnIndex: number): DuckDBValue[];
14
+ setColumnValues(columnIndex: number, values: readonly DuckDBValue[]): void;
13
15
  getColumns(): DuckDBValue[][];
16
+ setColumns(columns: readonly (readonly DuckDBValue[])[]): void;
14
17
  getRows(): DuckDBValue[][];
18
+ setRows(rows: readonly (readonly DuckDBValue[])[]): void;
15
19
  get rowCount(): number;
16
20
  set rowCount(count: number);
17
21
  }
@@ -12,8 +12,12 @@ class DuckDBDataChunk {
12
12
  constructor(chunk) {
13
13
  this.chunk = chunk;
14
14
  }
15
- static create(logical_types) {
16
- return new DuckDBDataChunk(node_bindings_1.default.create_data_chunk(logical_types));
15
+ static create(types, rowCount) {
16
+ const chunk = new DuckDBDataChunk(node_bindings_1.default.create_data_chunk(types.map(t => t.toLogicalType().logical_type)));
17
+ if (rowCount != undefined) {
18
+ chunk.rowCount = rowCount;
19
+ }
20
+ return chunk;
17
21
  }
18
22
  reset() {
19
23
  node_bindings_1.default.data_chunk_reset(this.chunk);
@@ -32,6 +36,16 @@ class DuckDBDataChunk {
32
36
  getColumnValues(columnIndex) {
33
37
  return this.getColumnVector(columnIndex).toArray();
34
38
  }
39
+ setColumnValues(columnIndex, values) {
40
+ const vector = this.getColumnVector(columnIndex);
41
+ if (vector.itemCount !== values.length) {
42
+ throw new Error(`number of values must equal chunk row count`);
43
+ }
44
+ for (let i = 0; i < values.length; i++) {
45
+ vector.setItem(i, values[i]);
46
+ }
47
+ vector.flush();
48
+ }
35
49
  getColumns() {
36
50
  const columns = [];
37
51
  const columnCount = this.columnCount;
@@ -40,6 +54,14 @@ class DuckDBDataChunk {
40
54
  }
41
55
  return columns;
42
56
  }
57
+ setColumns(columns) {
58
+ if (columns.length > 0) {
59
+ this.rowCount = columns[0].length;
60
+ }
61
+ for (let columnIndex = 0; columnIndex < columns.length; columnIndex++) {
62
+ this.setColumnValues(columnIndex, columns[columnIndex]);
63
+ }
64
+ }
43
65
  getRows() {
44
66
  const rows = [];
45
67
  const vectors = [];
@@ -57,6 +79,17 @@ class DuckDBDataChunk {
57
79
  }
58
80
  return rows;
59
81
  }
82
+ setRows(rows) {
83
+ this.rowCount = rows.length;
84
+ const columnCount = this.columnCount;
85
+ for (let columnIndex = 0; columnIndex < columnCount; columnIndex++) {
86
+ const vector = this.getColumnVector(columnIndex);
87
+ for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
88
+ vector.setItem(rowIndex, rows[rowIndex][columnIndex]);
89
+ }
90
+ vector.flush();
91
+ }
92
+ }
60
93
  get rowCount() {
61
94
  return node_bindings_1.default.data_chunk_get_size(this.chunk);
62
95
  }
@@ -3,9 +3,10 @@ import { DuckDBMaterializedResult } from './DuckDBMaterializedResult';
3
3
  import { DuckDBPendingResult } from './DuckDBPendingResult';
4
4
  import { DuckDBResult } from './DuckDBResult';
5
5
  import { DuckDBResultReader } from './DuckDBResultReader';
6
+ import { DuckDBArrayType, DuckDBListType, DuckDBStructType, DuckDBType } from './DuckDBType';
6
7
  import { DuckDBTypeId } from './DuckDBTypeId';
7
8
  import { StatementType } from './enums';
8
- import { DuckDBDateValue, DuckDBDecimalValue, DuckDBIntervalValue, DuckDBTimestampValue, DuckDBTimeValue } from './values';
9
+ import { DuckDBArrayValue, DuckDBDateValue, DuckDBDecimalValue, DuckDBIntervalValue, DuckDBListValue, DuckDBStructValue, DuckDBTimestampTZValue, DuckDBTimestampValue, DuckDBTimeTZValue, DuckDBTimeValue, DuckDBValue } from './values';
9
10
  export declare class DuckDBPreparedStatement {
10
11
  private readonly prepared_statement;
11
12
  constructor(prepared_statement: duckdb.PreparedStatement);
@@ -31,11 +32,18 @@ export declare class DuckDBPreparedStatement {
31
32
  bindDouble(parameterIndex: number, value: number): void;
32
33
  bindDate(parameterIndex: number, value: DuckDBDateValue): void;
33
34
  bindTime(parameterIndex: number, value: DuckDBTimeValue): void;
35
+ bindTimeTZ(parameterIndex: number, value: DuckDBTimeTZValue): void;
34
36
  bindTimestamp(parameterIndex: number, value: DuckDBTimestampValue): void;
37
+ bindTimestampTZ(parameterIndex: number, value: DuckDBTimestampTZValue): void;
35
38
  bindInterval(parameterIndex: number, value: DuckDBIntervalValue): void;
36
39
  bindVarchar(parameterIndex: number, value: string): void;
37
40
  bindBlob(parameterIndex: number, value: Uint8Array): void;
41
+ bindArray(parameterIndex: number, value: DuckDBArrayValue, type: DuckDBArrayType): void;
42
+ bindList(parameterIndex: number, value: DuckDBListValue, type: DuckDBListType): void;
43
+ bindStruct(parameterIndex: number, value: DuckDBStructValue, type: DuckDBStructType): void;
38
44
  bindNull(parameterIndex: number): void;
45
+ bindValue(parameterIndex: number, value: DuckDBValue, type: DuckDBType): void;
46
+ bind(values: DuckDBValue[] | Record<string, DuckDBValue>, types?: DuckDBType[] | Record<string, DuckDBType>): void;
39
47
  run(): Promise<DuckDBMaterializedResult>;
40
48
  runAndRead(): Promise<DuckDBResultReader>;
41
49
  runAndReadAll(): Promise<DuckDBResultReader>;
@@ -5,10 +5,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.DuckDBPreparedStatement = void 0;
7
7
  const node_bindings_1 = __importDefault(require("@duckdb/node-bindings"));
8
+ const createValue_1 = require("./createValue");
8
9
  const DuckDBMaterializedResult_1 = require("./DuckDBMaterializedResult");
9
10
  const DuckDBPendingResult_1 = require("./DuckDBPendingResult");
10
11
  const DuckDBResult_1 = require("./DuckDBResult");
11
12
  const DuckDBResultReader_1 = require("./DuckDBResultReader");
13
+ const DuckDBType_1 = require("./DuckDBType");
14
+ const typeForValue_1 = require("./typeForValue");
12
15
  class DuckDBPreparedStatement {
13
16
  prepared_statement;
14
17
  constructor(prepared_statement) {
@@ -80,11 +83,16 @@ class DuckDBPreparedStatement {
80
83
  bindTime(parameterIndex, value) {
81
84
  node_bindings_1.default.bind_time(this.prepared_statement, parameterIndex, value);
82
85
  }
86
+ bindTimeTZ(parameterIndex, value) {
87
+ this.bindValue(parameterIndex, value, DuckDBType_1.TIMETZ);
88
+ }
83
89
  bindTimestamp(parameterIndex, value) {
84
90
  node_bindings_1.default.bind_timestamp(this.prepared_statement, parameterIndex, value);
85
91
  }
86
- // TODO: bind TIMESTAMPS_S/_MS/_NS?
87
- // TODO: bind TIME_TZ/TIMESTAMP_TZ?
92
+ bindTimestampTZ(parameterIndex, value) {
93
+ this.bindValue(parameterIndex, value, DuckDBType_1.TIMESTAMPTZ);
94
+ }
95
+ // TODO: bind TIMESTAMPS_S/_MS/_NS
88
96
  bindInterval(parameterIndex, value) {
89
97
  node_bindings_1.default.bind_interval(this.prepared_statement, parameterIndex, value);
90
98
  }
@@ -94,17 +102,40 @@ class DuckDBPreparedStatement {
94
102
  bindBlob(parameterIndex, value) {
95
103
  node_bindings_1.default.bind_blob(this.prepared_statement, parameterIndex, value);
96
104
  }
97
- // TODO: bind ENUM?
98
- // TODO: bind nested types? (ARRAY, LIST, STRUCT, MAP, UNION) (using bindValue?)
99
- // TODO: bind UUID?
100
- // TODO: bind BIT?
105
+ // TODO: bind ENUM
106
+ bindArray(parameterIndex, value, type) {
107
+ this.bindValue(parameterIndex, value, type);
108
+ }
109
+ bindList(parameterIndex, value, type) {
110
+ this.bindValue(parameterIndex, value, type);
111
+ }
112
+ bindStruct(parameterIndex, value, type) {
113
+ this.bindValue(parameterIndex, value, type);
114
+ }
115
+ // TODO: bind MAP, UNION
116
+ // TODO: bind UUID
117
+ // TODO: bind BIT
101
118
  bindNull(parameterIndex) {
102
119
  node_bindings_1.default.bind_null(this.prepared_statement, parameterIndex);
103
120
  }
104
- // TODO: expose bindValue, or implement bindList, bindStruct, etc.?
105
- // public bindValue(parameterIndex: number, value: Value) {
106
- // duckdb.bind_value(this.prepared_statement, parameterIndex, value);
107
- // }
121
+ bindValue(parameterIndex, value, type) {
122
+ node_bindings_1.default.bind_value(this.prepared_statement, parameterIndex, (0, createValue_1.createValue)(type, value));
123
+ }
124
+ bind(values, types) {
125
+ if (Array.isArray(values)) {
126
+ const typesIsArray = Array.isArray(types);
127
+ for (let i = 0; i < values.length; i++) {
128
+ this.bindValue(i + 1, values[i], typesIsArray ? types[i] : (0, typeForValue_1.typeForValue)(values[i]));
129
+ }
130
+ }
131
+ else {
132
+ const typesIsRecord = types && !Array.isArray(types);
133
+ for (const key in values) {
134
+ const index = this.parameterIndex(key);
135
+ this.bindValue(index, values[key], typesIsRecord ? types[key] : (0, typeForValue_1.typeForValue)(values[key]));
136
+ }
137
+ }
138
+ }
108
139
  async run() {
109
140
  return new DuckDBMaterializedResult_1.DuckDBMaterializedResult(await node_bindings_1.default.execute_prepared(this.prepared_statement));
110
141
  }