duckdb 0.7.2-dev333.0 → 0.7.2-dev402.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 (28) hide show
  1. package/binding.gyp +1 -0
  2. package/lib/duckdb.d.ts +42 -0
  3. package/package.json +1 -1
  4. package/src/connection.cpp +1 -2
  5. package/src/database.cpp +1 -1
  6. package/src/duckdb/extension/icu/icu-extension.cpp +2 -0
  7. package/src/duckdb/extension/icu/icu-list-range.cpp +207 -0
  8. package/src/duckdb/extension/icu/include/icu-list-range.hpp +17 -0
  9. package/src/duckdb/extension/json/json_functions/read_json.cpp +6 -5
  10. package/src/duckdb/src/common/exception.cpp +15 -1
  11. package/src/duckdb/src/common/preserved_error.cpp +7 -5
  12. package/src/duckdb/src/execution/operator/scan/physical_positional_scan.cpp +20 -5
  13. package/src/duckdb/src/execution/physical_plan/plan_positional_join.cpp +14 -5
  14. package/src/duckdb/src/function/cast/time_casts.cpp +2 -2
  15. package/src/duckdb/src/function/table/version/pragma_version.cpp +2 -2
  16. package/src/duckdb/src/include/duckdb/common/exception.hpp +38 -2
  17. package/src/duckdb/src/include/duckdb/common/preserved_error.hpp +3 -1
  18. package/src/duckdb/src/include/duckdb/main/prepared_statement.hpp +2 -0
  19. package/src/duckdb/src/include/duckdb/main/relation/explain_relation.hpp +2 -1
  20. package/src/duckdb/src/include/duckdb/main/relation.hpp +2 -1
  21. package/src/duckdb/src/main/extension/extension_install.cpp +2 -2
  22. package/src/duckdb/src/main/prepared_statement.cpp +4 -0
  23. package/src/duckdb/src/main/relation/explain_relation.cpp +3 -3
  24. package/src/duckdb/src/main/relation.cpp +3 -2
  25. package/src/duckdb_node.hpp +2 -1
  26. package/src/statement.cpp +5 -5
  27. package/src/utils.cpp +15 -2
  28. package/test/syntax_error.test.ts +3 -1
package/binding.gyp CHANGED
@@ -230,6 +230,7 @@
230
230
  "src/duckdb/extension/icu/./icu-datefunc.cpp",
231
231
  "src/duckdb/extension/icu/./icu-extension.cpp",
232
232
  "src/duckdb/extension/icu/./icu-makedate.cpp",
233
+ "src/duckdb/extension/icu/./icu-list-range.cpp",
233
234
  "src/duckdb/extension/icu/./icu-timezone.cpp",
234
235
  "src/duckdb/extension/icu/./icu-datesub.cpp",
235
236
  "src/duckdb/extension/icu/./icu-timebucket.cpp",
package/lib/duckdb.d.ts CHANGED
@@ -4,12 +4,54 @@
4
4
  * on Node.JS API
5
5
  */
6
6
 
7
+ export type ExceptionType =
8
+ | "Invalid" // invalid type
9
+ | "Out of Range" // value out of range error
10
+ | "Conversion" // conversion/casting error
11
+ | "Unknown Type" // unknown type
12
+ | "Decimal" // decimal related
13
+ | "Mismatch Type" // type mismatch
14
+ | "Divide by Zero" // divide by 0
15
+ | "Object Size" // object size exceeded
16
+ | "Invalid type" // incompatible for operation
17
+ | "Serialization" // serialization
18
+ | "TransactionContext" // transaction management
19
+ | "Not implemented" // method not implemented
20
+ | "Expression" // expression parsing
21
+ | "Catalog" // catalog related
22
+ | "Parser" // parser related
23
+ | "Binder" // binder related
24
+ | "Planner" // planner related
25
+ | "Scheduler" // scheduler related
26
+ | "Executor" // executor related
27
+ | "Constraint" // constraint related
28
+ | "Index" // index related
29
+ | "Stat" // stat related
30
+ | "Connection" // connection related
31
+ | "Syntax" // syntax related
32
+ | "Settings" // settings related
33
+ | "Optimizer" // optimizer related
34
+ | "NullPointer" // nullptr exception
35
+ | "IO" // IO exception
36
+ | "INTERRUPT" // interrupt
37
+ | "FATAL" // Fatal exceptions are non-recoverable and render the entire DB in an unusable state
38
+ | "INTERNAL" // Internal exceptions indicate something went wrong internally (i.e. bug in the code base)
39
+ | "Invalid Input" // Input or arguments error
40
+ | "Out of Memory" // out of memory
41
+ | "Permission" // insufficient permissions
42
+ | "Parameter Not Resolved" // parameter types could not be resolved
43
+ | "Parameter Not Allowed" // parameter types not allowed
44
+ | "Dependency" // dependency
45
+ | "Unknown"
46
+ ;
47
+
7
48
  /**
8
49
  * Standard error shape for DuckDB errors
9
50
  */
10
51
  export interface DuckDbError extends Error {
11
52
  errno: -1; // value of ERROR
12
53
  code: 'DUCKDB_NODEJS_ERROR';
54
+ errorType: ExceptionType;
13
55
  }
14
56
 
15
57
  type Callback<T> = (err: DuckDbError | null, res: T) => void;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "duckdb",
