duckdb 0.4.1-dev2.0 → 0.4.1-dev2127.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.
package/src/database.cpp CHANGED
@@ -1,4 +1,5 @@
1
1
  #include "duckdb_node.hpp"
2
+ #include "napi.h"
2
3
  #include "parquet-amalgamation.hpp"
3
4
 
4
5
  namespace node_duckdb {
@@ -22,23 +23,47 @@ Napi::Object Database::Init(Napi::Env env, Napi::Object exports) {
22
23
  }
23
24
 
24
25
  struct OpenTask : public Task {
25
- OpenTask(Database &database_, std::string filename_, bool read_only_, Napi::Function callback_)
26
- : Task(database_, callback_), filename(filename_), read_only(read_only_) {
26
+ OpenTask(Database &database_, std::string filename_, duckdb::AccessMode access_mode_, Napi::Object config_,
27
+ Napi::Function callback_)
28
+ : Task(database_, callback_), filename(filename_) {
29
+
30
+ duckdb_config.options.access_mode = access_mode_;
31
+ Napi::Env env = database_.Env();
32
+ Napi::HandleScope scope(env);
33
+
34
+ if (!config_.IsUndefined()) {
35
+ const Napi::Array config_names = config_.GetPropertyNames();
36
+
37
+ for (duckdb::idx_t config_idx = 0; config_idx < config_names.Length(); config_idx++) {
38
+ std::string key = config_names.Get(config_idx).As<Napi::String>();
39
+ std::string val = config_.Get(key).As<Napi::String>();
40
+ auto config_property = duckdb::DBConfig::GetOptionByName(key);
41
+ if (!config_property) {
42
+ Napi::TypeError::New(env, "Unrecognized configuration property" + key).ThrowAsJavaScriptException();
43
+ return;
44
+ }
45
+ try {
46
+ duckdb_config.SetOption(*config_property, duckdb::Value(val));
47
+ } catch (std::exception &e) {
48
+ Napi::TypeError::New(env, "Failed to set configuration option " + key + ": " + e.what())
49
+ .ThrowAsJavaScriptException();
50
+ return;
51
+ }
52
+ }
53
+ }
27
54
  }
28
55
 
29
56
  void DoWork() override {
30
57
  try {
31
- duckdb::DBConfig config;
32
- if (read_only) {
33
- config.access_mode = duckdb::AccessMode::READ_ONLY;
34
- }
35
- Get<Database>().database = duckdb::make_unique<duckdb::DuckDB>(filename, &config);
58
+ Get<Database>().database = duckdb::make_unique<duckdb::DuckDB>(filename, &duckdb_config);
36
59
  duckdb::ParquetExtension extension;
37
60
  extension.Load(*Get<Database>().database);
38
61
  success = true;
39
62
 
63
+ } catch (const duckdb::Exception &ex) {
64
+ error = duckdb::PreservedError(ex);
40
65
  } catch (std::exception &ex) {
41
- error = ex.what();
66
+ error = duckdb::PreservedError(ex);
42
67
  }
43
68
  }
44
69
 
@@ -48,7 +73,7 @@ struct OpenTask : public Task {
48
73
 
49
74
  std::vector<napi_value> args;
50
75
  if (!success) {
51
- args.push_back(Utils::CreateError(env, error));
76
+ args.push_back(Utils::CreateError(env, error.Message()));
52
77
  } else {
53
78
  args.push_back(env.Null());
54
79
  }
@@ -59,22 +84,35 @@ struct OpenTask : public Task {
59
84
  }
60
85
 
61
86
  std::string filename;
62
- bool read_only = false;
63
- std::string error = "";
87
+ duckdb::DBConfig duckdb_config;
88
+ duckdb::PreservedError error;
64
89
  bool success = false;
65
90
  };
66
91
 
67
- Database::Database(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Database>(info), task_inflight(false) {
92
+ Database::Database(const Napi::CallbackInfo &info)
93
+ : Napi::ObjectWrap<Database>(info), task_inflight(false), env(info.Env()) {
68
94
  auto env = info.Env();
95
+
69
96
  if (info.Length() < 1 || !info[0].IsString()) {
70
97
  Napi::TypeError::New(env, "Database location expected").ThrowAsJavaScriptException();
71
98
  return;
72
99
  }
73
100
  std::string filename = info[0].As<Napi::String>();
74
101
  unsigned int pos = 1;
102
+
103
+ duckdb::AccessMode access_mode = duckdb::AccessMode::AUTOMATIC;
104
+
75
105
  int mode = 0;
76
106
  if (info.Length() >= pos && info[pos].IsNumber() && Utils::OtherIsInt(info[pos].As<Napi::Number>())) {
77
107
  mode = info[pos++].As<Napi::Number>().Int32Value();
108
+ if (mode == DUCKDB_NODEJS_READONLY) {
109
+ access_mode = duckdb::AccessMode::READ_ONLY;
110
+ }
111
+ }
112
+
113
+ Napi::Object config;
114
+ if (info.Length() >= pos && info[pos].IsObject() && !info[pos].IsFunction()) {
115
+ config = info[pos++].As<Napi::Object>();
78
116
  }
79
117
 
80
118
  Napi::Function callback;
@@ -82,7 +120,11 @@ Database::Database(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Database>(
82
120
  callback = info[pos++].As<Napi::Function>();
83
121
  }
84
122
 
85
- Schedule(env, duckdb::make_unique<OpenTask>(*this, filename, mode == DUCKDB_NODEJS_READONLY, callback));
123
+ Schedule(env, duckdb::make_unique<OpenTask>(*this, filename, access_mode, config, callback));
124
+ }
125
+
126
+ Database::~Database() {
127
+ Napi::MemoryManagement::AdjustExternalMemory(env, -bytes_allocated);
86
128
  }
87
129
 
88
130
  void Database::Schedule(Napi::Env env, std::unique_ptr<Task> task) {
@@ -93,17 +135,15 @@ void Database::Schedule(Napi::Env env, std::unique_ptr<Task> task) {
93
135
  Process(env);
94
136
  }
95
137
 
96
- static void task_execute(napi_env e, void *data) {
138
+ static void TaskExecuteCallback(napi_env e, void *data) {
97
139
  auto holder = (TaskHolder *)data;
98
140
  holder->task->DoWork();
99
141
  }
100
142
 
101
- static void task_complete(napi_env e, napi_status status, void *data) {
143
+ static void TaskCompleteCallback(napi_env e, napi_status status, void *data) {
102
144
  std::unique_ptr<TaskHolder> holder((TaskHolder *)data);
103
145
  holder->db->TaskComplete(e);
104
- if (holder->task->callback.Value().IsFunction()) {
105
- holder->task->Callback();
106
- }
146
+ holder->task->DoCallback();
107
147
  }
108
148
 
109
149
  void Database::TaskComplete(Napi::Env env) {
@@ -112,6 +152,16 @@ void Database::TaskComplete(Napi::Env env) {
112
152
  task_inflight = false;
113
153
  }
114
154
  Process(env);
155
+
156
+ if (database) {
157
+ // Bookkeeping: tell node (and the node GC in particular) how much
158
+ // memory we're using, such that it can make better decisions on when to
159
+ // trigger collections.
160
+ auto &buffer_manager = duckdb::BufferManager::GetBufferManager(*database->instance);
161
+ auto current_bytes = buffer_manager.GetUsedMemory();
162
+ Napi::MemoryManagement::AdjustExternalMemory(env, current_bytes - bytes_allocated);
163
+ bytes_allocated = current_bytes;
164
+ }
115
165
  }
116
166
 
117
167
  void Database::Process(Napi::Env env) {
@@ -131,8 +181,8 @@ void Database::Process(Napi::Env env) {
131
181
  holder->task = move(task);
132
182
  holder->db = this;
133
183
 
134
- napi_create_async_work(env, NULL, Napi::String::New(env, "duckdb.Database.Task"), task_execute, task_complete,
135
- holder, &holder->request);
184
+ napi_create_async_work(env, nullptr, Napi::String::New(env, "duckdb.Database.Task"), TaskExecuteCallback,
185
+ TaskCompleteCallback, holder, &holder->request);
136
186
 
137
187
  napi_queue_async_work(env, holder->request);
138
188
  }
@@ -157,7 +207,7 @@ Napi::Value Database::Serialize(const Napi::CallbackInfo &info) {
157
207
  }
158
208
 
159
209
  struct WaitTask : public Task {
160
- WaitTask(Database &database_, Napi::Function callback_) : Task(database_, callback_) {
210
+ WaitTask(Database &database, Napi::Function callback) : Task(database, callback) {
161
211
  }
162
212
 
163
213
  void DoWork() override {
@@ -171,7 +221,7 @@ Napi::Value Database::Wait(const Napi::CallbackInfo &info) {
171
221
  }
172
222
 
173
223
  struct CloseTask : public Task {
174
- CloseTask(Database &database_, Napi::Function callback_) : Task(database_, callback_) {
224
+ CloseTask(Database &database, Napi::Function callback) : Task(database, callback) {
175
225
  }
176
226
 
177
227
  void DoWork() override {