duckdb 0.7.1-dev240.0 → 0.7.1-dev320.0

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.
Files changed (60) hide show
  1. package/README.md +1 -1
  2. package/package.json +3 -3
  3. package/src/duckdb/extension/json/json_scan.cpp +1 -4
  4. package/src/duckdb/extension/parquet/column_reader.cpp +7 -0
  5. package/src/duckdb/extension/parquet/include/column_reader.hpp +1 -0
  6. package/src/duckdb/extension/parquet/parquet-extension.cpp +2 -10
  7. package/src/duckdb/src/catalog/catalog.cpp +62 -13
  8. package/src/duckdb/src/catalog/catalog_entry/index_catalog_entry.cpp +8 -7
  9. package/src/duckdb/src/catalog/default/default_views.cpp +1 -1
  10. package/src/duckdb/src/common/file_system.cpp +23 -9
  11. package/src/duckdb/src/common/local_file_system.cpp +4 -4
  12. package/src/duckdb/src/execution/index/art/art.cpp +117 -67
  13. package/src/duckdb/src/execution/index/art/art_key.cpp +24 -12
  14. package/src/duckdb/src/execution/index/art/leaf.cpp +7 -8
  15. package/src/duckdb/src/execution/index/art/node.cpp +13 -27
  16. package/src/duckdb/src/execution/index/art/node16.cpp +5 -8
  17. package/src/duckdb/src/execution/index/art/node256.cpp +3 -5
  18. package/src/duckdb/src/execution/index/art/node4.cpp +4 -7
  19. package/src/duckdb/src/execution/index/art/node48.cpp +5 -8
  20. package/src/duckdb/src/execution/index/art/prefix.cpp +2 -3
  21. package/src/duckdb/src/execution/operator/helper/physical_reset.cpp +1 -9
  22. package/src/duckdb/src/execution/operator/helper/physical_set.cpp +1 -9
  23. package/src/duckdb/src/function/pragma/pragma_queries.cpp +2 -2
  24. package/src/duckdb/src/function/scalar/generic/current_setting.cpp +2 -2
  25. package/src/duckdb/src/function/table/read_csv.cpp +3 -5
  26. package/src/duckdb/src/function/table/table_scan.cpp +3 -0
  27. package/src/duckdb/src/function/table/version/pragma_version.cpp +2 -2
  28. package/src/duckdb/src/include/duckdb/catalog/catalog.hpp +7 -1
  29. package/src/duckdb/src/include/duckdb/catalog/catalog_entry/duck_index_entry.hpp +1 -1
  30. package/src/duckdb/src/include/duckdb/catalog/catalog_entry/index_catalog_entry.hpp +1 -1
  31. package/src/duckdb/src/include/duckdb/common/enums/wal_type.hpp +3 -0
  32. package/src/duckdb/src/include/duckdb/common/file_system.hpp +1 -1
  33. package/src/duckdb/src/include/duckdb/execution/index/art/art.hpp +37 -41
  34. package/src/duckdb/src/include/duckdb/execution/index/art/art_key.hpp +8 -11
  35. package/src/duckdb/src/include/duckdb/main/{extension_functions.hpp → extension_entries.hpp} +26 -5
  36. package/src/duckdb/src/include/duckdb/main/extension_helper.hpp +3 -0
  37. package/src/duckdb/src/include/duckdb/planner/binder.hpp +3 -0
  38. package/src/duckdb/src/include/duckdb/planner/expression_binder/index_binder.hpp +10 -3
  39. package/src/duckdb/src/include/duckdb/storage/data_table.hpp +7 -1
  40. package/src/duckdb/src/include/duckdb/storage/index.hpp +47 -38
  41. package/src/duckdb/src/include/duckdb/storage/write_ahead_log.hpp +7 -0
  42. package/src/duckdb/src/main/database.cpp +4 -2
  43. package/src/duckdb/src/main/extension/extension_load.cpp +22 -3
  44. package/src/duckdb/src/parser/parsed_data/create_index_info.cpp +3 -0
  45. package/src/duckdb/src/planner/binder/statement/bind_create_table.cpp +13 -0
  46. package/src/duckdb/src/planner/expression_binder/index_binder.cpp +32 -1
  47. package/src/duckdb/src/storage/buffer_manager.cpp +30 -3
  48. package/src/duckdb/src/storage/compression/bitpacking.cpp +16 -7
  49. package/src/duckdb/src/storage/data_table.cpp +66 -3
  50. package/src/duckdb/src/storage/index.cpp +1 -1
  51. package/src/duckdb/src/storage/local_storage.cpp +1 -1
  52. package/src/duckdb/src/storage/table_index_list.cpp +1 -2
  53. package/src/duckdb/src/storage/wal_replay.cpp +68 -0
  54. package/src/duckdb/src/storage/write_ahead_log.cpp +21 -1
  55. package/src/duckdb/src/transaction/commit_state.cpp +5 -2
  56. package/src/duckdb/third_party/concurrentqueue/blockingconcurrentqueue.h +2 -2
  57. package/src/statement.cpp +46 -12
  58. package/test/arrow.test.ts +3 -3
  59. package/test/prepare.test.ts +39 -1
  60. package/test/typescript_decls.test.ts +1 -1