3
3
  "main": "./lib/duckdb.js",
4
4
  "types": "./lib/duckdb.d.ts",
5
- "version": "0.7.2-dev333.0",
5
+ "version": "0.7.2-dev402.0",
6
6
  "description": "DuckDB node.js API",
7
7
  "gypfile": true,
8
8
  "dependencies": {
@@ -388,8 +388,7 @@ struct ExecTask : public Task {
388
388
  void Callback() override {
389
389
  auto env = object.Env();
390
390
  Napi::HandleScope scope(env);
391
- callback.Value().MakeCallback(object.Value(),
392
- {success ? env.Null() : Utils::CreateError(env, error.Message())});
391
+ callback.Value().MakeCallback(object.Value(), {success ? env.Null() : Utils::CreateError(env, error)});
393
392
  };
394
393
 
395
394
  std::string sql;
package/src/database.cpp CHANGED
@@ -73,7 +73,7 @@ struct OpenTask : public Task {
73
73
 
74
74
  std::vector<napi_value> args;
75
75
  if (!success) {
76
- args.push_back(Utils::CreateError(env, error.Message()));
76
+ args.push_back(Utils::CreateError(env, error));
77
77
  } else {
78
78
  args.push_back(env.Null());
79
79
  }
@@ -13,6 +13,7 @@
13
13
  #include "include/icu-datesub.hpp"
14
14
  #include "include/icu-datetrunc.hpp"
15
15
  #include "include/icu-makedate.hpp"
16
+ #include "include/icu-list-range.hpp"
16
17
  #include "include/icu-table-range.hpp"
17
18
  #include "include/icu-strptime.hpp"
18
19
  #include "include/icu-timebucket.hpp"
@@ -268,6 +269,7 @@ void ICUExtension::Load(DuckDB &db) {
268
269
  RegisterICUDateTruncFunctions(*con.context);
269
270
  RegisterICUMakeDateFunctions(*con.context);
270
271
  RegisterICUTableRangeFunctions(*con.context);
272
+ RegisterICUListRangeFunctions(*con.context);
271
273
  RegisterICUStrptimeFunctions(*con.context);
272
274
  RegisterICUTimeBucketFunctions(*con.context);
273
275
  RegisterICUTimeZoneFunctions(*con.context);
@@ -0,0 +1,207 @@
1
+ #include "duckdb/common/exception.hpp"
2
+ #include "duckdb/common/types/interval.hpp"
3
+ #include "duckdb/common/types/timestamp.hpp"
4
+ #include "duckdb/common/types/vector.hpp"
5
+ #include "duckdb/function/function_set.hpp"
6
+ #include "duckdb/function/scalar_function.hpp"
7
+ #include "duckdb/main/client_context.hpp"
8
+ #include "duckdb/parser/parsed_data/create_scalar_function_info.hpp"
9
+ #include "include/icu-datefunc.hpp"
10
+
11
+ namespace duckdb {
12
+
13
+ struct ICUListRange : public ICUDateFunc {
14
+ template <bool INCLUSIVE_BOUND>
15
+ class RangeInfoStruct {
16
+ public:
17
+ explicit RangeInfoStruct(DataChunk &args_p) : args(args_p) {
18
+ if (args.ColumnCount() == 3) {
19
+ args.data[0].ToUnifiedFormat(args.size(), vdata[0]);
20
+ args.data[1].ToUnifiedFormat(args.size(), vdata[1]);
21
+ args.data[2].ToUnifiedFormat(args.size(), vdata[2]);
22
+ } else {
23
+ throw InternalException("Unsupported number of parameters for range");
24
+ }
25
+ }
26
+
27
+ bool RowIsValid(idx_t row_idx) {
28
+ for (idx_t i = 0; i < args.ColumnCount(); i++) {
29
+ auto idx = vdata[i].sel->get_index(row_idx);
30
+ if (!vdata[i].validity.RowIsValid(idx)) {
31
+ return false;
32
+ }
33
+ }
34
+ return true;
35
+ }
36
+
37
+ timestamp_t StartListValue(idx_t row_idx) {
38
+ auto data = (timestamp_t *)vdata[0].data;
39
+ auto idx = vdata[0].sel->get_index(row_idx);
40
+ return data[idx];
41
+ }
42
+
43
+ timestamp_t EndListValue(idx_t row_idx) {
44
+ auto data = (timestamp_t *)vdata[1].data;
45
+ auto idx = vdata[1].sel->get_index(row_idx);
46
+ return data[idx];
47
+ }
48
+
49
+ interval_t ListIncrementValue(idx_t row_idx) {
50
+ auto data = (interval_t *)vdata[2].data;
51
+ auto idx = vdata[2].sel->get_index(row_idx);
52
+ return data[idx];
53
+ }
54
+
55
+ void GetListValues(idx_t row_idx, timestamp_t &start_value, timestamp_t &end_value,
56
+ interval_t &increment_value) {
57
+ start_value = StartListValue(row_idx);
58
+ end_value = EndListValue(row_idx);
59
+ increment_value = ListIncrementValue(row_idx);
60
+ }
61
+
62
+ uint64_t ListLength(idx_t row_idx, icu::Calendar *calendar) {
63
+ timestamp_t start_value;
64
+ timestamp_t end_value;
65
+ interval_t increment_value;
66
+ GetListValues(row_idx, start_value, end_value, increment_value);
67
+ return ListLength(start_value, end_value, increment_value, INCLUSIVE_BOUND, calendar);
68
+ }
69
+
70
+ void Increment(timestamp_t &input, interval_t increment, icu::Calendar *calendar) {
71
+ input = Add(calendar, input, increment);
72
+ }
73
+
74
+ private:
75
+ DataChunk &args;
76
+ UnifiedVectorFormat vdata[3];
77
+
78
+ uint64_t ListLength(timestamp_t start_value, timestamp_t end_value, interval_t increment_value,
79
+ bool inclusive_bound, icu::Calendar *calendar) {
80
+ bool is_positive = increment_value.months > 0 || increment_value.days > 0 || increment_value.micros > 0;
81
+ bool is_negative = increment_value.months < 0 || increment_value.days < 0 || increment_value.micros < 0;
82
+ if (!is_negative && !is_positive) {
83
+ // interval is 0: no result
84
+ return 0;
85
+ }
86
+ // We don't allow infinite bounds because they generate errors or infinite loops
87
+ if (!Timestamp::IsFinite(start_value) || !Timestamp::IsFinite(end_value)) {
88
+ throw InvalidInputException("Interval infinite bounds not supported");
89
+ }
90
+
91
+ if (is_negative && is_positive) {
92
+ // we don't allow a mix of
93
+ throw InvalidInputException("Interval with mix of negative/positive entries not supported");
94
+ }
95
+ if (start_value > end_value && is_positive) {
96
+ return 0;
97
+ }
98
+ if (start_value < end_value && is_negative) {
99
+ return 0;
100
+ }
101
+ int64_t total_values = 0;
102
+ if (is_negative) {
103
+ // negative interval, start_value is going down
104
+ while (inclusive_bound ? start_value >= end_value : start_value > end_value) {
105
+ start_value = Add(calendar, start_value, increment_value);
106
+ total_values++;
107
+ if (total_values > NumericLimits<uint32_t>::Maximum()) {
108
+ throw InvalidInputException("Lists larger than 2^32 elements are not supported");
109
+ }
110
+ }
111
+ } else {
112
+ // positive interval, start_value is going up
113
+ while (inclusive_bound ? start_value <= end_value : start_value < end_value) {
114
+ start_value = Add(calendar, start_value, increment_value);
115
+ total_values++;
116
+ if (total_values > NumericLimits<uint32_t>::Maximum()) {
117
+ throw InvalidInputException("Lists larger than 2^32 elements are not supported");
118
+ }
119
+ }
120
+ }
121
+ return total_values;
122
+ }
123
+ };
124
+
125
+ template <bool INCLUSIVE_BOUND>
126
+ static void ICUListRangeFunction(DataChunk &args, ExpressionState &state, Vector &result) {
127
+ D_ASSERT(result.GetType().id() == LogicalTypeId::LIST);
128
+ D_ASSERT(args.ColumnCount() == 3);
129
+
130
+ auto &func_expr = (BoundFunctionExpression &)state.expr;
131
+ auto &bind_info = (BindData &)*func_expr.bind_info;
132
+ CalendarPtr calendar_ptr(bind_info.calendar->clone());
133
+ auto calendar = calendar_ptr.get();
134
+
135
+ RangeInfoStruct<INCLUSIVE_BOUND> info(args);
136
+ idx_t args_size = 1;
137
+ auto result_type = VectorType::CONSTANT_VECTOR;
138
+ for (idx_t i = 0; i < args.ColumnCount(); i++) {
139
+ if (args.data[i].GetVectorType() != VectorType::CONSTANT_VECTOR) {
140
+ args_size = args.size();
141
+ result_type = VectorType::FLAT_VECTOR;
142
+ break;
143
+ }
144
+ }
145
+ auto list_data = FlatVector::GetData<list_entry_t>(result);
146
+ auto &result_validity = FlatVector::Validity(result);
147
+ int64_t total_size = 0;
148
+ for (idx_t i = 0; i < args_size; i++) {
149
+ if (!info.RowIsValid(i)) {
150
+ result_validity.SetInvalid(i);
151
+ list_data[i].offset = total_size;
152
+ list_data[i].length = 0;
153
+ } else {
154
+ list_data[i].offset = total_size;
155
+ list_data[i].length = info.ListLength(i, calendar);
156
+ total_size += list_data[i].length;
157
+ }
158
+ }
159
+
160
+ // now construct the child vector of the list
161
+ ListVector::Reserve(result, total_size);
162
+ auto range_data = FlatVector::GetData<timestamp_t>(ListVector::GetEntry(result));
163
+ idx_t total_idx = 0;
164
+ for (idx_t i = 0; i < args_size; i++) {
165
+ timestamp_t start_value = info.StartListValue(i);
166
+ interval_t increment = info.ListIncrementValue(i);
167
+
168
+ timestamp_t range_value = start_value;
169
+ for (idx_t range_idx = 0; range_idx < list_data[i].length; range_idx++) {
170
+ if (range_idx > 0) {
171
+ info.Increment(range_value, increment, calendar);
172
+ }
173
+ range_data[total_idx++] = range_value;
174
+ }
175
+ }
176
+
177
+ ListVector::SetListSize(result, total_size);
178
+ result.SetVectorType(result_type);
179
+
180
+ result.Verify(args.size());
181
+ }
182
+
183
+ static void AddICUListRangeFunction(ClientContext &context) {
184
+ auto &catalog = Catalog::GetSystemCatalog(context);
185
+
186
+ ScalarFunctionSet range("range");
187
+ range.AddFunction(ScalarFunction({LogicalType::TIMESTAMP_TZ, LogicalType::TIMESTAMP_TZ, LogicalType::INTERVAL},
188
+ LogicalType::LIST(LogicalType::TIMESTAMP_TZ), ICUListRangeFunction<false>,
189
+ Bind));
190
+ CreateScalarFunctionInfo range_func_info(range);
191
+ catalog.AddFunction(context, &range_func_info);
192
+
193
+ // generate_series: similar to range, but inclusive instead of exclusive bounds on the RHS
194
+ ScalarFunctionSet generate_series("generate_series");
195
+ generate_series.AddFunction(
196
+ ScalarFunction({LogicalType::TIMESTAMP_TZ, LogicalType::TIMESTAMP_TZ, LogicalType::INTERVAL},
197
+ LogicalType::LIST(LogicalType::TIMESTAMP_TZ), ICUListRangeFunction<true>, Bind));
198
+ CreateScalarFunctionInfo generate_series_func_info(generate_series);
199
+ catalog.AddFunction(context, &generate_series_func_info);
200
+ }
201
+ };
202
+
203
+ void RegisterICUListRangeFunctions(ClientContext &context) {
204
+ ICUListRange::AddICUListRangeFunction(context);
205
+ }
206
+
207
+ } // namespace duckdb
@@ -0,0 +1,17 @@
1
+ //===----------------------------------------------------------------------===//
2
+ // DuckDB
3
+ //
4
+ // icu-list-range.hpp
5
+ //
6
+ //
7
+ //===----------------------------------------------------------------------===//
8
+
9
+ #pragma once
10
+
11
+ #include "duckdb.hpp"
12
+
13
+ namespace duckdb {
14
+
15
+ void RegisterICUListRangeFunctions(ClientContext &context);
16
+
17
+ } // namespace duckdb
@@ -20,6 +20,12 @@ void JSONScan::AutoDetect(ClientContext &context, JSONScanData &bind_data, vecto
20
20
  idx_t remaining = bind_data.sample_size;
21
21
  while (remaining != 0) {
22
22
  allocator.Reset();
23
+
24
+ if (gstate.file_index >= 10) {
25
+ // We really shouldn't open more than 10 files when sampling
26
+ break;
27
+ }
28
+
23
29
  auto read_count = lstate.ReadNext(gstate);
24
30
  if (lstate.scan_count > 1) {
25
31
  more_than_one = true;
@@ -46,11 +52,6 @@ void JSONScan::AutoDetect(ClientContext &context, JSONScanData &bind_data, vecto
46
52
  node.InitializeCandidateTypes(bind_data.max_depth);
47
53
  node.RefineCandidateTypes(values, next, string_vector, allocator, bind_data.date_format_map);
48
54
  remaining -= next;
49
-
50
- if (gstate.file_index == 10) {
51
- // We really shouldn't open more than 10 files when sampling
52
- break;
53
- }
54
55
  }
55
56
  bind_data.type = original_scan_type;
56
57
 
@@ -138,12 +138,22 @@ string Exception::ExceptionTypeToString(ExceptionType type) {
138
138
  return "Parameter Not Allowed";
139
139
  case ExceptionType::DEPENDENCY:
140
140
  return "Dependency";
141
+ case ExceptionType::HTTP:
142
+ return "HTTP";
141
143
  default:
142
144
  return "Unknown";
143
145
  }
144
146
  }
145
147
 
146
- void Exception::ThrowAsTypeWithMessage(ExceptionType type, const string &message) {
148
+ const HTTPException &Exception::AsHTTPException() const {
149
+ D_ASSERT(type == ExceptionType::HTTP);
150
+ const auto &e = static_cast<const HTTPException *>(this);
151
+ D_ASSERT(e->GetStatusCode() != 0);
152
+ return *e;
153
+ }
154
+
155
+ void Exception::ThrowAsTypeWithMessage(ExceptionType type, const string &message,
156
+ const std::shared_ptr<Exception> &original) {
147
157
  switch (type) {
148
158
  case ExceptionType::OUT_OF_RANGE:
149
159
  throw OutOfRangeException(message);
@@ -191,6 +201,10 @@ void Exception::ThrowAsTypeWithMessage(ExceptionType type, const string &message
191
201
  throw FatalException(message);
192
202
  case ExceptionType::DEPENDENCY:
193
203
  throw DependencyException(message);
204
+ case ExceptionType::HTTP: {
205
+ auto exc = original->AsHTTPException();
206
+ throw HTTPException(exc.GetStatusCode(), exc.GetResponse(), exc.what());
207
+ }
194
208
  default:
195
209
  throw Exception(type, message);
196
210
  }
@@ -7,15 +7,17 @@
7
7
 
8
8
  namespace duckdb {
9
9
 
10
- PreservedError::PreservedError() : initialized(false) {
10
+ PreservedError::PreservedError() : initialized(false), exception_instance(nullptr) {
11
11
  }
12
12
 
13
13
  PreservedError::PreservedError(const Exception &exception)
14
- : initialized(true), type(exception.type), raw_message(SanitizeErrorMessage(exception.RawMessage())) {
14
+ : initialized(true), type(exception.type), raw_message(SanitizeErrorMessage(exception.RawMessage())),
15
+ exception_instance(exception.Copy()) {
15
16
  }
16
17
 
17
18
  PreservedError::PreservedError(const string &message)
18
- : initialized(true), type(ExceptionType::INVALID), raw_message(SanitizeErrorMessage(message)) {
19
+ : initialized(true), type(ExceptionType::INVALID), raw_message(SanitizeErrorMessage(message)),
20
+ exception_instance(nullptr) {
19
21
  }
20
22
 
21
23
  const string &PreservedError::Message() {
@@ -33,9 +35,9 @@ void PreservedError::Throw(const string &prepended_message) const {
33
35
  D_ASSERT(initialized);
34
36
  if (!prepended_message.empty()) {
35
37
  string new_message = prepended_message + raw_message;
36
- Exception::ThrowAsTypeWithMessage(type, new_message);
38
+ Exception::ThrowAsTypeWithMessage(type, new_message, exception_instance);
37
39
  }
38
- Exception::ThrowAsTypeWithMessage(type, raw_message);
40
+ Exception::ThrowAsTypeWithMessage(type, raw_message, exception_instance);
39
41
  }
40
42
 
41
43
  const ExceptionType &PreservedError::Type() const {
@@ -12,13 +12,28 @@ namespace duckdb {
12
12
  PhysicalPositionalScan::PhysicalPositionalScan(vector<LogicalType> types, unique_ptr<PhysicalOperator> left,
13
13
  unique_ptr<PhysicalOperator> right)
14
14
  : PhysicalOperator(PhysicalOperatorType::POSITIONAL_SCAN, std::move(types),
15
- MinValue(left->estimated_cardinality, right->estimated_cardinality)) {
15
+ MaxValue(left->estimated_cardinality, right->estimated_cardinality)) {
16
16
 
17
17
  // Manage the children ourselves
18
- D_ASSERT(left->type == PhysicalOperatorType::TABLE_SCAN);
19
- D_ASSERT(right->type == PhysicalOperatorType::TABLE_SCAN);
20
- child_tables.emplace_back(std::move(left));
21
- child_tables.emplace_back(std::move(right));
18
+ if (left->type == PhysicalOperatorType::TABLE_SCAN) {
19
+ child_tables.emplace_back(std::move(left));
20
+ } else if (left->type == PhysicalOperatorType::POSITIONAL_SCAN) {
21
+ auto &left_scan = (PhysicalPositionalScan &)*left;
22
+ child_tables = std::move(left_scan.child_tables);
23
+ } else {
24
+ throw InternalException("Invalid left input for PhysicalPositionalScan");
25
+ }
26
+
27
+ if (right->type == PhysicalOperatorType::TABLE_SCAN) {
28
+ child_tables.emplace_back(std::move(right));
29
+ } else if (right->type == PhysicalOperatorType::POSITIONAL_SCAN) {
30
+ auto &right_scan = (PhysicalPositionalScan &)*right;
31
+ auto &right_tables = right_scan.child_tables;
32
+ child_tables.reserve(child_tables.size() + right_tables.size());
33
+ std::move(right_tables.begin(), right_tables.end(), std::back_inserter(child_tables));
34
+ } else {
35
+ throw InternalException("Invalid right input for PhysicalPositionalScan");
36
+ }
22
37
  }
23
38
 
24
39
  class PositionalScanGlobalSourceState : public GlobalSourceState {
@@ -10,12 +10,21 @@ unique_ptr<PhysicalOperator> PhysicalPlanGenerator::CreatePlan(LogicalPositional
10
10
 
11
11
  auto left = CreatePlan(*op.children[0]);
12
12
  auto right = CreatePlan(*op.children[1]);
13
- if (left->type == PhysicalOperatorType::TABLE_SCAN && right->type == PhysicalOperatorType::TABLE_SCAN) {
14
- return make_unique<PhysicalPositionalScan>(op.types, std::move(left), std::move(right));
15
- } else {
16
- return make_unique<PhysicalPositionalJoin>(op.types, std::move(left), std::move(right),
17
- op.estimated_cardinality);
13
+ switch (left->type) {
14
+ case PhysicalOperatorType::TABLE_SCAN:
15
+ case PhysicalOperatorType::POSITIONAL_SCAN:
16
+ switch (right->type) {
17
+ case PhysicalOperatorType::TABLE_SCAN:
18
+ case PhysicalOperatorType::POSITIONAL_SCAN:
19
+ return make_unique<PhysicalPositionalScan>(op.types, std::move(left), std::move(right));
20
+ default:
21
+ break;
22
+ }
23
+ default:
24
+ break;
18
25
  }
26
+
27
+ return make_unique<PhysicalPositionalJoin>(op.types, std::move(left), std::move(right), op.estimated_cardinality);
19
28
  }
20
29
 
21
30
  } // namespace duckdb
@@ -69,7 +69,7 @@ BoundCastInfo DefaultCasts::TimestampCastSwitch(BindCastInput &input, const Logi
69
69
  return BoundCastInfo(&VectorCastHelpers::TemplatedCastLoop<timestamp_t, dtime_t, duckdb::Cast>);
70
70
  case LogicalTypeId::TIMESTAMP_TZ:
71
71
  // timestamp (us) to timestamp with time zone
72
- return BoundCastInfo(&VectorCastHelpers::TemplatedCastLoop<timestamp_t, timestamp_t, duckdb::Cast>);
72
+ return ReinterpretCast;
73
73
  case LogicalTypeId::TIMESTAMP_NS:
74
74
  // timestamp (us) to timestamp (ns)
75
75
  return BoundCastInfo(
@@ -100,7 +100,7 @@ BoundCastInfo DefaultCasts::TimestampTzCastSwitch(BindCastInput &input, const Lo
100
100
  return BoundCastInfo(&VectorCastHelpers::TemplatedCastLoop<timestamp_t, dtime_t, duckdb::Cast>);
101
101
  case LogicalTypeId::TIMESTAMP:
102
102
  // timestamp with time zone to timestamp (us)
103
- return BoundCastInfo(&VectorCastHelpers::TemplatedCastLoop<timestamp_t, timestamp_t, duckdb::Cast>);
103
+ return ReinterpretCast;
104
104
  default:
105
105
  return TryVectorNullCast;
106
106
  }
@@ -1,8 +1,8 @@
1
1
  #ifndef DUCKDB_VERSION
2
- #define DUCKDB_VERSION "0.7.2-dev333"
2
+ #define DUCKDB_VERSION "0.7.2-dev402"
3
3
  #endif
4
4
  #ifndef DUCKDB_SOURCE_ID
5
- #define DUCKDB_SOURCE_ID "1fdfe867bb"
5
+ #define DUCKDB_SOURCE_ID "ab9736bed0"
6
6
  #endif
7
7
  #include "duckdb/function/table/system_functions.hpp"
8
8
  #include "duckdb/main/database.hpp"
@@ -76,8 +76,10 @@ enum class ExceptionType {
76
76
  PERMISSION = 34, // insufficient permissions
77
77
  PARAMETER_NOT_RESOLVED = 35, // parameter types could not be resolved
78
78
  PARAMETER_NOT_ALLOWED = 36, // parameter types not allowed
79
- DEPENDENCY = 37 // dependency
79
+ DEPENDENCY = 37, // dependency
80
+ HTTP = 38
80
81
  };
82
+ class HTTPException;
81
83
 
82
84
  class Exception : public std::exception {
83
85
  public:
@@ -91,7 +93,12 @@ public:
91
93
  DUCKDB_API const string &RawMessage() const;
92
94
 
93
95
  DUCKDB_API static string ExceptionTypeToString(ExceptionType type);
94
- [[noreturn]] DUCKDB_API static void ThrowAsTypeWithMessage(ExceptionType type, const string &message);
96
+ [[noreturn]] DUCKDB_API static void ThrowAsTypeWithMessage(ExceptionType type, const string &message,
97
+ const std::shared_ptr<Exception> &original);
98
+ virtual std::shared_ptr<Exception> Copy() const {
99
+ return make_shared<Exception>(type, raw_message_);
100
+ }
101
+ DUCKDB_API const HTTPException &AsHTTPException() const;
95
102
 
96
103
  template <typename... Args>
97
104
  static string ConstructMessage(const string &msg, Args... params) {
@@ -256,6 +263,8 @@ public:
256
263
  class IOException : public Exception {
257
264
  public:
258
265
  DUCKDB_API explicit IOException(const string &msg);
266
+ DUCKDB_API explicit IOException(ExceptionType exception_type, const string &msg) : Exception(exception_type, msg) {
267
+ }
259
268
 
260
269
  template <typename... Args>
261
270
  explicit IOException(const string &msg, Args... params) : IOException(ConstructMessage(msg, params...)) {
@@ -272,6 +281,33 @@ public:
272
281
  }
273
282
  };
274
283
 
284
+ class HTTPException : public IOException {
285
+ public:
286
+ DUCKDB_API explicit HTTPException(int status_code, string response, const string &msg)
287
+ : IOException(ExceptionType::HTTP, msg), status_code(status_code), response(std::move(response)) {
288
+ }
289
+
290
+ template <typename... ARGS>
291
+ explicit HTTPException(int status_code, string response, const string &msg, ARGS... params)
292
+ : HTTPException(status_code, std::move(response), ConstructMessage(msg, params...)) {
293
+ }
294
+
295
+ std::shared_ptr<Exception> Copy() const {
296
+ return make_shared<HTTPException>(status_code, response, RawMessage());
297
+ }
298
+
299
+ int GetStatusCode() const {
300
+ return status_code;
301
+ }
302
+ const string &GetResponse() const {
303
+ return response;
304
+ }
305
+
306
+ private:
307
+ int status_code;
308
+ string response; // we can keep a copy for the user
309
+ };
310
+
275
311
  class SerializationException : public Exception {
276
312
  public:
277
313
  DUCKDB_API explicit SerializationException(const string &msg);
@@ -19,7 +19,8 @@ public:
19
19
  DUCKDB_API PreservedError();
20
20
  //! From std::exception
21
21
  PreservedError(const std::exception &ex)
22
- : initialized(true), type(ExceptionType::INVALID), raw_message(SanitizeErrorMessage(ex.what())) {
22
+ : initialized(true), type(ExceptionType::INVALID), raw_message(SanitizeErrorMessage(ex.what())),
23
+ exception_instance(nullptr) {
23
24
  }
24
25
  //! From a raw string
25
26
  DUCKDB_API explicit PreservedError(const string &raw_message);
@@ -48,6 +49,7 @@ private:
48
49
  string raw_message;
49
50
  //! The final message (stored in the preserved error for compatibility reasons with C-API)
50
51
  string final_message;
52
+ std::shared_ptr<Exception> exception_instance;
51
53
 
52
54
  private:
53
55
  DUCKDB_API static string SanitizeErrorMessage(string error);
@@ -48,6 +48,8 @@ public:
48
48
  public:
49
49
  //! Returns the stored error message
50
50
  DUCKDB_API const string &GetError();
51
+ //! Returns the stored error object
52
+ DUCKDB_API PreservedError &GetErrorObject();
51
53
  //! Returns whether or not an error occurred
52
54
  DUCKDB_API bool HasError() const;
53
55
  //! Returns the number of columns in the result
@@ -14,10 +14,11 @@ namespace duckdb {
14
14
 
15
15
  class ExplainRelation : public Relation {
16
16
  public:
17
- explicit ExplainRelation(shared_ptr<Relation> child);
17
+ explicit ExplainRelation(shared_ptr<Relation> child, ExplainType type = ExplainType::EXPLAIN_STANDARD);
18
18
 
19
19
  shared_ptr<Relation> child;
20
20
  vector<ColumnDefinition> columns;
21
+ ExplainType type;
21
22
 
22
23
  public:
23
24
  BoundStatement Bind(Binder &binder) override;
@@ -17,6 +17,7 @@
17
17
  #include "duckdb/common/named_parameter_map.hpp"
18
18
  #include "duckdb/main/client_context.hpp"
19
19
  #include "duckdb/main/external_dependencies.hpp"
20
+ #include "duckdb/parser/statement/explain_statement.hpp"
20
21
 
21
22
  #include <memory>
22
23
 
@@ -66,7 +67,7 @@ public:
66
67
  DUCKDB_API unique_ptr<QueryResult> Query(const string &name, const string &sql);
67
68
 
68
69
  //! Explain the query plan of this relation
69
- DUCKDB_API unique_ptr<QueryResult> Explain();
70
+ DUCKDB_API unique_ptr<QueryResult> Explain(ExplainType type = ExplainType::EXPLAIN_STANDARD);
70
71
 
71
72
  DUCKDB_API virtual unique_ptr<TableRef> GetTableRef();
72
73
  DUCKDB_API virtual bool IsReadOnly() {
@@ -206,8 +206,8 @@ void ExtensionHelper::InstallExtensionInternal(DBConfig &config, ClientConfig *c
206
206
  if (exact_match) {
207
207
  message += "\nAre you using a development build? In this case, extensions might not (yet) be uploaded.";
208
208
  }
209
- throw IOException("Failed to download extension \"%s\" at URL \"%s%s\"\n%s", extension_name, url_base,
210
- url_local_part, message);
209
+ throw HTTPException(res.value().status, res->body, "Failed to download extension \"%s\" at URL \"%s%s\"\n%s",
210
+ extension_name, url_base, url_local_part, message);
211
211
  }
212
212
  auto decompressed_body = GZipFileSystem::UncompressGZIPString(res->body);
213
213
  std::ofstream out(temp_path, std::ios::binary);
@@ -23,6 +23,10 @@ const string &PreparedStatement::GetError() {
23
23
  return error.Message();
24
24
  }
25
25
 
26
+ PreservedError &PreparedStatement::GetErrorObject() {
27
+ return error;
28
+ }
29
+
26
30
  bool PreparedStatement::HasError() const {
27
31
  return !success;
28
32
  }
@@ -7,15 +7,15 @@
7
7
 
8
8
  namespace duckdb {
9
9
 
10
- ExplainRelation::ExplainRelation(shared_ptr<Relation> child_p)
11
- : Relation(child_p->context, RelationType::EXPLAIN_RELATION), child(std::move(child_p)) {
10
+ ExplainRelation::ExplainRelation(shared_ptr<Relation> child_p, ExplainType type)
11
+ : Relation(child_p->context, RelationType::EXPLAIN_RELATION), child(std::move(child_p)), type(type) {
12
12
  context.GetContext()->TryBindRelation(*this, this->columns);
13
13
  }
14
14
 
15
15
  BoundStatement ExplainRelation::Bind(Binder &binder) {
16
16
  auto select = make_unique<SelectStatement>();
17
17
  select->node = child->GetQueryNode();
18
- ExplainStatement explain(std::move(select));
18
+ ExplainStatement explain(std::move(select), type);
19
19
  return binder.Bind((SQLStatement &)explain);
20
20
  }
21
21
 
@@ -25,6 +25,7 @@
25
25
  #include "duckdb/parser/expression/columnref_expression.hpp"
26
26
  #include "duckdb/main/relation/join_relation.hpp"
27
27
  #include "duckdb/main/relation/value_relation.hpp"
28
+ #include "duckdb/parser/statement/explain_statement.hpp"
28
29
 
29
30
  namespace duckdb {
30
31
 
@@ -303,8 +304,8 @@ unique_ptr<QueryResult> Relation::Query(const string &name, const string &sql) {
303
304
  return Query(sql);
304
305
  }
305
306
 
306
- unique_ptr<QueryResult> Relation::Explain() {
307
- auto explain = make_shared<ExplainRelation>(shared_from_this());
307
+ unique_ptr<QueryResult> Relation::Explain(ExplainType type) {
308
+ auto explain = make_shared<ExplainRelation>(shared_from_this(), type);
308
309
  return explain->Execute();
309
310
  }
310
311
 
@@ -198,7 +198,8 @@ struct TaskHolder {
198
198
 
199
199
  class Utils {
200
200
  public:
201
- static Napi::Value CreateError(Napi::Env env, std::string msg);
201
+ static Napi::Object CreateError(Napi::Env env, duckdb::PreservedError &e);
202
+ static Napi::Object CreateError(Napi::Env env, std::string msg);
202
203
  static bool OtherIsInt(Napi::Number source);
203
204
 
204
205
  template <class T>
package/src/statement.cpp CHANGED
@@ -78,7 +78,7 @@ struct PrepareTask : public Task {
78
78
 
79
79
  auto cb = callback.Value();
80
80
  if (statement.statement->HasError()) {
81
- cb.MakeCallback(statement.Value(), {Utils::CreateError(env, statement.statement->error.Message())});
81
+ cb.MakeCallback(statement.Value(), {Utils::CreateError(env, statement.statement->error)});
82
82
  return;
83
83
  }
84
84
  cb.MakeCallback(statement.Value(), {env.Null(), statement.Value()});
@@ -282,11 +282,11 @@ struct RunPreparedTask : public Task {
282
282
  return;
283
283
  }
284
284
  if (statement.statement->HasError()) {
285
- cb.MakeCallback(statement.Value(), {Utils::CreateError(env, statement.statement->GetError())});
285
+ cb.MakeCallback(statement.Value(), {Utils::CreateError(env, statement.statement->GetErrorObject())});
286
286
  return;
287
287
  }
288
288
  if (result->HasError()) {
289
- cb.MakeCallback(statement.Value(), {Utils::CreateError(env, result->GetError())});
289
+ cb.MakeCallback(statement.Value(), {Utils::CreateError(env, result->GetErrorObject())});
290
290
  return;
291
291
  }
292
292
 
@@ -427,9 +427,9 @@ struct RunQueryTask : public Task {
427
427
  if (!statement.statement) {
428
428
  deferred.Reject(Utils::CreateError(env, "statement was finalized"));
429
429
  } else if (statement.statement->HasError()) {
430
- deferred.Reject(Utils::CreateError(env, statement.statement->GetError()));
430
+ deferred.Reject(Utils::CreateError(env, statement.statement->GetErrorObject()));
431
431
  } else if (result->HasError()) {
432
- deferred.Reject(Utils::CreateError(env, result->GetError()));
432
+ deferred.Reject(Utils::CreateError(env, result->GetErrorObject()));
433
433
  } else {
434
434
  auto db = statement.connection_ref->database_ref->Value();
435
435
  auto query_result = QueryResult::constructor.New({db});
package/src/utils.cpp CHANGED
@@ -12,11 +12,24 @@ bool Utils::OtherIsInt(Napi::Number source) {
12
12
  }
13
13
  }
14
14
 
15
- Napi::Value Utils::CreateError(Napi::Env env, std::string msg) {
15
+ static void SetString(Napi::Object &obj, const std::string &key, const std::string &value) {
16
+ obj.Set(Napi::String::New(obj.Env(), key), Napi::String::New(obj.Env(), value));
17
+ }
18
+
19
+ Napi::Object Utils::CreateError(Napi::Env env, duckdb::PreservedError &error) {
20
+ auto obj = Utils::CreateError(env, error.Message());
21
+
22
+ SetString(obj, "errorType", duckdb::Exception::ExceptionTypeToString(error.Type()));
23
+
24
+ return obj;
25
+ }
26
+
27
+ Napi::Object Utils::CreateError(Napi::Env env, std::string msg) {
16
28
  auto err = Napi::Error::New(env, Napi::String::New(env, msg).Utf8Value()).Value();
17
29
  Napi::Object obj = err.As<Napi::Object>();
18
30
  obj.Set(Napi::String::New(env, "errno"), Napi::Number::New(env, Database::DUCKDB_NODEJS_ERROR));
19
- obj.Set(Napi::String::New(env, "code"), Napi::String::New(env, "DUCKDB_NODEJS_ERROR"));
31
+ SetString(obj, "code", "DUCKDB_NODEJS_ERROR");
32
+ SetString(obj, "errorType", "Invalid");
20
33
 
21
34
  return obj;
22
35
  }
@@ -8,8 +8,10 @@ describe('exec', function() {
8
8
  });
9
9
 
10
10
  it("doesn't crash on a syntax error", function(done) {
11
- db.exec("syntax error", function(err: null | Error) {
11
+ db.exec("syntax error", function(err: null | duckdb.DuckDbError) {
12
12
  assert.notEqual(err, null, "Expected an error")
13
+ assert.equal(err?.errorType, 'Parser');
14
+ assert.ok(err?.message.startsWith('Parser Error: syntax error at or near "syntax"'))
13
15
  done();
14
16
  });
15
17
  });