duckdb 0.8.2-dev5002.0 → 0.8.2-dev5080.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/package.json +1 -1
- package/src/duckdb/extension/icu/icu-strptime.cpp +1 -0
- package/src/duckdb/extension/json/json_functions/copy_json.cpp +1 -1
- package/src/duckdb/src/common/enum_util.cpp +5 -0
- package/src/duckdb/src/common/types/data_chunk.cpp +1 -1
- package/src/duckdb/src/common/types.cpp +9 -0
- package/src/duckdb/src/core_functions/scalar/date/strftime.cpp +2 -2
- package/src/duckdb/src/core_functions/scalar/list/array_slice.cpp +13 -7
- package/src/duckdb/src/core_functions/scalar/struct/struct_pack.cpp +12 -6
- package/src/duckdb/src/execution/operator/csv_scanner/parallel_csv_reader.cpp +0 -2
- package/src/duckdb/src/execution/operator/csv_scanner/sniffer/type_detection.cpp +14 -2
- package/src/duckdb/src/execution/operator/persistent/physical_export.cpp +1 -1
- package/src/duckdb/src/execution/physical_plan/plan_comparison_join.cpp +13 -8
- package/src/duckdb/src/function/cast/struct_cast.cpp +8 -0
- package/src/duckdb/src/function/cast/union_casts.cpp +5 -0
- package/src/duckdb/src/function/function_binder.cpp +11 -2
- package/src/duckdb/src/function/pragma/pragma_functions.cpp +5 -0
- package/src/duckdb/src/function/scalar/strftime_format.cpp +29 -8
- package/src/duckdb/src/function/table/arrow.cpp +4 -0
- package/src/duckdb/src/function/table/copy_csv.cpp +2 -1
- package/src/duckdb/src/function/table/read_csv.cpp +4 -1
- package/src/duckdb/src/function/table/version/pragma_version.cpp +2 -2
- package/src/duckdb/src/include/duckdb/common/types.hpp +1 -0
- package/src/duckdb/src/include/duckdb/function/scalar/strftime_format.hpp +2 -1
- package/src/duckdb/src/include/duckdb/main/client_config.hpp +2 -0
- package/src/duckdb/src/include/duckdb/main/prepared_statement_data.hpp +1 -0
- package/src/duckdb/src/include/duckdb/planner/bound_parameter_map.hpp +28 -1
- package/src/duckdb/src/include/duckdb/planner/expression/bound_parameter_data.hpp +0 -18
- package/src/duckdb/src/include/duckdb/planner/expression/bound_parameter_expression.hpp +1 -1
- package/src/duckdb/src/include/duckdb/planner/planner.hpp +1 -1
- package/src/duckdb/src/main/capi/prepared-c.cpp +9 -3
- package/src/duckdb/src/planner/binder/expression/bind_comparison_expression.cpp +1 -0
- package/src/duckdb/src/planner/binder/expression/bind_parameter_expression.cpp +9 -19
- package/src/duckdb/src/planner/bound_parameter_map.cpp +67 -0
- package/src/duckdb/src/planner/planner.cpp +2 -2
- package/src/duckdb/src/storage/table/struct_column_data.cpp +3 -0
- package/src/duckdb/ub_src_planner.cpp +2 -0
package/package.json
CHANGED
@@ -75,6 +75,7 @@ struct ICUStrptime : public ICUDateFunc {
|
|
75
75
|
calendar->set(UCAL_MINUTE, parsed.data[4]);
|
76
76
|
calendar->set(UCAL_SECOND, parsed.data[5]);
|
77
77
|
calendar->set(UCAL_MILLISECOND, parsed.data[6] / Interval::MICROS_PER_MSEC);
|
78
|
+
micros = parsed.data[6] % Interval::MICROS_PER_MSEC;
|
78
79
|
|
79
80
|
// This overrides the TZ setting, so only use it if an offset was parsed.
|
80
81
|
// Note that we don't bother/worry about the DST setting because the two just combine.
|
@@ -101,7 +101,7 @@ static BoundStatement CopyToJSONPlan(Binder &binder, CopyStatement &stmt) {
|
|
101
101
|
info.options["quote"] = {""};
|
102
102
|
info.options["escape"] = {""};
|
103
103
|
info.options["delimiter"] = {"\n"};
|
104
|
-
info.options["header"] = {0};
|
104
|
+
info.options["header"] = {{0}};
|
105
105
|
|
106
106
|
return binder.Bind(*stmt_copy);
|
107
107
|
}
|
@@ -5503,6 +5503,8 @@ const char* EnumUtil::ToChars<StrTimeSpecifier>(StrTimeSpecifier value) {
|
|
5503
5503
|
return "LOCALE_APPROPRIATE_DATE";
|
5504
5504
|
case StrTimeSpecifier::LOCALE_APPROPRIATE_TIME:
|
5505
5505
|
return "LOCALE_APPROPRIATE_TIME";
|
5506
|
+
case StrTimeSpecifier::NANOSECOND_PADDED:
|
5507
|
+
return "NANOSECOND_PADDED";
|
5506
5508
|
default:
|
5507
5509
|
throw NotImplementedException(StringUtil::Format("Enum value: '%d' not implemented", value));
|
5508
5510
|
}
|
@@ -5606,6 +5608,9 @@ StrTimeSpecifier EnumUtil::FromString<StrTimeSpecifier>(const char *value) {
|
|
5606
5608
|
if (StringUtil::Equals(value, "LOCALE_APPROPRIATE_TIME")) {
|
5607
5609
|
return StrTimeSpecifier::LOCALE_APPROPRIATE_TIME;
|
5608
5610
|
}
|
5611
|
+
if (StringUtil::Equals(value, "NANOSECOND_PADDED")) {
|
5612
|
+
return StrTimeSpecifier::NANOSECOND_PADDED;
|
5613
|
+
}
|
5609
5614
|
throw NotImplementedException(StringUtil::Format("Enum value: '%s' not implemented", value));
|
5610
5615
|
}
|
5611
5616
|
|
@@ -263,7 +263,6 @@ void DataChunk::Deserialize(Deserializer &deserializer) {
|
|
263
263
|
|
264
264
|
// read and set the row count
|
265
265
|
auto row_count = deserializer.ReadProperty<sel_t>(100, "rows");
|
266
|
-
SetCardinality(row_count);
|
267
266
|
|
268
267
|
// read the types
|
269
268
|
vector<LogicalType> types;
|
@@ -275,6 +274,7 @@ void DataChunk::Deserialize(Deserializer &deserializer) {
|
|
275
274
|
// initialize the data chunk
|
276
275
|
D_ASSERT(!types.empty());
|
277
276
|
Initialize(Allocator::DefaultAllocator(), types);
|
277
|
+
SetCardinality(row_count);
|
278
278
|
|
279
279
|
// read the data
|
280
280
|
deserializer.ReadList(102, "columns", [&](Deserializer::List &list, idx_t i) {
|
@@ -659,6 +659,10 @@ LogicalType LogicalType::MaxLogicalType(const LogicalType &left, const LogicalTy
|
|
659
659
|
return right;
|
660
660
|
} else if (right.id() == LogicalTypeId::UNKNOWN) {
|
661
661
|
return left;
|
662
|
+
} else if ((right.id() == LogicalTypeId::ENUM || left.id() == LogicalTypeId::ENUM) && right.id() != left.id()) {
|
663
|
+
// if one is an enum and the other is not, compare strings, not enums
|
664
|
+
// see https://github.com/duckdb/duckdb/issues/8561
|
665
|
+
return LogicalTypeId::VARCHAR;
|
662
666
|
} else if (left.id() < right.id()) {
|
663
667
|
return right;
|
664
668
|
}
|
@@ -911,6 +915,11 @@ const string &StructType::GetChildName(const LogicalType &type, idx_t index) {
|
|
911
915
|
idx_t StructType::GetChildCount(const LogicalType &type) {
|
912
916
|
return StructType::GetChildTypes(type).size();
|
913
917
|
}
|
918
|
+
bool StructType::IsUnnamed(const LogicalType &type) {
|
919
|
+
auto &child_types = StructType::GetChildTypes(type);
|
920
|
+
D_ASSERT(child_types.size() > 0);
|
921
|
+
return child_types[0].first.empty();
|
922
|
+
}
|
914
923
|
|
915
924
|
LogicalType LogicalType::STRUCT(child_list_t<LogicalType> children) {
|
916
925
|
auto info = make_shared<StructTypeInfo>(std::move(children));
|
@@ -183,7 +183,7 @@ struct StrpTimeFunction {
|
|
183
183
|
auto &func_expr = state.expr.Cast<BoundFunctionExpression>();
|
184
184
|
auto &info = func_expr.bind_info->Cast<StrpTimeBindData>();
|
185
185
|
|
186
|
-
if (ConstantVector::IsNull(args.data[1])) {
|
186
|
+
if (args.data[1].GetVectorType() == VectorType::CONSTANT_VECTOR && ConstantVector::IsNull(args.data[1])) {
|
187
187
|
result.SetVectorType(VectorType::CONSTANT_VECTOR);
|
188
188
|
ConstantVector::SetNull(result, true);
|
189
189
|
return;
|
@@ -203,7 +203,7 @@ struct StrpTimeFunction {
|
|
203
203
|
auto &func_expr = state.expr.Cast<BoundFunctionExpression>();
|
204
204
|
auto &info = func_expr.bind_info->Cast<StrpTimeBindData>();
|
205
205
|
|
206
|
-
if (ConstantVector::IsNull(args.data[1])) {
|
206
|
+
if (args.data[1].GetVectorType() == VectorType::CONSTANT_VECTOR && ConstantVector::IsNull(args.data[1])) {
|
207
207
|
result.SetVectorType(VectorType::CONSTANT_VECTOR);
|
208
208
|
ConstantVector::SetNull(result, true);
|
209
209
|
return;
|
@@ -237,6 +237,16 @@ static void ExecuteFlatSlice(Vector &result, Vector &list_vector, Vector &begin_
|
|
237
237
|
auto end_idx = end_data.sel->get_index(i);
|
238
238
|
auto step_idx = step_vector ? step_data.sel->get_index(i) : 0;
|
239
239
|
|
240
|
+
auto list_valid = list_data.validity.RowIsValid(list_idx);
|
241
|
+
auto begin_valid = begin_data.validity.RowIsValid(begin_idx);
|
242
|
+
auto end_valid = end_data.validity.RowIsValid(end_idx);
|
243
|
+
auto step_valid = step_vector && step_data.validity.RowIsValid(step_idx);
|
244
|
+
|
245
|
+
if (!list_valid || !begin_valid || !end_valid || (step_vector && !step_valid)) {
|
246
|
+
result_mask.SetInvalid(i);
|
247
|
+
continue;
|
248
|
+
}
|
249
|
+
|
240
250
|
auto sliced = reinterpret_cast<INPUT_TYPE *>(list_data.data)[list_idx];
|
241
251
|
auto begin = begin_is_empty ? 0 : reinterpret_cast<INDEX_TYPE *>(begin_data.data)[begin_idx];
|
242
252
|
auto end = end_is_empty ? ValueLength<INPUT_TYPE, INDEX_TYPE>(sliced)
|
@@ -248,23 +258,19 @@ static void ExecuteFlatSlice(Vector &result, Vector &list_vector, Vector &begin_
|
|
248
258
|
begin = end_is_empty ? 0 : begin;
|
249
259
|
end = begin_is_empty ? ValueLength<INPUT_TYPE, INDEX_TYPE>(sliced) : end;
|
250
260
|
}
|
251
|
-
auto list_valid = list_data.validity.RowIsValid(list_idx);
|
252
|
-
auto begin_valid = begin_data.validity.RowIsValid(begin_idx);
|
253
|
-
auto end_valid = end_data.validity.RowIsValid(end_idx);
|
254
|
-
auto step_valid = step_vector && step_data.validity.RowIsValid(step_idx);
|
255
261
|
|
256
262
|
bool clamp_result = false;
|
257
|
-
if (
|
263
|
+
if (step_valid || step == 1) {
|
258
264
|
clamp_result = ClampSlice(sliced, begin, end);
|
259
265
|
}
|
260
266
|
|
261
267
|
auto length = 0;
|
262
|
-
if (
|
268
|
+
if (end - begin > 0) {
|
263
269
|
length = CalculateSliceLength(begin, end, step, step_valid);
|
264
270
|
}
|
265
271
|
sel_length += length;
|
266
272
|
|
267
|
-
if (!
|
273
|
+
if (!clamp_result) {
|
268
274
|
result_mask.SetInvalid(i);
|
269
275
|
} else if (!step_vector) {
|
270
276
|
result_data[i] = SliceValue<INPUT_TYPE, INDEX_TYPE>(result, sliced, begin, end);
|
@@ -39,15 +39,21 @@ static unique_ptr<FunctionData> StructPackBind(ClientContext &context, ScalarFun
|
|
39
39
|
throw Exception("Can't pack nothing into a struct");
|
40
40
|
}
|
41
41
|
child_list_t<LogicalType> struct_children;
|
42
|
+
bool unnamed = false;
|
42
43
|
for (idx_t i = 0; i < arguments.size(); i++) {
|
43
44
|
auto &child = arguments[i];
|
44
|
-
if (child->alias.empty()
|
45
|
-
|
45
|
+
if (child->alias.empty()) {
|
46
|
+
if (bound_function.name == "struct_pack") {
|
47
|
+
throw BinderException("Need named argument for struct pack, e.g. STRUCT_PACK(a := b)");
|
48
|
+
} else {
|
49
|
+
D_ASSERT(bound_function.name == "row");
|
50
|
+
if (i > 1) {
|
51
|
+
D_ASSERT(unnamed);
|
52
|
+
}
|
53
|
+
unnamed = true;
|
54
|
+
}
|
46
55
|
}
|
47
|
-
if (child->alias.empty() &&
|
48
|
-
child->alias = "v" + std::to_string(i + 1);
|
49
|
-
}
|
50
|
-
if (name_collision_set.find(child->alias) != name_collision_set.end()) {
|
56
|
+
if (!child->alias.empty() && name_collision_set.find(child->alias) != name_collision_set.end()) {
|
51
57
|
throw BinderException("Duplicate struct entry name \"%s\"", child->alias);
|
52
58
|
}
|
53
59
|
name_collision_set.insert(child->alias);
|
@@ -335,8 +335,6 @@ normal : {
|
|
335
335
|
if (c == options.dialect_options.state_machine_options.delimiter) {
|
336
336
|
// delimiter: end the value and add it to the chunk
|
337
337
|
goto add_value;
|
338
|
-
} else if (c == options.dialect_options.state_machine_options.quote && try_add_line) {
|
339
|
-
return false;
|
340
338
|
} else if (StringUtil::CharacterIsNewline(c)) {
|
341
339
|
// newline: add row
|
342
340
|
if (column > 0 || try_add_line || parse_chunk.data.size() == 1) {
|
@@ -141,6 +141,10 @@ struct SniffValue {
|
|
141
141
|
(current_char == '\r' || current_char == '\n')) ||
|
142
142
|
(machine.dialect_options.new_line == NewLineIdentifier::CARRY_ON && current_char == '\n')) {
|
143
143
|
machine.rows_read++;
|
144
|
+
}
|
145
|
+
|
146
|
+
if ((machine.previous_state == CSVState::RECORD_SEPARATOR && machine.state != CSVState::EMPTY_LINE) ||
|
147
|
+
(machine.state != CSVState::RECORD_SEPARATOR && machine.previous_state == CSVState::CARRIAGE_RETURN)) {
|
144
148
|
sniffed_values[machine.cur_rows].position = machine.line_start_pos;
|
145
149
|
sniffed_values[machine.cur_rows].set = true;
|
146
150
|
machine.line_start_pos = current_pos;
|
@@ -287,11 +291,15 @@ void CSVSniffer::DetectTypes() {
|
|
287
291
|
candidate->csv_buffer_iterator.Process<SniffValue>(*candidate, tuples);
|
288
292
|
// Potentially Skip empty rows (I find this dirty, but it is what the original code does)
|
289
293
|
idx_t true_start = 0;
|
294
|
+
idx_t true_pos = 0;
|
290
295
|
idx_t values_start = 0;
|
291
296
|
while (true_start < tuples.size()) {
|
292
297
|
if (tuples[true_start].values.empty() ||
|
293
298
|
(tuples[true_start].values.size() == 1 && tuples[true_start].values[0].IsNull())) {
|
294
299
|
true_start = tuples[true_start].line_number;
|
300
|
+
if (true_start < tuples.size()) {
|
301
|
+
true_pos = tuples[true_start].position;
|
302
|
+
}
|
295
303
|
values_start++;
|
296
304
|
} else {
|
297
305
|
break;
|
@@ -301,7 +309,11 @@ void CSVSniffer::DetectTypes() {
|
|
301
309
|
// Potentially Skip Notes (I also find this dirty, but it is what the original code does)
|
302
310
|
while (true_start < tuples.size()) {
|
303
311
|
if (tuples[true_start].values.size() < max_columns_found && !options.null_padding) {
|
312
|
+
|
304
313
|
true_start = tuples[true_start].line_number;
|
314
|
+
if (true_start < tuples.size()) {
|
315
|
+
true_pos = tuples[true_start].position;
|
316
|
+
}
|
305
317
|
values_start++;
|
306
318
|
} else {
|
307
319
|
break;
|
@@ -317,7 +329,7 @@ void CSVSniffer::DetectTypes() {
|
|
317
329
|
row_idx = 1;
|
318
330
|
}
|
319
331
|
if (!tuples.empty()) {
|
320
|
-
best_start_without_header = tuples[0].position;
|
332
|
+
best_start_without_header = tuples[0].position - true_pos;
|
321
333
|
}
|
322
334
|
|
323
335
|
// First line where we start our type detection
|
@@ -387,7 +399,7 @@ void CSVSniffer::DetectTypes() {
|
|
387
399
|
best_sql_types_candidates_per_column_idx = info_sql_types_candidates;
|
388
400
|
best_format_candidates = format_candidates;
|
389
401
|
best_header_row = tuples[0].values;
|
390
|
-
best_start_with_header = tuples[0].position;
|
402
|
+
best_start_with_header = tuples[0].position - true_pos;
|
391
403
|
}
|
392
404
|
}
|
393
405
|
// Assert that it's all good at this point.
|
@@ -59,7 +59,7 @@ static void WriteCopyStatement(FileSystem &fs, stringstream &ss, CopyInfo &info,
|
|
59
59
|
if (info.format == "csv") {
|
60
60
|
// insert default csv options, if not specified
|
61
61
|
if (info.options.find("header") == info.options.end()) {
|
62
|
-
info.options["header"].push_back(Value::INTEGER(
|
62
|
+
info.options["header"].push_back(Value::INTEGER(1));
|
63
63
|
}
|
64
64
|
if (info.options.find("delimiter") == info.options.end() && info.options.find("sep") == info.options.end() &&
|
65
65
|
info.options.find("delim") == info.options.end()) {
|
@@ -184,19 +184,21 @@ static bool PlanIndexJoin(ClientContext &context, LogicalComparisonJoin &op, uni
|
|
184
184
|
if (!index) {
|
185
185
|
return false;
|
186
186
|
}
|
187
|
-
|
188
|
-
D_ASSERT(right->type == PhysicalOperatorType::TABLE_SCAN);
|
189
|
-
auto &tbl_scan = right->Cast<PhysicalTableScan>();
|
190
|
-
// if (tbl_scan.table_filters && !tbl_scan.table_filters->filters.empty()) {
|
191
|
-
// return false;
|
192
|
-
// }
|
187
|
+
|
193
188
|
// index joins are disabled if enable_optimizer is false
|
194
189
|
if (!ClientConfig::GetConfig(context).enable_optimizer) {
|
195
190
|
return false;
|
196
191
|
}
|
192
|
+
|
193
|
+
// index joins are disabled on default
|
194
|
+
auto force_index_join = ClientConfig::GetConfig(context).force_index_join;
|
195
|
+
if (!ClientConfig::GetConfig(context).enable_index_join && !force_index_join) {
|
196
|
+
return false;
|
197
|
+
}
|
198
|
+
|
197
199
|
// check if the cardinality difference justifies an index join
|
198
|
-
|
199
|
-
|
200
|
+
auto index_join_is_applicable = left->estimated_cardinality < 0.01 * right->estimated_cardinality;
|
201
|
+
if (!index_join_is_applicable && !force_index_join) {
|
200
202
|
return false;
|
201
203
|
}
|
202
204
|
|
@@ -205,6 +207,9 @@ static bool PlanIndexJoin(ClientContext &context, LogicalComparisonJoin &op, uni
|
|
205
207
|
swap(op.conditions[0].left, op.conditions[0].right);
|
206
208
|
swap(op.left_projection_map, op.right_projection_map);
|
207
209
|
}
|
210
|
+
D_ASSERT(right->type == PhysicalOperatorType::TABLE_SCAN);
|
211
|
+
auto &tbl_scan = right->Cast<PhysicalTableScan>();
|
212
|
+
|
208
213
|
plan = make_uniq<PhysicalIndexJoin>(op, std::move(left), std::move(right), std::move(op.conditions), op.join_type,
|
209
214
|
op.left_projection_map, op.right_projection_map, tbl_scan.column_ids, *index,
|
210
215
|
!swap_condition, op.estimated_cardinality);
|
@@ -9,10 +9,18 @@ unique_ptr<BoundCastData> StructBoundCastData::BindStructToStructCast(BindCastIn
|
|
9
9
|
vector<BoundCastInfo> child_cast_info;
|
10
10
|
auto &source_child_types = StructType::GetChildTypes(source);
|
11
11
|
auto &result_child_types = StructType::GetChildTypes(target);
|
12
|
+
|
13
|
+
auto target_is_unnamed = StructType::IsUnnamed(target);
|
14
|
+
auto source_is_unnamed = StructType::IsUnnamed(source);
|
15
|
+
|
12
16
|
if (source_child_types.size() != result_child_types.size()) {
|
13
17
|
throw TypeMismatchException(source, target, "Cannot cast STRUCTs of different size");
|
14
18
|
}
|
15
19
|
for (idx_t i = 0; i < source_child_types.size(); i++) {
|
20
|
+
if (!target_is_unnamed && !source_is_unnamed &&
|
21
|
+
!StringUtil::CIEquals(source_child_types[i].first, result_child_types[i].first)) {
|
22
|
+
throw TypeMismatchException(source, target, "Cannot cast STRUCTs with different names");
|
23
|
+
}
|
16
24
|
auto child_cast = input.GetCastFunction(source_child_types[i].second, result_child_types[i].second);
|
17
25
|
child_cast_info.push_back(std::move(child_cast));
|
18
26
|
}
|
@@ -304,6 +304,11 @@ static bool UnionToVarcharCast(Vector &source, Vector &result, idx_t count, Cast
|
|
304
304
|
// now construct the actual varchar vector
|
305
305
|
varchar_union.Flatten(count);
|
306
306
|
auto &tag_vector = UnionVector::GetTags(source);
|
307
|
+
auto tag_vector_type = tag_vector.GetVectorType();
|
308
|
+
if (tag_vector_type != VectorType::CONSTANT_VECTOR && tag_vector_type != VectorType::FLAT_VECTOR) {
|
309
|
+
tag_vector.Flatten(count);
|
310
|
+
}
|
311
|
+
|
307
312
|
auto tags = FlatVector::GetData<union_tag_t>(tag_vector);
|
308
313
|
|
309
314
|
auto &validity = FlatVector::Validity(varchar_union);
|
@@ -268,8 +268,17 @@ unique_ptr<Expression> FunctionBinder::BindScalarFunction(ScalarFunctionCatalogE
|
|
268
268
|
|
269
269
|
if (bound_function.null_handling == FunctionNullHandling::DEFAULT_NULL_HANDLING) {
|
270
270
|
for (auto &child : children) {
|
271
|
-
if (child->return_type == LogicalTypeId::SQLNULL
|
272
|
-
|
271
|
+
if (child->return_type == LogicalTypeId::SQLNULL) {
|
272
|
+
return make_uniq<BoundConstantExpression>(Value(LogicalType::SQLNULL));
|
273
|
+
}
|
274
|
+
if (!child->IsFoldable()) {
|
275
|
+
continue;
|
276
|
+
}
|
277
|
+
Value result;
|
278
|
+
if (!ExpressionExecutor::TryEvaluateScalar(context, *child, result)) {
|
279
|
+
continue;
|
280
|
+
}
|
281
|
+
if (result.IsNull()) {
|
273
282
|
return make_uniq<BoundConstantExpression>(Value(LogicalType::SQLNULL));
|
274
283
|
}
|
275
284
|
}
|
@@ -80,6 +80,10 @@ static void PragmaEnableForceParallelism(ClientContext &context, const FunctionP
|
|
80
80
|
ClientConfig::GetConfig(context).verify_parallelism = true;
|
81
81
|
}
|
82
82
|
|
83
|
+
static void PragmaEnableIndexJoin(ClientContext &context, const FunctionParameters ¶meters) {
|
84
|
+
ClientConfig::GetConfig(context).enable_index_join = true;
|
85
|
+
}
|
86
|
+
|
83
87
|
static void PragmaEnableForceIndexJoin(ClientContext &context, const FunctionParameters ¶meters) {
|
84
88
|
ClientConfig::GetConfig(context).force_index_join = true;
|
85
89
|
}
|
@@ -140,6 +144,7 @@ void PragmaFunctions::RegisterFunction(BuiltinFunctions &set) {
|
|
140
144
|
set.AddFunction(PragmaFunction::PragmaStatement("enable_optimizer", PragmaEnableOptimizer));
|
141
145
|
set.AddFunction(PragmaFunction::PragmaStatement("disable_optimizer", PragmaDisableOptimizer));
|
142
146
|
|
147
|
+
set.AddFunction(PragmaFunction::PragmaStatement("enable_index_join", PragmaEnableIndexJoin));
|
143
148
|
set.AddFunction(PragmaFunction::PragmaStatement("force_index_join", PragmaEnableForceIndexJoin));
|
144
149
|
set.AddFunction(PragmaFunction::PragmaStatement("force_checkpoint", PragmaForceCheckpoint));
|
145
150
|
|
@@ -26,6 +26,8 @@ idx_t StrfTimepecifierSize(StrTimeSpecifier specifier) {
|
|
26
26
|
case StrTimeSpecifier::WEEK_NUMBER_PADDED_SUN_FIRST:
|
27
27
|
case StrTimeSpecifier::WEEK_NUMBER_PADDED_MON_FIRST:
|
28
28
|
return 2;
|
29
|
+
case StrTimeSpecifier::NANOSECOND_PADDED:
|
30
|
+
return 9;
|
29
31
|
case StrTimeSpecifier::MICROSECOND_PADDED:
|
30
32
|
return 6;
|
31
33
|
case StrTimeSpecifier::MILLISECOND_PADDED:
|
@@ -183,9 +185,15 @@ char *StrfTimeFormat::WritePadded3(char *target, uint32_t value) {
|
|
183
185
|
}
|
184
186
|
}
|
185
187
|
|
186
|
-
// write a value in the range of 0..999999 padded to
|
188
|
+
// write a value in the range of 0..999999... padded to the given number of digits
|
187
189
|
char *StrfTimeFormat::WritePadded(char *target, uint32_t value, size_t padding) {
|
188
|
-
D_ASSERT(padding
|
190
|
+
D_ASSERT(padding > 1);
|
191
|
+
if (padding % 2) {
|
192
|
+
int decimals = value % 1000;
|
193
|
+
WritePadded3(target + padding - 3, decimals);
|
194
|
+
value /= 1000;
|
195
|
+
padding -= 3;
|
196
|
+
}
|
189
197
|
for (size_t i = 0; i < padding / 2; i++) {
|
190
198
|
int decimals = value % 100;
|
191
199
|
WritePadded2(target + padding - 2 * (i + 1), decimals);
|
@@ -309,11 +317,14 @@ char *StrfTimeFormat::WriteStandardSpecifier(StrTimeSpecifier specifier, int32_t
|
|
309
317
|
case StrTimeSpecifier::SECOND_PADDED:
|
310
318
|
target = WritePadded2(target, data[5]);
|
311
319
|
break;
|
320
|
+
case StrTimeSpecifier::NANOSECOND_PADDED:
|
321
|
+
target = WritePadded(target, data[6] * Interval::NANOS_PER_MICRO, 9);
|
322
|
+
break;
|
312
323
|
case StrTimeSpecifier::MICROSECOND_PADDED:
|
313
324
|
target = WritePadded(target, data[6], 6);
|
314
325
|
break;
|
315
326
|
case StrTimeSpecifier::MILLISECOND_PADDED:
|
316
|
-
target = WritePadded3(target, data[6] /
|
327
|
+
target = WritePadded3(target, data[6] / Interval::MICROS_PER_MSEC);
|
317
328
|
break;
|
318
329
|
case StrTimeSpecifier::UTC_OFFSET: {
|
319
330
|
*target++ = (data[7] < 0) ? '-' : '+';
|
@@ -516,6 +527,9 @@ string StrTimeFormat::ParseFormatSpecifier(const string &format_string, StrTimeF
|
|
516
527
|
case 'S':
|
517
528
|
specifier = StrTimeSpecifier::SECOND_PADDED;
|
518
529
|
break;
|
530
|
+
case 'n':
|
531
|
+
specifier = StrTimeSpecifier::NANOSECOND_PADDED;
|
532
|
+
break;
|
519
533
|
case 'f':
|
520
534
|
specifier = StrTimeSpecifier::MICROSECOND_PADDED;
|
521
535
|
break;
|
@@ -660,6 +674,8 @@ int StrpTimeFormat::NumericSpecifierWidth(StrTimeSpecifier specifier) {
|
|
660
674
|
return 4;
|
661
675
|
case StrTimeSpecifier::MICROSECOND_PADDED:
|
662
676
|
return 6;
|
677
|
+
case StrTimeSpecifier::NANOSECOND_PADDED:
|
678
|
+
return 9;
|
663
679
|
default:
|
664
680
|
return -1;
|
665
681
|
}
|
@@ -855,15 +871,20 @@ bool StrpTimeFormat::Parse(string_t str, ParseResult &result) const {
|
|
855
871
|
// seconds
|
856
872
|
result_data[5] = number;
|
857
873
|
break;
|
874
|
+
case StrTimeSpecifier::NANOSECOND_PADDED:
|
875
|
+
D_ASSERT(number < Interval::NANOS_PER_SEC); // enforced by the length of the number
|
876
|
+
// microseconds (rounded)
|
877
|
+
result_data[6] = (number + Interval::NANOS_PER_MICRO / 2) / Interval::NANOS_PER_MICRO;
|
878
|
+
break;
|
858
879
|
case StrTimeSpecifier::MICROSECOND_PADDED:
|
859
|
-
D_ASSERT(number <
|
860
|
-
//
|
880
|
+
D_ASSERT(number < Interval::MICROS_PER_SEC); // enforced by the length of the number
|
881
|
+
// microseconds
|
861
882
|
result_data[6] = number;
|
862
883
|
break;
|
863
884
|
case StrTimeSpecifier::MILLISECOND_PADDED:
|
864
|
-
D_ASSERT(number <
|
865
|
-
//
|
866
|
-
result_data[6] = number *
|
885
|
+
D_ASSERT(number < Interval::MSECS_PER_SEC); // enforced by the length of the number
|
886
|
+
// microseconds
|
887
|
+
result_data[6] = number * Interval::MICROS_PER_MSEC;
|
867
888
|
break;
|
868
889
|
case StrTimeSpecifier::WEEK_NUMBER_PADDED_SUN_FIRST:
|
869
890
|
case StrTimeSpecifier::WEEK_NUMBER_PADDED_MON_FIRST:
|
@@ -237,6 +237,10 @@ void ArrowTableFunction::PopulateArrowTableType(ArrowTableType &arrow_table, Arr
|
|
237
237
|
|
238
238
|
unique_ptr<FunctionData> ArrowTableFunction::ArrowScanBind(ClientContext &context, TableFunctionBindInput &input,
|
239
239
|
vector<LogicalType> &return_types, vector<string> &names) {
|
240
|
+
if (input.inputs[0].IsNull() || input.inputs[1].IsNull() || input.inputs[2].IsNull()) {
|
241
|
+
throw BinderException("arrow_scan: pointers cannot be null");
|
242
|
+
}
|
243
|
+
|
240
244
|
auto stream_factory_ptr = input.inputs[0].GetPointer();
|
241
245
|
auto stream_factory_produce = (stream_factory_produce_t)input.inputs[1].GetPointer(); // NOLINT
|
242
246
|
auto stream_factory_get_schema = (stream_factory_get_schema_t)input.inputs[2].GetPointer(); // NOLINT
|
@@ -157,6 +157,7 @@ static unique_ptr<FunctionData> ReadCSVBind(ClientContext &context, CopyInfo &in
|
|
157
157
|
}
|
158
158
|
|
159
159
|
bind_data->FinalizeRead(context);
|
160
|
+
|
160
161
|
if (options.auto_detect) {
|
161
162
|
// We must run the sniffer.
|
162
163
|
auto file_handle = BaseCSVReader::OpenCSV(context, options);
|
@@ -332,7 +333,7 @@ static unique_ptr<GlobalFunctionData> WriteCSVInitializeGlobal(ClientContext &co
|
|
332
333
|
global_data->WriteData(options.prefix.c_str(), options.prefix.size());
|
333
334
|
}
|
334
335
|
|
335
|
-
if (options.dialect_options.header) {
|
336
|
+
if (!(options.has_header && !options.dialect_options.header)) {
|
336
337
|
MemoryStream stream;
|
337
338
|
// write the header line to the file
|
338
339
|
for (idx_t i = 0; i < csv_data.options.name_list.size(); i++) {
|
@@ -178,7 +178,10 @@ public:
|
|
178
178
|
current_file_path = files_path_p[0];
|
179
179
|
CSVFileHandle *file_handle_ptr;
|
180
180
|
|
181
|
-
if (!buffer_manager) {
|
181
|
+
if (!buffer_manager || (options.skip_rows_set && options.dialect_options.skip_rows > 0)) {
|
182
|
+
// If our buffers are too small, and we skip too many rows there is a chance things will go over-buffer
|
183
|
+
// for now don't reuse the buffer manager
|
184
|
+
buffer_manager.reset();
|
182
185
|
file_handle = ReadCSV::OpenCSV(current_file_path, options.compression, context);
|
183
186
|
file_handle_ptr = file_handle.get();
|
184
187
|
} else {
|
@@ -1,8 +1,8 @@
|
|
1
1
|
#ifndef DUCKDB_VERSION
|
2
|
-
#define DUCKDB_VERSION "0.8.2-
|
2
|
+
#define DUCKDB_VERSION "0.8.2-dev5080"
|
3
3
|
#endif
|
4
4
|
#ifndef DUCKDB_SOURCE_ID
|
5
|
-
#define DUCKDB_SOURCE_ID "
|
5
|
+
#define DUCKDB_SOURCE_ID "e07b94a748"
|
6
6
|
#endif
|
7
7
|
#include "duckdb/function/table/system_functions.hpp"
|
8
8
|
#include "duckdb/main/database.hpp"
|
@@ -408,6 +408,7 @@ struct StructType {
|
|
408
408
|
DUCKDB_API static const LogicalType &GetChildType(const LogicalType &type, idx_t index);
|
409
409
|
DUCKDB_API static const string &GetChildName(const LogicalType &type, idx_t index);
|
410
410
|
DUCKDB_API static idx_t GetChildCount(const LogicalType &type);
|
411
|
+
DUCKDB_API static bool IsUnnamed(const LogicalType &type);
|
411
412
|
};
|
412
413
|
|
413
414
|
struct MapType {
|
@@ -52,7 +52,8 @@ enum class StrTimeSpecifier : uint8_t {
|
|
52
52
|
LOCALE_APPROPRIATE_DATE_AND_TIME =
|
53
53
|
29, // %c - Locale’s appropriate date and time representation. (Mon Sep 30 07:06:05 2013)
|
54
54
|
LOCALE_APPROPRIATE_DATE = 30, // %x - Locale’s appropriate date representation. (09/30/13)
|
55
|
-
LOCALE_APPROPRIATE_TIME = 31
|
55
|
+
LOCALE_APPROPRIATE_TIME = 31, // %X - Locale’s appropriate time representation. (07:06:05)
|
56
|
+
NANOSECOND_PADDED = 32 // %n - Nanosecond as a decimal number, zero-padded on the left. (000000000 - 999999999)
|
56
57
|
};
|
57
58
|
|
58
59
|
struct StrTimeFormat {
|
@@ -67,6 +67,8 @@ struct ClientConfig {
|
|
67
67
|
bool enable_caching_operators = true;
|
68
68
|
//! Force parallelism of small tables, used for testing
|
69
69
|
bool verify_parallelism = false;
|
70
|
+
//! Enable the optimizer to consider index joins, which are disabled on default
|
71
|
+
bool enable_index_join = false;
|
70
72
|
//! Force index join independent of table cardinality, used for testing
|
71
73
|
bool force_index_join = false;
|
72
74
|
//! Force out-of-core computation for operators that support it, used for testing
|
@@ -10,12 +10,39 @@
|
|
10
10
|
|
11
11
|
#include "duckdb/common/types.hpp"
|
12
12
|
#include "duckdb/common/unordered_map.hpp"
|
13
|
+
#include "duckdb/planner/expression/bound_parameter_data.hpp"
|
13
14
|
#include "duckdb/common/case_insensitive_map.hpp"
|
14
15
|
|
15
16
|
namespace duckdb {
|
16
17
|
|
17
|
-
|
18
|
+
class ParameterExpression;
|
19
|
+
class BoundParameterExpression;
|
18
20
|
|
19
21
|
using bound_parameter_map_t = case_insensitive_map_t<shared_ptr<BoundParameterData>>;
|
20
22
|
|
23
|
+
struct BoundParameterMap {
|
24
|
+
public:
|
25
|
+
explicit BoundParameterMap(case_insensitive_map_t<BoundParameterData> ¶meter_data);
|
26
|
+
|
27
|
+
public:
|
28
|
+
LogicalType GetReturnType(const string &identifier);
|
29
|
+
|
30
|
+
bound_parameter_map_t *GetParametersPtr();
|
31
|
+
|
32
|
+
const bound_parameter_map_t &GetParameters();
|
33
|
+
|
34
|
+
const case_insensitive_map_t<BoundParameterData> &GetParameterData();
|
35
|
+
|
36
|
+
unique_ptr<BoundParameterExpression> BindParameterExpression(ParameterExpression &expr);
|
37
|
+
|
38
|
+
private:
|
39
|
+
shared_ptr<BoundParameterData> CreateOrGetData(const string &identifier);
|
40
|
+
void CreateNewParameter(const string &id, const shared_ptr<BoundParameterData> ¶m_data);
|
41
|
+
|
42
|
+
private:
|
43
|
+
bound_parameter_map_t parameters;
|
44
|
+
// Pre-provided parameter data if populated
|
45
|
+
case_insensitive_map_t<BoundParameterData> ¶meter_data;
|
46
|
+
};
|
47
|
+
|
21
48
|
} // namespace duckdb
|
@@ -9,7 +9,6 @@
|
|
9
9
|
#pragma once
|
10
10
|
|
11
11
|
#include "duckdb/common/types/value.hpp"
|
12
|
-
#include "duckdb/planner/bound_parameter_map.hpp"
|
13
12
|
#include "duckdb/common/case_insensitive_map.hpp"
|
14
13
|
|
15
14
|
namespace duckdb {
|
@@ -40,21 +39,4 @@ public:
|
|
40
39
|
static shared_ptr<BoundParameterData> Deserialize(Deserializer &deserializer);
|
41
40
|
};
|
42
41
|
|
43
|
-
struct BoundParameterMap {
|
44
|
-
explicit BoundParameterMap(case_insensitive_map_t<BoundParameterData> ¶meter_data)
|
45
|
-
: parameter_data(parameter_data) {
|
46
|
-
}
|
47
|
-
|
48
|
-
bound_parameter_map_t parameters;
|
49
|
-
case_insensitive_map_t<BoundParameterData> ¶meter_data;
|
50
|
-
|
51
|
-
LogicalType GetReturnType(const string &identifier) {
|
52
|
-
auto it = parameter_data.find(identifier);
|
53
|
-
if (it == parameter_data.end()) {
|
54
|
-
return LogicalTypeId::UNKNOWN;
|
55
|
-
}
|
56
|
-
return it->second.return_type;
|
57
|
-
}
|
58
|
-
};
|
59
|
-
|
60
42
|
} // namespace duckdb
|
@@ -11,7 +11,7 @@
|
|
11
11
|
#include "duckdb/parser/sql_statement.hpp"
|
12
12
|
#include "duckdb/planner/binder.hpp"
|
13
13
|
#include "duckdb/planner/logical_operator.hpp"
|
14
|
-
#include "duckdb/planner/
|
14
|
+
#include "duckdb/planner/bound_parameter_map.hpp"
|
15
15
|
|
16
16
|
namespace duckdb {
|
17
17
|
class ClientContext;
|
@@ -124,10 +124,16 @@ duckdb_type duckdb_param_type(duckdb_prepared_statement prepared_statement, idx_
|
|
124
124
|
}
|
125
125
|
LogicalType param_type;
|
126
126
|
auto identifier = std::to_string(param_idx);
|
127
|
-
if (
|
128
|
-
return
|
127
|
+
if (wrapper->statement->data->TryGetType(identifier, param_type)) {
|
128
|
+
return ConvertCPPTypeToC(param_type);
|
129
|
+
}
|
130
|
+
// The value_map is gone after executing the prepared statement
|
131
|
+
// See if this is the case and we still have a value registered for it
|
132
|
+
auto it = wrapper->values.find(identifier);
|
133
|
+
if (it != wrapper->values.end()) {
|
134
|
+
return ConvertCPPTypeToC(it->second.type());
|
129
135
|
}
|
130
|
-
return
|
136
|
+
return DUCKDB_TYPE_INVALID;
|
131
137
|
}
|
132
138
|
|
133
139
|
duckdb_state duckdb_clear_bindings(duckdb_prepared_statement prepared_statement) {
|
@@ -119,6 +119,7 @@ BindResult ExpressionBinder::BindExpression(ComparisonExpression &expr, idx_t de
|
|
119
119
|
if (!error.empty()) {
|
120
120
|
return BindResult(error);
|
121
121
|
}
|
122
|
+
|
122
123
|
// the children have been successfully resolved
|
123
124
|
auto &left = BoundExpression::GetExpression(*expr.left);
|
124
125
|
auto &right = BoundExpression::GetExpression(*expr.right);
|
@@ -7,35 +7,25 @@
|
|
7
7
|
namespace duckdb {
|
8
8
|
|
9
9
|
BindResult ExpressionBinder::BindExpression(ParameterExpression &expr, idx_t depth) {
|
10
|
-
auto bound_parameter = make_uniq<BoundParameterExpression>(expr.identifier);
|
11
|
-
bound_parameter->alias = expr.alias;
|
12
10
|
if (!binder.parameters) {
|
13
11
|
throw BinderException("Unexpected prepared parameter. This type of statement can't be prepared!");
|
14
12
|
}
|
15
13
|
auto parameter_id = expr.identifier;
|
16
|
-
|
17
|
-
|
14
|
+
|
15
|
+
D_ASSERT(binder.parameters);
|
16
|
+
// Check if a parameter value has already been supplied
|
17
|
+
auto ¶meter_data = binder.parameters->GetParameterData();
|
18
|
+
auto param_data_it = parameter_data.find(parameter_id);
|
19
|
+
if (param_data_it != parameter_data.end()) {
|
18
20
|
// it has! emit a constant directly
|
19
|
-
auto &data =
|
21
|
+
auto &data = param_data_it->second;
|
20
22
|
auto constant = make_uniq<BoundConstantExpression>(data.GetValue());
|
21
23
|
constant->alias = expr.alias;
|
24
|
+
constant->return_type = binder.parameters->GetReturnType(parameter_id);
|
22
25
|
return BindResult(std::move(constant));
|
23
26
|
}
|
24
27
|
|
25
|
-
auto
|
26
|
-
if (entry == binder.parameters->parameters.end()) {
|
27
|
-
// no entry yet: create a new one
|
28
|
-
auto data = make_shared<BoundParameterData>();
|
29
|
-
data->return_type = binder.parameters->GetReturnType(parameter_id);
|
30
|
-
bound_parameter->return_type = data->return_type;
|
31
|
-
bound_parameter->parameter_data = data;
|
32
|
-
binder.parameters->parameters[parameter_id] = std::move(data);
|
33
|
-
} else {
|
34
|
-
// a prepared statement with this parameter index was already there: use it
|
35
|
-
auto &data = entry->second;
|
36
|
-
bound_parameter->parameter_data = data;
|
37
|
-
bound_parameter->return_type = binder.parameters->GetReturnType(parameter_id);
|
38
|
-
}
|
28
|
+
auto bound_parameter = binder.parameters->BindParameterExpression(expr);
|
39
29
|
return BindResult(std::move(bound_parameter));
|
40
30
|
}
|
41
31
|
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#include "duckdb/planner/bound_parameter_map.hpp"
|
2
|
+
#include "duckdb/parser/expression/parameter_expression.hpp"
|
3
|
+
#include "duckdb/planner/expression/bound_parameter_expression.hpp"
|
4
|
+
|
5
|
+
namespace duckdb {
|
6
|
+
|
7
|
+
BoundParameterMap::BoundParameterMap(case_insensitive_map_t<BoundParameterData> ¶meter_data)
|
8
|
+
: parameter_data(parameter_data) {
|
9
|
+
}
|
10
|
+
|
11
|
+
LogicalType BoundParameterMap::GetReturnType(const string &identifier) {
|
12
|
+
D_ASSERT(!identifier.empty());
|
13
|
+
auto it = parameter_data.find(identifier);
|
14
|
+
if (it == parameter_data.end()) {
|
15
|
+
return LogicalTypeId::UNKNOWN;
|
16
|
+
}
|
17
|
+
return it->second.return_type;
|
18
|
+
}
|
19
|
+
|
20
|
+
bound_parameter_map_t *BoundParameterMap::GetParametersPtr() {
|
21
|
+
return ¶meters;
|
22
|
+
}
|
23
|
+
|
24
|
+
const bound_parameter_map_t &BoundParameterMap::GetParameters() {
|
25
|
+
return parameters;
|
26
|
+
}
|
27
|
+
|
28
|
+
const case_insensitive_map_t<BoundParameterData> &BoundParameterMap::GetParameterData() {
|
29
|
+
return parameter_data;
|
30
|
+
}
|
31
|
+
|
32
|
+
shared_ptr<BoundParameterData> BoundParameterMap::CreateOrGetData(const string &identifier) {
|
33
|
+
auto entry = parameters.find(identifier);
|
34
|
+
if (entry == parameters.end()) {
|
35
|
+
// no entry yet: create a new one
|
36
|
+
auto data = make_shared<BoundParameterData>();
|
37
|
+
data->return_type = GetReturnType(identifier);
|
38
|
+
|
39
|
+
CreateNewParameter(identifier, data);
|
40
|
+
return data;
|
41
|
+
}
|
42
|
+
return entry->second;
|
43
|
+
}
|
44
|
+
|
45
|
+
unique_ptr<BoundParameterExpression> BoundParameterMap::BindParameterExpression(ParameterExpression &expr) {
|
46
|
+
auto &identifier = expr.identifier;
|
47
|
+
auto return_type = GetReturnType(identifier);
|
48
|
+
|
49
|
+
D_ASSERT(!parameter_data.count(identifier));
|
50
|
+
|
51
|
+
// No value has been supplied yet,
|
52
|
+
// We return a shared pointer to an object that will get populated wtih a Value later
|
53
|
+
// When the BoundParameterExpression get executed, this will be used to get the corresponding value
|
54
|
+
auto param_data = CreateOrGetData(identifier);
|
55
|
+
auto bound_expr = make_uniq<BoundParameterExpression>(identifier);
|
56
|
+
bound_expr->parameter_data = param_data;
|
57
|
+
bound_expr->return_type = return_type;
|
58
|
+
bound_expr->alias = expr.alias;
|
59
|
+
return bound_expr;
|
60
|
+
}
|
61
|
+
|
62
|
+
void BoundParameterMap::CreateNewParameter(const string &id, const shared_ptr<BoundParameterData> ¶m_data) {
|
63
|
+
D_ASSERT(!parameters.count(id));
|
64
|
+
parameters.emplace(std::make_pair(id, param_data));
|
65
|
+
}
|
66
|
+
|
67
|
+
} // namespace duckdb
|
@@ -78,10 +78,10 @@ void Planner::CreatePlan(SQLStatement &statement) {
|
|
78
78
|
this->properties.parameter_count = parameter_count;
|
79
79
|
properties.bound_all_parameters = parameters_resolved;
|
80
80
|
|
81
|
-
Planner::VerifyPlan(context, plan,
|
81
|
+
Planner::VerifyPlan(context, plan, bound_parameters.GetParametersPtr());
|
82
82
|
|
83
83
|
// set up a map of parameter number -> value entries
|
84
|
-
for (auto &kv : bound_parameters.
|
84
|
+
for (auto &kv : bound_parameters.GetParameters()) {
|
85
85
|
auto &identifier = kv.first;
|
86
86
|
auto ¶m = kv.second;
|
87
87
|
// check if the type of the parameter could be resolved
|
@@ -15,6 +15,9 @@ StructColumnData::StructColumnData(BlockManager &block_manager, DataTableInfo &i
|
|
15
15
|
D_ASSERT(type.InternalType() == PhysicalType::STRUCT);
|
16
16
|
auto &child_types = StructType::GetChildTypes(type);
|
17
17
|
D_ASSERT(child_types.size() > 0);
|
18
|
+
if (type.id() != LogicalTypeId::UNION && StructType::IsUnnamed(type)) {
|
19
|
+
throw InvalidInputException("A table cannot be created from an unnamed struct");
|
20
|
+
}
|
18
21
|
// the sub column index, starting at 1 (0 is the validity mask)
|
19
22
|
idx_t sub_column_index = 1;
|
20
23
|
for (auto &child_type : child_types) {
|