@@ -1144,15 +1144,78 @@ void DataTable::UpdateColumn(TableCatalogEntry &table, ClientContext &context, V
1144
1144
  }
1145
1145
 
1146
1146
  //===--------------------------------------------------------------------===//
1147
- // Create Index Scan
1147
+ // Index Scan
1148
1148
  //===--------------------------------------------------------------------===//
1149
- void DataTable::InitializeCreateIndexScan(CreateIndexScanState &state, const vector<column_t> &column_ids) {
1149
+ void DataTable::InitializeWALCreateIndexScan(CreateIndexScanState &state, const vector<column_t> &column_ids) {
1150
1150
  // we grab the append lock to make sure nothing is appended until AFTER we finish the index scan
1151
1151
  state.append_lock = std::unique_lock<mutex>(append_lock);
1152
- row_groups->InitializeCreateIndexScan(state);
1153
1152
  InitializeScan(state, column_ids);
1154
1153
  }
1155
1154
 
1155
+ void DataTable::WALAddIndex(ClientContext &context, unique_ptr<Index> index,
1156
+ const vector<unique_ptr<Expression>> &expressions) {
1157
+
1158
+ // if the data table is empty
1159
+ if (row_groups->IsEmpty()) {
1160
+ info->indexes.AddIndex(std::move(index));
1161
+ return;
1162
+ }
1163
+
1164
+ auto &allocator = Allocator::Get(db);
1165
+
1166
+ DataChunk result;
1167
+ result.Initialize(allocator, index->logical_types);
1168
+
1169
+ DataChunk intermediate;
1170
+ vector<LogicalType> intermediate_types;
1171
+ auto column_ids = index->column_ids;
1172
+ column_ids.push_back(COLUMN_IDENTIFIER_ROW_ID);
1173
+ for (auto &id : index->column_ids) {
1174
+ auto &col = column_definitions[id];
1175
+ intermediate_types.push_back(col.Type());
1176
+ }
1177
+ intermediate_types.emplace_back(LogicalType::ROW_TYPE);
1178
+ intermediate.Initialize(allocator, intermediate_types);
1179
+
1180
+ // initialize an index scan
1181
+ CreateIndexScanState state;
1182
+ InitializeWALCreateIndexScan(state, column_ids);
1183
+
1184
+ if (!is_root) {
1185
+ throw InternalException("Error during WAL replay. Cannot add an index to a table that has been altered.");
1186
+ }
1187
+
1188
+ // now start incrementally building the index
1189
+ {
1190
+ IndexLock lock;
1191
+ index->InitializeLock(lock);
1192
+
1193
+ while (true) {
1194
+ intermediate.Reset();
1195
+ result.Reset();
1196
+ // scan a new chunk from the table to index
1197
+ CreateIndexScan(state, intermediate, TableScanType::TABLE_SCAN_COMMITTED_ROWS_OMIT_PERMANENTLY_DELETED);
1198
+ if (intermediate.size() == 0) {
1199
+ // finished scanning for index creation
1200
+ // release all locks
1201
+ break;
1202
+ }
1203
+ // resolve the expressions for this chunk
1204
+ index->ExecuteExpressions(intermediate, result);
1205
+
1206
+ // insert into the index
1207
+ if (!index->Insert(lock, result, intermediate.data[intermediate.ColumnCount() - 1])) {
1208
+ throw InternalException("Error during WAL replay. Can't create unique index, table contains "
1209
+ "duplicate data on indexed column(s).");
1210
+ }
1211
+ }
1212
+ }
1213
+ info->indexes.AddIndex(std::move(index));
1214
+ }
1215
+
1216
+ //===--------------------------------------------------------------------===//
1217
+ // Statistics
1218
+ //===--------------------------------------------------------------------===//
1156
1219
  unique_ptr<BaseStatistics> DataTable::GetStatistics(ClientContext &context, column_t column_id) {
1157
1220
  if (column_id == COLUMN_IDENTIFIER_ROW_ID) {
1158
1221
  return nullptr;
@@ -86,7 +86,7 @@ bool Index::IndexIsUpdated(const vector<PhysicalIndex> &column_ids) const {
86
86
  return false;
87
87
  }
88
88
 
89
- BlockPointer Index::Serialize(duckdb::MetaBlockWriter &writer) {
89
+ BlockPointer Index::Serialize(MetaBlockWriter &writer) {
90
90
  throw NotImplementedException("The implementation of this index serialization does not exist.");
91
91
  }
92
92
 
@@ -127,7 +127,7 @@ LocalTableStorage::LocalTableStorage(DataTable &table)
127
127
  unbound_expressions.push_back(expr->Copy());
128
128
  }
129
129
  indexes.AddIndex(make_unique<ART>(art.column_ids, art.table_io_manager, std::move(unbound_expressions),
130
- art.constraint_type, art.db, false));
130
+ art.constraint_type, art.db, true));
131
131
  }
132
132
  return false;
133
133
  });
@@ -60,8 +60,7 @@ void TableIndexList::VerifyForeignKey(const vector<PhysicalIndex> &fk_keys, Data
60
60
  throw InternalException("Internal Foreign Key error: could not find index to verify...");
61
61
  }
62
62
  conflict_manager.SetIndexCount(1);
63
-
64
- index->LookupValues(chunk, conflict_manager);
63
+ index->CheckConstraintsForChunk(chunk, conflict_manager);
65
64
  }
66
65
 
67
66
  vector<column_t> TableIndexList::GetRequiredColumns() {
@@ -18,6 +18,8 @@
18
18
  #include "duckdb/storage/write_ahead_log.hpp"
19
19
  #include "duckdb/storage/storage_manager.hpp"
20
20
  #include "duckdb/main/attached_database.hpp"
21
+ #include "duckdb/execution/index/art/art.hpp"
22
+ #include "duckdb/catalog/catalog_entry/duck_index_entry.hpp"
21
23
 
22
24
  namespace duckdb {
23
25
 
@@ -154,6 +156,12 @@ void ReplayState::ReplayEntry(WALType entry_type) {
154
156
  case WALType::DROP_TABLE_MACRO:
155
157
  ReplayDropTableMacro();
156
158
  break;
159
+ case WALType::CREATE_INDEX:
160
+ ReplayCreateIndex();
161
+ break;
162
+ case WALType::DROP_INDEX:
163
+ ReplayDropIndex();
164
+ break;
157
165
  case WALType::USE_TABLE:
158
166
  ReplayUseTable();
159
167
  break;
@@ -379,6 +387,66 @@ void ReplayState::ReplayDropTableMacro() {
379
387
  catalog.DropEntry(context, &info);
380
388
  }
381
389
 
390
+ //===--------------------------------------------------------------------===//
391
+ // Replay Index
392
+ //===--------------------------------------------------------------------===//
393
+ void ReplayState::ReplayCreateIndex() {
394
+
395
+ auto info = IndexCatalogEntry::Deserialize(source, context);
396
+ if (deserialize_only) {
397
+ return;
398
+ }
399
+
400
+ // get the physical table to which we'll add the index
401
+ auto table = catalog.GetEntry<TableCatalogEntry>(context, info->schema, info->table->table_name);
402
+ auto &data_table = table->GetStorage();
403
+
404
+ // bind the parsed expressions
405
+ if (info->expressions.empty()) {
406
+ for (auto &parsed_expr : info->parsed_expressions) {
407
+ info->expressions.push_back(parsed_expr->Copy());
408
+ }
409
+ }
410
+ auto binder = Binder::CreateBinder(context);
411
+ auto expressions = binder->BindCreateIndexExpressions(table, info.get());
412
+
413
+ // create the empty index
414
+ unique_ptr<Index> index;
415
+ switch (info->index_type) {
416
+ case IndexType::ART: {
417
+ index = make_unique<ART>(info->column_ids, TableIOManager::Get(data_table), expressions, info->constraint_type,
418
+ data_table.db, true);
419
+ break;
420
+ }
421
+ default:
422
+ throw InternalException("Unimplemented index type");
423
+ }
424
+
425
+ // add the index to the catalog
426
+ auto index_entry = (DuckIndexEntry *)catalog.CreateIndex(context, info.get());
427
+ index_entry->index = index.get();
428
+ index_entry->info = data_table.info;
429
+ for (auto &parsed_expr : info->parsed_expressions) {
430
+ index_entry->parsed_expressions.push_back(parsed_expr->Copy());
431
+ }
432
+
433
+ // physically add the index to the data table storage
434
+ data_table.WALAddIndex(context, std::move(index), expressions);
435
+ }
436
+
437
+ void ReplayState::ReplayDropIndex() {
438
+
439
+ DropInfo info;
440
+ info.type = CatalogType::INDEX_ENTRY;
441
+ info.schema = source.Read<string>();
442
+ info.name = source.Read<string>();
443
+ if (deserialize_only) {
444
+ return;
445
+ }
446
+
447
+ catalog.DropEntry(context, &info);
448
+ }
449
+
382
450
  //===--------------------------------------------------------------------===//
383
451
  // Replay Data
384
452
  //===--------------------------------------------------------------------===//
@@ -119,7 +119,7 @@ void WriteAheadLog::WriteSequenceValue(SequenceCatalogEntry *entry, SequenceValu
119
119
  }
120
120
 
121
121
  //===--------------------------------------------------------------------===//
122
- // MACRO'S
122
+ // MACROS
123
123
  //===--------------------------------------------------------------------===//
124
124
  void WriteAheadLog::WriteCreateMacro(ScalarMacroCatalogEntry *entry) {
125
125
  if (skip_writing) {
@@ -155,6 +155,26 @@ void WriteAheadLog::WriteDropTableMacro(TableMacroCatalogEntry *entry) {
155
155
  writer->WriteString(entry->name);
156
156
  }
157
157
 
158
+ //===--------------------------------------------------------------------===//
159
+ // Indexes
160
+ //===--------------------------------------------------------------------===//
161
+ void WriteAheadLog::WriteCreateIndex(IndexCatalogEntry *entry) {
162
+ if (skip_writing) {
163
+ return;
164
+ }
165
+ writer->Write<WALType>(WALType::CREATE_INDEX);
166
+ entry->Serialize(*writer);
167
+ }
168
+
169
+ void WriteAheadLog::WriteDropIndex(IndexCatalogEntry *entry) {
170
+ if (skip_writing) {
171
+ return;
172
+ }
173
+ writer->Write<WALType>(WALType::DROP_INDEX);
174
+ writer->WriteString(entry->schema->name);
175
+ writer->WriteString(entry->name);
176
+ }
177
+
158
178
  //===--------------------------------------------------------------------===//
159
179
  // Custom Types
160
180
  //===--------------------------------------------------------------------===//
@@ -87,7 +87,9 @@ void CommitState::WriteCatalogEntry(CatalogEntry *entry, data_ptr_t dataptr) {
87
87
  case CatalogType::TABLE_MACRO_ENTRY:
88
88
  log->WriteCreateTableMacro((TableMacroCatalogEntry *)parent);
89
89
  break;
90
-
90
+ case CatalogType::INDEX_ENTRY:
91
+ log->WriteCreateIndex((IndexCatalogEntry *)parent);
92
+ break;
91
93
  case CatalogType::TYPE_ENTRY:
92
94
  log->WriteCreateType((TypeCatalogEntry *)parent);
93
95
  break;
@@ -119,6 +121,8 @@ void CommitState::WriteCatalogEntry(CatalogEntry *entry, data_ptr_t dataptr) {
119
121
  log->WriteDropType((TypeCatalogEntry *)entry);
120
122
  break;
121
123
  case CatalogType::INDEX_ENTRY:
124
+ log->WriteDropIndex((IndexCatalogEntry *)entry);
125
+ break;
122
126
  case CatalogType::PREPARED_STATEMENT:
123
127
  case CatalogType::SCALAR_FUNCTION_ENTRY:
124
128
  // do nothing, indexes/prepared statements/functions aren't persisted to disk
@@ -127,7 +131,6 @@ void CommitState::WriteCatalogEntry(CatalogEntry *entry, data_ptr_t dataptr) {
127
131
  throw InternalException("Don't know how to drop this type!");
128
132
  }
129
133
  break;
130
- case CatalogType::INDEX_ENTRY:
131
134
  case CatalogType::PREPARED_STATEMENT:
132
135
  case CatalogType::AGGREGATE_FUNCTION_ENTRY:
133
136
  case CatalogType::SCALAR_FUNCTION_ENTRY:
@@ -24,8 +24,8 @@ template<typename T, typename Traits = ConcurrentQueueDefaultTraits>
24
24
  class BlockingConcurrentQueue
25
25
  {
26
26
  private:
27
- typedef ::duckdb_moodycamelmoodycamel::ConcurrentQueue<T, Traits> ConcurrentQueue;
28
- typedef ::duckdb_moodycamelmoodycamel::LightweightSemaphore LightweightSemaphore;
27
+ typedef ::duckdb_moodycamel::ConcurrentQueue<T, Traits> ConcurrentQueue;
28
+ typedef ::duckdb_moodycamel::LightweightSemaphore LightweightSemaphore;
29
29
 
30
30
  public:
31
31
  typedef typename ConcurrentQueue::producer_token_t producer_token_t;
package/src/statement.cpp CHANGED
@@ -8,6 +8,8 @@
8
8
  #include <string>
9
9
  #include <regex>
10
10
 
11
+ using std::unique_ptr;
12
+
11
13
  namespace node_duckdb {
12
14
 
13
15
  Napi::FunctionReference Statement::constructor;
@@ -28,13 +30,45 @@ Napi::Object Statement::Init(Napi::Env env, Napi::Object exports) {
28
30
  return exports;
29
31
  }
30
32
 
33
+ static unique_ptr<duckdb::PreparedStatement> PrepareManyInternal(Statement &statement) {
34
+ auto &connection = statement.connection_ref->connection;
35
+ std::vector<unique_ptr<duckdb::SQLStatement>> statements;
36
+ try {
37
+ if (connection == nullptr) {
38
+ throw duckdb::ConnectionException("Connection was never established or has been closed already");
39
+ }
40
+
41
+ // Prepare all statements
42
+ statements = connection->ExtractStatements(statement.sql);
43
+ if (statements.empty()) {
44
+ throw duckdb::InvalidInputException("No statement to prepare!");
45
+ }
46
+
47
+ // if there are multiple statements, we directly execute the statements besides the last one
48
+ // we only return the result of the last statement to the user, unless one of the previous statements fails
49
+ for (idx_t i = 0; i + 1 < statements.size(); i++) {
50
+ auto pending_query = connection->PendingQuery(std::move(statements[i]));
51
+ auto res = pending_query->Execute();
52
+ if (res->HasError()) {
53
+ return duckdb::make_unique<duckdb::PreparedStatement>(res->GetErrorObject());
54
+ }
55
+ }
56
+
57
+ return connection->Prepare(std::move(statements.back()));
58
+ } catch (const duckdb::Exception &ex) {
59
+ return duckdb::make_unique<duckdb::PreparedStatement>(duckdb::PreservedError(ex));
60
+ } catch (std::exception &ex) {
61
+ return duckdb::make_unique<duckdb::PreparedStatement>(duckdb::PreservedError(ex));
62
+ }
63
+ }
64
+
31
65
  struct PrepareTask : public Task {
32
66
  PrepareTask(Statement &statement, Napi::Function callback) : Task(statement, callback) {
33
67
  }
34
68
 
35
69
  void DoWork() override {
36
70
  auto &statement = Get<Statement>();
37
- statement.statement = statement.connection_ref->connection->Prepare(statement.sql);
71
+ statement.statement = PrepareManyInternal(statement);
38
72
  }
39
73
 
40
74
  void Callback() override {
@@ -221,7 +255,7 @@ struct StatementParam {
221
255
  };
222
256
 
223
257
  struct RunPreparedTask : public Task {
224
- RunPreparedTask(Statement &statement, duckdb::unique_ptr<StatementParam> params, RunType run_type)
258
+ RunPreparedTask(Statement &statement, unique_ptr<StatementParam> params, RunType run_type)
225
259
  : Task(statement, params->callback), params(std::move(params)), run_type(run_type) {
226
260
  }
227
261
 
@@ -366,13 +400,13 @@ struct RunPreparedTask : public Task {
366
400
  } break;
367
401
  }
368
402
  }
369
- std::unique_ptr<duckdb::QueryResult> result;
370
- duckdb::unique_ptr<StatementParam> params;
403
+ unique_ptr<duckdb::QueryResult> result;
404
+ unique_ptr<StatementParam> params;
371
405
  RunType run_type;
372
406
  };
373
407
 
374
408
  struct RunQueryTask : public Task {
375
- RunQueryTask(Statement &statement, duckdb::unique_ptr<StatementParam> params, Napi::Promise::Deferred deferred)
409
+ RunQueryTask(Statement &statement, unique_ptr<StatementParam> params, Napi::Promise::Deferred deferred)
376
410
  : Task(statement), deferred(deferred), params(std::move(params)) {
377
411
  }
378
412
 
@@ -406,11 +440,11 @@ struct RunQueryTask : public Task {
406
440
  }
407
441
 
408
442
  Napi::Promise::Deferred deferred;
409
- std::unique_ptr<duckdb::QueryResult> result;
410
- duckdb::unique_ptr<StatementParam> params;
443
+ unique_ptr<duckdb::QueryResult> result;
444
+ unique_ptr<StatementParam> params;
411
445
  };
412
446
 
413
- duckdb::unique_ptr<StatementParam> Statement::HandleArgs(const Napi::CallbackInfo &info) {
447
+ unique_ptr<StatementParam> Statement::HandleArgs(const Napi::CallbackInfo &info) {
414
448
  size_t start_idx = ignore_first_param ? 1 : 0;
415
449
  auto params = duckdb::make_unique<StatementParam>();
416
450
 
@@ -541,7 +575,7 @@ struct GetChunkTask : public Task {
541
575
  }
542
576
 
543
577
  Napi::Promise::Deferred deferred;
544
- std::unique_ptr<duckdb::DataChunk> chunk;
578
+ unique_ptr<duckdb::DataChunk> chunk;
545
579
  };
546
580
 
547
581
  struct GetNextArrowIpcTask : public Task {
@@ -571,10 +605,10 @@ struct GetNextArrowIpcTask : public Task {
571
605
  duckdb::string_t blob = *(duckdb::string_t *)(chunk->data[0].GetData());
572
606
 
573
607
  // Transfer ownership and Construct ArrayBuffer
574
- auto data_chunk_ptr = new std::unique_ptr<duckdb::DataChunk>();
608
+ auto data_chunk_ptr = new unique_ptr<duckdb::DataChunk>();
575
609
  *data_chunk_ptr = std::move(chunk);
576
610
  auto deleter = [](Napi::Env, void *finalizeData, void *hint) {
577
- delete static_cast<std::unique_ptr<duckdb::DataChunk> *>(hint);
611
+ delete static_cast<unique_ptr<duckdb::DataChunk> *>(hint);
578
612
  };
579
613
  auto array_buffer =
580
614
  Napi::ArrayBuffer::New(env, (void *)blob.GetDataUnsafe(), blob.GetSize(), deleter, data_chunk_ptr);
@@ -583,7 +617,7 @@ struct GetNextArrowIpcTask : public Task {
583
617
  }
584
618
 
585
619
  Napi::Promise::Deferred deferred;
586
- std::unique_ptr<duckdb::DataChunk> chunk;
620
+ unique_ptr<duckdb::DataChunk> chunk;
587
621
  };
588
622
 
589
623
  Napi::Value QueryResult::NextChunk(const Napi::CallbackInfo &info) {
@@ -20,13 +20,13 @@ describe('arrow IPC API fails neatly when extension not loaded', function() {
20
20
  db.arrowIPCStream(query).then(
21
21
  () => Promise.reject(new Error('Expected method to reject.')),
22
22
  err => {
23
- assert.ok(err.message.includes("Catalog Error: Function with name to_arrow_ipc is not on the catalog, but it exists in the arrow extension. To Install and Load the extension, run: INSTALL arrow; LOAD arrow;"))
23
+ assert.ok(err.message.includes("arrow"))
24
24
  }
25
25
  );
26
26
 
27
27
  db.arrowIPCAll(`SELECT * FROM ipc_table`, function (err: null | Error, result: ArrowArray) {
28
28
  if (err) {
29
- assert.ok(err.message.includes("Catalog Error: Function with name to_arrow_ipc is not on the catalog, but it exists in the arrow extension. To Install and Load the extension, run: INSTALL arrow; LOAD arrow;"))
29
+ assert.ok(err.message.includes("arrow"))
30
30
  } else {
31
31
  assert.fail("Expected error");
32
32
  }
@@ -39,7 +39,7 @@ describe('arrow IPC API fails neatly when extension not loaded', function() {
39
39
  it('register buffer should be disabled currently', function(done) {
40
40
  db.register_buffer("test", [new Uint8Array(new ArrayBuffer(10))], true, (err: null | Error) => {
41
41
  assert.ok(err)
42
- assert.ok(err.toString().includes("Function with name scan_arrow_ipc is not on the catalog, but it exists in the arrow extension. To Install and Load the extension, run: INSTALL arrow; LOAD arrow;"));
42
+ assert.ok(err.toString().includes("arrow"));
43
43
  done()
44
44
  });
45
45
  });
@@ -66,7 +66,6 @@ describe('prepare', function() {
66
66
  }
67
67
  });
68
68
 
69
-
70
69
  it('should prepare a statement and return values again', function(done) {
71
70
  var stmt = db.prepare("SELECT txt, num, flt, blb FROM foo ORDER BY num", function(err: null | Error) {
72
71
  if (err) throw err;
@@ -343,6 +342,45 @@ describe('prepare', function() {
343
342
  after(function(done) { db.close(done); });
344
343
  });
345
344
 
345
+ describe('prepare multiple statements', function() {
346
+ var db: sqlite3.Database;
347
+ before(function(done) { db = new sqlite3.Database(':memory:',
348
+ function(err) {
349
+ db.run("CREATE TABLE foo (a integer)", done)
350
+ }
351
+ ); });
352
+
353
+ it('should directly execute first statements', function(done) {
354
+ db.prepare("insert into foo values (3); insert into foo values (4); select * from foo")
355
+ .all(function(err: null | Error, rows: TableData) {
356
+ if (err) throw err;
357
+ assert.equal(rows[0].a, 3);
358
+ assert.equal(rows[1].a, 4);
359
+ })
360
+ .finalize(done);
361
+ });
362
+
363
+ it('should fail in prepare, when executing the first statement', function(done) {
364
+ let prepared = db.prepare("SELECT * FROM non_existent_table; SELECT 42", function(err: null | Error) {
365
+ if (err) {
366
+ done();
367
+ return;
368
+ }
369
+ });
370
+ });
371
+
372
+ it('should fail in prepare, when preparing the first statement', function(done) {
373
+ let prepared = db.prepare("SELCET * FROM foo; SELECT 42", function(err: null | Error) {
374
+ if (err) {
375
+ done();
376
+ return;
377
+ }
378
+ });
379
+ });
380
+
381
+ after(function(done) { db.close(done); });
382
+ });
383
+
346
384
  describe('all()', function() {
347
385
  var db: sqlite3.Database;
348
386
  before(function(done) { db = new sqlite3.Database(':memory:',
@@ -2,7 +2,7 @@ import * as duckdb from "..";
2
2
  import assert from "assert";
3
3
  import fs from "fs";
4
4
 
5
- describe("TypeScript declarataions", function () {
5
+ describe("TypeScript declarations", function () {
6
6
  var db: duckdb.Database;
7
7
  before(function (done) {
8
8
  db = new duckdb.Database(":memory:", duckdb.OPEN_READWRITE, done);