From d11bd584c1e5eea2eee66676a8a228524932c4f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Tue, 27 Feb 2024 13:39:03 +0100 Subject: [PATCH 1/5] Update code generation for Makevars.win --- rconfigure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rconfigure.py b/rconfigure.py index c1c0f0b6c..b5a01f2f3 100644 --- a/rconfigure.py +++ b/rconfigure.py @@ -130,7 +130,7 @@ def open_utf8(fpath, flags): include_list += " -DDUCKDB_PLATFORM_RTOOLS=1" text = text.replace('{{ INCLUDES }}', include_list) -text = text.replace('{{ LINK_FLAGS }}', "-lws2_32") +text = text.replace('{{ LINK_FLAGS }}', "-lws2_32 -lrstrtmgr") # now write it to the output Makevars with open_utf8(os.path.join('src', 'Makevars.win'), 'w+') as f: From c46a6bf8487fc4f3eabd1ad03e6f52316cf92f7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Tue, 27 Feb 2024 11:02:36 +0100 Subject: [PATCH 2/5] chore: Update vendored sources to duckdb/duckdb@a20a91c5a4159ac85717783acb50e31050fb8e64 Merge pull request duckdb/duckdb#10650 from hannes/noprintf Merge pull request duckdb/duckdb#10658 from hannes/csvpathlength Merge pull request duckdb/duckdb#10245 from Tishj/parallel_streaming_query_result --- src/duckdb/src/common/enum_util.cpp | 5 + src/duckdb/src/common/types/vector.cpp | 15 ++ .../core_functions/scalar/map/map_entries.cpp | 4 +- .../scalar/map/map_from_entries.cpp | 3 +- .../scalar/string/parse_path.cpp | 2 +- .../aggregate/physical_hash_aggregate.cpp | 84 +++++--- .../physical_ungrouped_aggregate.cpp | 50 +++-- .../helper/physical_buffered_collector.cpp | 85 ++++++++ .../helper/physical_result_collector.cpp | 10 + .../operator/join/physical_hash_join.cpp | 21 +- .../execution/radix_partitioned_hashtable.cpp | 62 ++++-- .../src/function/scalar/nested_functions.cpp | 31 +++ .../function/table/version/pragma_version.cpp | 4 +- src/duckdb/src/include/duckdb.h | 12 ++ .../common/enums/pending_execution_result.hpp | 8 +- .../src/include/duckdb/common/types.hpp | 13 ++ .../include/duckdb/common/types/vector.hpp | 5 + .../src/include/duckdb/execution/executor.hpp | 7 +- .../helper/physical_batch_collector.hpp | 4 - .../helper/physical_buffered_collector.hpp | 37 ++++ .../duckdb/execution/task_error_manager.hpp | 11 ++ .../function/scalar/nested_functions.hpp | 4 + .../main/buffered_data/buffered_data.hpp | 89 +++++++++ .../buffered_data/simple_buffered_data.hpp | 53 +++++ .../include/duckdb/main/client_context.hpp | 14 +- .../duckdb/main/pending_query_result.hpp | 3 + .../duckdb/main/prepared_statement_data.hpp | 2 + .../duckdb/main/stream_query_result.hpp | 10 +- .../src/include/duckdb/parallel/pipeline.hpp | 20 ++ .../duckdb/parallel/pipeline_executor.hpp | 8 +- .../src/include/duckdb/parallel/task.hpp | 7 + .../duckdb/parallel/task_scheduler.hpp | 7 +- .../buffered_data/simple_buffered_data.cpp | 96 +++++++++ src/duckdb/src/main/capi/pending-c.cpp | 41 +++- src/duckdb/src/main/capi/result-c.cpp | 2 +- src/duckdb/src/main/client_context.cpp | 186 ++++++++---------- src/duckdb/src/main/database.cpp | 1 + src/duckdb/src/main/pending_query_result.cpp | 31 ++- src/duckdb/src/main/query_result.cpp | 2 + src/duckdb/src/main/stream_query_result.cpp | 72 +++++-- src/duckdb/src/parallel/executor.cpp | 71 ++++--- src/duckdb/src/parallel/pipeline.cpp | 82 ++++---- src/duckdb/src/parallel/pipeline_executor.cpp | 71 +------ src/duckdb/src/parallel/task_scheduler.cpp | 15 +- .../ub_src_execution_operator_helper.cpp | 2 + src/duckdb/ub_src_main_buffered_data.cpp | 2 + src/include/sources.mk | 2 +- 47 files changed, 999 insertions(+), 367 deletions(-) create mode 100644 src/duckdb/src/execution/operator/helper/physical_buffered_collector.cpp create mode 100644 src/duckdb/src/include/duckdb/execution/operator/helper/physical_buffered_collector.hpp create mode 100644 src/duckdb/src/include/duckdb/main/buffered_data/buffered_data.hpp create mode 100644 src/duckdb/src/include/duckdb/main/buffered_data/simple_buffered_data.hpp create mode 100644 src/duckdb/src/main/buffered_data/simple_buffered_data.cpp create mode 100644 src/duckdb/ub_src_main_buffered_data.cpp diff --git a/src/duckdb/src/common/enum_util.cpp b/src/duckdb/src/common/enum_util.cpp index 2c5c2568f..27b4364d4 100644 --- a/src/duckdb/src/common/enum_util.cpp +++ b/src/duckdb/src/common/enum_util.cpp @@ -4138,6 +4138,8 @@ const char* EnumUtil::ToChars(PendingExecutionResult val return "RESULT_NOT_READY"; case PendingExecutionResult::EXECUTION_ERROR: return "EXECUTION_ERROR"; + case PendingExecutionResult::BLOCKED: + return "BLOCKED"; case PendingExecutionResult::NO_TASKS_AVAILABLE: return "NO_TASKS_AVAILABLE"; default: @@ -4156,6 +4158,9 @@ PendingExecutionResult EnumUtil::FromString(const char * if (StringUtil::Equals(value, "EXECUTION_ERROR")) { return PendingExecutionResult::EXECUTION_ERROR; } + if (StringUtil::Equals(value, "BLOCKED")) { + return PendingExecutionResult::BLOCKED; + } if (StringUtil::Equals(value, "NO_TASKS_AVAILABLE")) { return PendingExecutionResult::NO_TASKS_AVAILABLE; } diff --git a/src/duckdb/src/common/types/vector.cpp b/src/duckdb/src/common/types/vector.cpp index f9db436e0..2884ea911 100644 --- a/src/duckdb/src/common/types/vector.cpp +++ b/src/duckdb/src/common/types/vector.cpp @@ -118,6 +118,21 @@ void Vector::ReferenceAndSetType(const Vector &other) { void Vector::Reinterpret(const Vector &other) { vector_type = other.vector_type; +#ifdef DEBUG + auto &this_type = GetType(); + auto &other_type = other.GetType(); + + auto type_is_same = other_type == this_type; + bool this_is_nested = this_type.IsNested(); + bool other_is_nested = other_type.IsNested(); + + bool not_nested = this_is_nested == false && other_is_nested == false; + bool type_size_equal = GetTypeIdSize(this_type.InternalType()) == GetTypeIdSize(other_type.InternalType()); + //! Either the types are completely identical, or they are not nested and their physical type size is the same + //! The reason nested types are not allowed is because copying the auxiliary buffer does not happen recursively + //! e.g DOUBLE[] to BIGINT[], the type of the LIST would say BIGINT but the child Vector says DOUBLE + D_ASSERT((not_nested && type_size_equal) || type_is_same); +#endif AssignSharedPointer(buffer, other.buffer); AssignSharedPointer(auxiliary, other.auxiliary); data = other.data; diff --git a/src/duckdb/src/core_functions/scalar/map/map_entries.cpp b/src/duckdb/src/core_functions/scalar/map/map_entries.cpp index caaeccee2..7629c7896 100644 --- a/src/duckdb/src/core_functions/scalar/map/map_entries.cpp +++ b/src/duckdb/src/core_functions/scalar/map/map_entries.cpp @@ -10,9 +10,9 @@ namespace duckdb { // Reverse of map_from_entries static void MapEntriesFunction(DataChunk &args, ExpressionState &state, Vector &result) { - idx_t count = args.size(); + auto count = args.size(); - result.Reinterpret(args.data[0]); + MapUtil::ReinterpretMap(result, args.data[0], count); if (args.AllConstant()) { result.SetVectorType(VectorType::CONSTANT_VECTOR); diff --git a/src/duckdb/src/core_functions/scalar/map/map_from_entries.cpp b/src/duckdb/src/core_functions/scalar/map/map_from_entries.cpp index be79503ed..fbaf1663d 100644 --- a/src/duckdb/src/core_functions/scalar/map/map_from_entries.cpp +++ b/src/duckdb/src/core_functions/scalar/map/map_from_entries.cpp @@ -10,8 +10,7 @@ namespace duckdb { static void MapFromEntriesFunction(DataChunk &args, ExpressionState &state, Vector &result) { auto count = args.size(); - result.Reinterpret(args.data[0]); - + MapUtil::ReinterpretMap(result, args.data[0], count); MapVector::MapConversionVerify(result, count); result.Verify(count); diff --git a/src/duckdb/src/core_functions/scalar/string/parse_path.cpp b/src/duckdb/src/core_functions/scalar/string/parse_path.cpp index e6ce3066a..cc304d512 100644 --- a/src/duckdb/src/core_functions/scalar/string/parse_path.cpp +++ b/src/duckdb/src/core_functions/scalar/string/parse_path.cpp @@ -175,7 +175,7 @@ static void TrimPathFunction(DataChunk &args, ExpressionState &state, Vector &re // set default values Vector &path = args.data[0]; Vector separator(string_t("default")); - Vector trim_extension(false); + Vector trim_extension(Value::BOOLEAN(false)); ReadOptionalArgs(args, separator, trim_extension, FRONT_TRIM); TernaryExecutor::Execute( diff --git a/src/duckdb/src/execution/operator/aggregate/physical_hash_aggregate.cpp b/src/duckdb/src/execution/operator/aggregate/physical_hash_aggregate.cpp index 3b62fc69f..e6bd35875 100644 --- a/src/duckdb/src/execution/operator/aggregate/physical_hash_aggregate.cpp +++ b/src/duckdb/src/execution/operator/aggregate/physical_hash_aggregate.cpp @@ -14,6 +14,7 @@ #include "duckdb/planner/expression/bound_aggregate_expression.hpp" #include "duckdb/planner/expression/bound_constant_expression.hpp" #include "duckdb/planner/expression/bound_reference_expression.hpp" +#include "duckdb/common/optional_idx.hpp" namespace duckdb { @@ -545,7 +546,7 @@ class HashAggregateDistinctFinalizeTask : public ExecutorTask { TaskExecutionResult ExecuteTask(TaskExecutionMode mode) override; private: - void AggregateDistinctGrouping(const idx_t grouping_idx); + TaskExecutionResult AggregateDistinctGrouping(const idx_t grouping_idx); private: Pipeline &pipeline; @@ -553,6 +554,14 @@ class HashAggregateDistinctFinalizeTask : public ExecutorTask { const PhysicalHashAggregate &op; HashAggregateGlobalSinkState &gstate; + + unique_ptr local_sink_state; + idx_t grouping_idx = 0; + unique_ptr radix_table_lstate; + bool blocked = false; + idx_t aggregation_idx = 0; + idx_t payload_idx = 0; + idx_t next_payload_idx = 0; }; void HashAggregateDistinctFinalizeEvent::Schedule() { @@ -604,14 +613,22 @@ void HashAggregateDistinctFinalizeEvent::FinishEvent() { } TaskExecutionResult HashAggregateDistinctFinalizeTask::ExecuteTask(TaskExecutionMode mode) { - for (idx_t grouping_idx = 0; grouping_idx < op.groupings.size(); grouping_idx++) { - AggregateDistinctGrouping(grouping_idx); + for (; grouping_idx < op.groupings.size(); grouping_idx++) { + auto res = AggregateDistinctGrouping(grouping_idx); + if (res == TaskExecutionResult::TASK_BLOCKED) { + return res; + } + D_ASSERT(res == TaskExecutionResult::TASK_FINISHED); + aggregation_idx = 0; + payload_idx = 0; + next_payload_idx = 0; + local_sink_state = nullptr; } event->FinishTask(); return TaskExecutionResult::TASK_FINISHED; } -void HashAggregateDistinctFinalizeTask::AggregateDistinctGrouping(const idx_t grouping_idx) { +TaskExecutionResult HashAggregateDistinctFinalizeTask::AggregateDistinctGrouping(const idx_t grouping_idx) { D_ASSERT(op.distinct_collection_info); auto &info = *op.distinct_collection_info; @@ -628,9 +645,11 @@ void HashAggregateDistinctFinalizeTask::AggregateDistinctGrouping(const idx_t gr ExecutionContext execution_context(executor.context, thread_context, &pipeline); // Sink state to sink into global HTs - InterruptState interrupt_state; + InterruptState interrupt_state(shared_from_this()); auto &global_sink_state = *grouping_state.table_state; - auto local_sink_state = grouping_data.table_data.GetLocalSinkState(execution_context); + if (!local_sink_state) { + local_sink_state = grouping_data.table_data.GetLocalSinkState(execution_context); + } OperatorSinkInput sink_input {global_sink_state, *local_sink_state, interrupt_state}; // Create a chunk that mimics the 'input' chunk in Sink, for storing the group vectors @@ -639,24 +658,24 @@ void HashAggregateDistinctFinalizeTask::AggregateDistinctGrouping(const idx_t gr group_chunk.Initialize(executor.context, op.input_group_types); } - auto &groups = op.grouped_aggregate_data.groups; - const idx_t group_by_size = groups.size(); + const idx_t group_by_size = op.grouped_aggregate_data.groups.size(); DataChunk aggregate_input_chunk; if (!gstate.payload_types.empty()) { aggregate_input_chunk.Initialize(executor.context, gstate.payload_types); } - auto &finalize_event = event->Cast(); + const auto &finalize_event = event->Cast(); - idx_t payload_idx; - idx_t next_payload_idx = 0; - for (idx_t agg_idx = 0; agg_idx < op.grouped_aggregate_data.aggregates.size(); agg_idx++) { + auto &agg_idx = aggregation_idx; + for (; agg_idx < op.grouped_aggregate_data.aggregates.size(); agg_idx++) { auto &aggregate = aggregates[agg_idx]->Cast(); - // Forward the payload idx - payload_idx = next_payload_idx; - next_payload_idx = payload_idx + aggregate.children.size(); + if (!blocked) { + // Forward the payload idx + payload_idx = next_payload_idx; + next_payload_idx = payload_idx + aggregate.children.size(); + } // If aggregate is not distinct, skip it if (!distinct_data.IsDistinct(agg_idx)) { @@ -668,8 +687,11 @@ void HashAggregateDistinctFinalizeTask::AggregateDistinctGrouping(const idx_t gr auto &radix_table = distinct_data.radix_tables[table_idx]; auto &sink = *distinct_state.radix_states[table_idx]; - auto local_source = radix_table->GetLocalSourceState(execution_context); - OperatorSourceInput source_input {*finalize_event.global_source_states[grouping_idx][agg_idx], *local_source, + if (!blocked) { + radix_table_lstate = radix_table->GetLocalSourceState(execution_context); + } + auto &local_source = *radix_table_lstate; + OperatorSourceInput source_input {*finalize_event.global_source_states[grouping_idx][agg_idx], local_source, interrupt_state}; // Create a duplicate of the output_chunk, because of multi-threading we cant alter the original @@ -687,8 +709,8 @@ void HashAggregateDistinctFinalizeTask::AggregateDistinctGrouping(const idx_t gr D_ASSERT(output_chunk.size() == 0); break; } else if (res == SourceResultType::BLOCKED) { - throw InternalException( - "Unexpected interrupt from radix table GetData in HashAggregateDistinctFinalizeTask"); + blocked = true; + return TaskExecutionResult::TASK_BLOCKED; } auto &grouped_aggregate_data = *distinct_data.grouped_aggregate_data[table_idx]; @@ -708,8 +730,10 @@ void HashAggregateDistinctFinalizeTask::AggregateDistinctGrouping(const idx_t gr // Sink it into the main ht grouping_data.table_data.Sink(execution_context, group_chunk, sink_input, aggregate_input_chunk, {agg_idx}); } + blocked = false; } grouping_data.table_data.Combine(execution_context, global_sink_state, *local_sink_state); + return TaskExecutionResult::TASK_FINISHED; } SinkFinalizeType PhysicalHashAggregate::FinalizeDistinct(Pipeline &pipeline, Event &event, ClientContext &context, @@ -809,6 +833,7 @@ class HashAggregateLocalSourceState : public LocalSourceState { } } + optional_idx radix_idx; vector> radix_states; }; @@ -823,32 +848,37 @@ SourceResultType PhysicalHashAggregate::GetData(ExecutionContext &context, DataC auto &gstate = input.global_state.Cast(); auto &lstate = input.local_state.Cast(); while (true) { - idx_t radix_idx = gstate.state_index; + if (!lstate.radix_idx.IsValid()) { + lstate.radix_idx = gstate.state_index.load(); + } + const auto radix_idx = lstate.radix_idx.GetIndex(); if (radix_idx >= groupings.size()) { break; } + auto &grouping = groupings[radix_idx]; auto &radix_table = grouping.table_data; auto &grouping_gstate = sink_gstate.grouping_states[radix_idx]; - InterruptState interrupt_state; OperatorSourceInput source_input {*gstate.radix_states[radix_idx], *lstate.radix_states[radix_idx], - interrupt_state}; + input.interrupt_state}; auto res = radix_table.GetData(context, chunk, *grouping_gstate.table_state, source_input); + if (res == SourceResultType::BLOCKED) { + return res; + } if (chunk.size() != 0) { return SourceResultType::HAVE_MORE_OUTPUT; - } else if (res == SourceResultType::BLOCKED) { - throw InternalException("Unexpectedly Blocked from radix_table"); } // move to the next table lock_guard l(gstate.lock); - radix_idx++; - if (radix_idx > gstate.state_index) { + lstate.radix_idx = lstate.radix_idx.GetIndex() + 1; + if (lstate.radix_idx.GetIndex() > gstate.state_index) { // we have not yet worked on the table // move the global index forwards - gstate.state_index = radix_idx; + gstate.state_index = lstate.radix_idx.GetIndex(); } + lstate.radix_idx = gstate.state_index.load(); } return chunk.size() == 0 ? SourceResultType::FINISHED : SourceResultType::HAVE_MORE_OUTPUT; diff --git a/src/duckdb/src/execution/operator/aggregate/physical_ungrouped_aggregate.cpp b/src/duckdb/src/execution/operator/aggregate/physical_ungrouped_aggregate.cpp index c72709ba0..693d17a03 100644 --- a/src/duckdb/src/execution/operator/aggregate/physical_ungrouped_aggregate.cpp +++ b/src/duckdb/src/execution/operator/aggregate/physical_ungrouped_aggregate.cpp @@ -403,13 +403,13 @@ class UngroupedDistinctAggregateFinalizeTask : public ExecutorTask { const PhysicalUngroupedAggregate &op, UngroupedAggregateGlobalSinkState &state_p) : ExecutorTask(executor), event(std::move(event_p)), op(op), gstate(state_p), - allocator(gstate.CreateAllocator()) { + allocator(gstate.CreateAllocator()), aggregate_state(op.aggregates) { } TaskExecutionResult ExecuteTask(TaskExecutionMode mode) override; private: - void AggregateDistinct(); + TaskExecutionResult AggregateDistinct(); private: shared_ptr event; @@ -418,6 +418,12 @@ class UngroupedDistinctAggregateFinalizeTask : public ExecutorTask { UngroupedAggregateGlobalSinkState &gstate; ArenaAllocator &allocator; + + // Distinct aggregation state + AggregateState aggregate_state; + idx_t aggregation_idx = 0; + unique_ptr radix_table_lstate; + bool blocked = false; }; void UngroupedDistinctAggregateFinalizeEvent::Schedule() { @@ -460,19 +466,21 @@ void UngroupedDistinctAggregateFinalizeEvent::Schedule() { } TaskExecutionResult UngroupedDistinctAggregateFinalizeTask::ExecuteTask(TaskExecutionMode mode) { - AggregateDistinct(); + auto res = AggregateDistinct(); + if (res == TaskExecutionResult::TASK_BLOCKED) { + return res; + } event->FinishTask(); return TaskExecutionResult::TASK_FINISHED; } -void UngroupedDistinctAggregateFinalizeTask::AggregateDistinct() { +TaskExecutionResult UngroupedDistinctAggregateFinalizeTask::AggregateDistinct() { D_ASSERT(gstate.distinct_state); auto &distinct_state = *gstate.distinct_state; auto &distinct_data = *op.distinct_data; - // Create thread-local copy of aggregate state auto &aggregates = op.aggregates; - AggregateState state(aggregates); + auto &state = aggregate_state; // Thread-local contexts ThreadContext thread_context(executor.context); @@ -481,14 +489,12 @@ void UngroupedDistinctAggregateFinalizeTask::AggregateDistinct() { auto &finalize_event = event->Cast(); // Now loop through the distinct aggregates, scanning the distinct HTs - idx_t payload_idx = 0; - idx_t next_payload_idx = 0; - for (idx_t agg_idx = 0; agg_idx < aggregates.size(); agg_idx++) { - auto &aggregate = aggregates[agg_idx]->Cast(); - // Forward the payload idx - payload_idx = next_payload_idx; - next_payload_idx = payload_idx + aggregate.children.size(); + // This needs to be preserved in case the radix_table.GetData blocks + auto &agg_idx = aggregation_idx; + + for (; agg_idx < aggregates.size(); agg_idx++) { + auto &aggregate = aggregates[agg_idx]->Cast(); // If aggregate is not distinct, skip it if (!distinct_data.IsDistinct(agg_idx)) { @@ -497,11 +503,15 @@ void UngroupedDistinctAggregateFinalizeTask::AggregateDistinct() { const auto table_idx = distinct_data.info.table_map.at(agg_idx); auto &radix_table = *distinct_data.radix_tables[table_idx]; - auto lstate = radix_table.GetLocalSourceState(execution_context); + if (!blocked) { + // Because we can block, we need to make sure we preserve this state + radix_table_lstate = radix_table.GetLocalSourceState(execution_context); + } + auto &lstate = *radix_table_lstate; auto &sink = *distinct_state.radix_states[table_idx]; - InterruptState interrupt_state; - OperatorSourceInput source_input {*finalize_event.global_source_states[agg_idx], *lstate, interrupt_state}; + InterruptState interrupt_state(shared_from_this()); + OperatorSourceInput source_input {*finalize_event.global_source_states[agg_idx], lstate, interrupt_state}; DataChunk output_chunk; output_chunk.Initialize(executor.context, distinct_state.distinct_output_chunks[table_idx]->GetTypes()); @@ -519,8 +529,8 @@ void UngroupedDistinctAggregateFinalizeTask::AggregateDistinct() { D_ASSERT(output_chunk.size() == 0); break; } else if (res == SourceResultType::BLOCKED) { - throw InternalException( - "Unexpected interrupt from radix table GetData in UngroupedDistinctAggregateFinalizeTask"); + blocked = true; + return TaskExecutionResult::TASK_BLOCKED; } // We dont need to resolve the filter, we already did this in Sink @@ -539,12 +549,11 @@ void UngroupedDistinctAggregateFinalizeTask::AggregateDistinct() { aggregate.function.simple_update(start_of_input, aggr_input_data, payload_cnt, state.aggregates[agg_idx].get(), payload_chunk.size()); } + blocked = false; } // After scanning the distinct HTs, we can combine the thread-local agg states with the thread-global lock_guard guard(finalize_event.lock); - payload_idx = 0; - next_payload_idx = 0; for (idx_t agg_idx = 0; agg_idx < aggregates.size(); agg_idx++) { if (!distinct_data.IsDistinct(agg_idx)) { continue; @@ -563,6 +572,7 @@ void UngroupedDistinctAggregateFinalizeTask::AggregateDistinct() { if (++finalize_event.tasks_done == finalize_event.tasks_scheduled) { gstate.finished = true; } + return TaskExecutionResult::TASK_FINISHED; } SinkFinalizeType PhysicalUngroupedAggregate::FinalizeDistinct(Pipeline &pipeline, Event &event, ClientContext &context, diff --git a/src/duckdb/src/execution/operator/helper/physical_buffered_collector.cpp b/src/duckdb/src/execution/operator/helper/physical_buffered_collector.cpp new file mode 100644 index 000000000..fcf75496e --- /dev/null +++ b/src/duckdb/src/execution/operator/helper/physical_buffered_collector.cpp @@ -0,0 +1,85 @@ +#include "duckdb/execution/operator/helper/physical_buffered_collector.hpp" +#include "duckdb/main/stream_query_result.hpp" +#include "duckdb/main/client_context.hpp" + +namespace duckdb { + +PhysicalBufferedCollector::PhysicalBufferedCollector(PreparedStatementData &data, bool parallel) + : PhysicalResultCollector(data), parallel(parallel) { +} + +//===--------------------------------------------------------------------===// +// Sink +//===--------------------------------------------------------------------===// +class BufferedCollectorGlobalState : public GlobalSinkState { +public: + mutex glock; + //! This is weak to avoid creating a cyclical reference + weak_ptr context; + shared_ptr buffered_data; +}; + +class BufferedCollectorLocalState : public LocalSinkState { +public: + bool blocked = false; +}; + +SinkResultType PhysicalBufferedCollector::Sink(ExecutionContext &context, DataChunk &chunk, + OperatorSinkInput &input) const { + auto &gstate = input.global_state.Cast(); + auto &lstate = input.local_state.Cast(); + + lock_guard l(gstate.glock); + auto &buffered_data = gstate.buffered_data->Cast(); + + if (!lstate.blocked || buffered_data.BufferIsFull()) { + lstate.blocked = true; + auto callback_state = input.interrupt_state; + auto blocked_sink = BlockedSink(callback_state, chunk.size()); + buffered_data.BlockSink(blocked_sink); + return SinkResultType::BLOCKED; + } + + auto to_append = make_uniq(); + to_append->Initialize(Allocator::DefaultAllocator(), chunk.GetTypes()); + chunk.Copy(*to_append, 0); + buffered_data.Append(std::move(to_append)); + return SinkResultType::NEED_MORE_INPUT; +} + +SinkCombineResultType PhysicalBufferedCollector::Combine(ExecutionContext &context, + OperatorSinkCombineInput &input) const { + return SinkCombineResultType::FINISHED; +} + +unique_ptr PhysicalBufferedCollector::GetGlobalSinkState(ClientContext &context) const { + auto state = make_uniq(); + state->context = context.shared_from_this(); + state->buffered_data = make_shared(state->context); + return std::move(state); +} + +unique_ptr PhysicalBufferedCollector::GetLocalSinkState(ExecutionContext &context) const { + auto state = make_uniq(); + return std::move(state); +} + +unique_ptr PhysicalBufferedCollector::GetResult(GlobalSinkState &state) { + auto &gstate = state.Cast(); + lock_guard l(gstate.glock); + // FIXME: maybe we want to check if the execution was successfull before creating the StreamQueryResult ? + auto cc = gstate.context.lock(); + auto result = make_uniq(statement_type, properties, types, names, cc->GetClientProperties(), + gstate.buffered_data); + return std::move(result); +} + +bool PhysicalBufferedCollector::ParallelSink() const { + return parallel; +} + +bool PhysicalBufferedCollector::SinkOrderDependent() const { + return true; +} + +} // namespace duckdb diff --git a/src/duckdb/src/execution/operator/helper/physical_result_collector.cpp b/src/duckdb/src/execution/operator/helper/physical_result_collector.cpp index e0ac959a7..8b2bbdf8e 100644 --- a/src/duckdb/src/execution/operator/helper/physical_result_collector.cpp +++ b/src/duckdb/src/execution/operator/helper/physical_result_collector.cpp @@ -2,6 +2,7 @@ #include "duckdb/execution/operator/helper/physical_batch_collector.hpp" #include "duckdb/execution/operator/helper/physical_materialized_collector.hpp" +#include "duckdb/execution/operator/helper/physical_buffered_collector.hpp" #include "duckdb/execution/physical_plan_generator.hpp" #include "duckdb/main/config.hpp" #include "duckdb/main/prepared_statement_data.hpp" @@ -20,13 +21,22 @@ unique_ptr PhysicalResultCollector::GetResultCollector( PreparedStatementData &data) { if (!PhysicalPlanGenerator::PreserveInsertionOrder(context, *data.plan)) { // the plan is not order preserving, so we just use the parallel materialized collector + if (data.is_streaming) { + return make_uniq_base(data, true); + } return make_uniq_base(data, true); } else if (!PhysicalPlanGenerator::UseBatchIndex(context, *data.plan)) { // the plan is order preserving, but we cannot use the batch index: use a single-threaded result collector + if (data.is_streaming) { + return make_uniq_base(data, false); + } return make_uniq_base(data, false); } else { // we care about maintaining insertion order and the sources all support batch indexes // use a batch collector + if (data.is_streaming) { + return make_uniq_base(data, false); + } return make_uniq_base(data); } } diff --git a/src/duckdb/src/execution/operator/join/physical_hash_join.cpp b/src/duckdb/src/execution/operator/join/physical_hash_join.cpp index 5e72b2065..6cb658f97 100644 --- a/src/duckdb/src/execution/operator/join/physical_hash_join.cpp +++ b/src/duckdb/src/execution/operator/join/physical_hash_join.cpp @@ -13,6 +13,7 @@ #include "duckdb/planner/expression/bound_reference_expression.hpp" #include "duckdb/storage/buffer_manager.hpp" #include "duckdb/storage/storage_manager.hpp" +#include "duckdb/parallel/interrupt.hpp" #include "duckdb/storage/temporary_memory_manager.hpp" namespace duckdb { @@ -614,7 +615,7 @@ class HashJoinGlobalSourceState : public GlobalSourceState { //! Initialize this source state using the info in the sink void Initialize(HashJoinGlobalSinkState &sink); //! Try to prepare the next stage - void TryPrepareNextStage(HashJoinGlobalSinkState &sink); + bool TryPrepareNextStage(HashJoinGlobalSinkState &sink); //! Prepare the next build/probe/scan_ht stage for external hash join (must hold lock) void PrepareBuild(HashJoinGlobalSinkState &sink); void PrepareProbe(HashJoinGlobalSinkState &sink); @@ -663,6 +664,8 @@ class HashJoinGlobalSourceState : public GlobalSourceState { idx_t full_outer_chunk_count; idx_t full_outer_chunk_done; idx_t full_outer_chunks_per_thread; + + vector blocked_tasks; }; class HashJoinLocalSourceState : public LocalSourceState { @@ -739,13 +742,14 @@ void HashJoinGlobalSourceState::Initialize(HashJoinGlobalSinkState &sink) { TryPrepareNextStage(sink); } -void HashJoinGlobalSourceState::TryPrepareNextStage(HashJoinGlobalSinkState &sink) { +bool HashJoinGlobalSourceState::TryPrepareNextStage(HashJoinGlobalSinkState &sink) { switch (global_stage.load()) { case HashJoinSourceStage::BUILD: if (build_chunk_done == build_chunk_count) { sink.hash_table->GetDataCollection().VerifyEverythingPinned(); sink.hash_table->finalized = true; PrepareProbe(sink); + return true; } break; case HashJoinSourceStage::PROBE: @@ -755,16 +759,19 @@ void HashJoinGlobalSourceState::TryPrepareNextStage(HashJoinGlobalSinkState &sin } else { PrepareBuild(sink); } + return true; } break; case HashJoinSourceStage::SCAN_HT: if (full_outer_chunk_done == full_outer_chunk_count) { PrepareBuild(sink); + return true; } break; default: break; } + return false; } void HashJoinGlobalSourceState::PrepareBuild(HashJoinGlobalSinkState &sink) { @@ -1014,7 +1021,15 @@ SourceResultType PhysicalHashJoin::GetData(ExecutionContext &context, DataChunk lstate.ExecuteTask(sink, gstate, chunk); } else { lock_guard guard(gstate.lock); - gstate.TryPrepareNextStage(sink); + if (gstate.TryPrepareNextStage(sink) || gstate.global_stage == HashJoinSourceStage::DONE) { + for (auto &state : gstate.blocked_tasks) { + state.Callback(); + } + gstate.blocked_tasks.clear(); + } else { + gstate.blocked_tasks.push_back(input.interrupt_state); + return SourceResultType::BLOCKED; + } } } diff --git a/src/duckdb/src/execution/radix_partitioned_hashtable.cpp b/src/duckdb/src/execution/radix_partitioned_hashtable.cpp index 52bb30562..b3d0a7ea8 100644 --- a/src/duckdb/src/execution/radix_partitioned_hashtable.cpp +++ b/src/duckdb/src/execution/radix_partitioned_hashtable.cpp @@ -175,6 +175,8 @@ class RadixHTGlobalSinkState : public GlobalSinkState { idx_t count_before_combining; //! Maximum partition size if all unique idx_t max_partition_size; + + vector blocked_tasks; }; RadixHTGlobalSinkState::RadixHTGlobalSinkState(ClientContext &context_p, const RadixPartitionedHashTable &radix_ht_p) @@ -526,7 +528,6 @@ void RadixPartitionedHashTable::Finalize(ClientContext &context, GlobalSinkState auto max_threads = MinValue(TaskScheduler::GetScheduler(context).NumberOfThreads(), gstate.partitions.size()); gstate.temporary_memory_state->SetRemainingSize(context, max_threads * gstate.max_partition_size); - gstate.finalized = true; } @@ -572,8 +573,9 @@ class RadixHTGlobalSourceState : public GlobalSourceState { vector column_ids; //! For synchronizing scan tasks - atomic scan_idx; - atomic scan_done; + mutex lock; + idx_t scan_idx; + idx_t scan_done; }; enum class RadixHTScanStatus : uint8_t { INIT, IN_PROGRESS, DONE }; @@ -635,18 +637,26 @@ bool RadixHTGlobalSourceState::AssignTask(RadixHTGlobalSinkState &sink, RadixHTL if (finished) { return false; } - // We first try to assign a Scan task, then a Finalize task if that didn't work, without using any locks - // We need an atomic compare-and-swap to assign a Scan task, because we need to only increment - // the 'scan_idx' atomic if the 'finalize' of that partition is true, i.e., ready to be scanned - bool scan_assigned = true; - do { - lstate.task_idx = scan_idx.load(); - if (lstate.task_idx >= n_partitions || !sink.partitions[lstate.task_idx]->finalized) { - scan_assigned = false; - break; + // We first try to assign a Scan task, then a Finalize task if that didn't work + bool scan_assigned = false; + { + lock_guard gstate_guard(lock); + if (scan_idx < n_partitions && sink.partitions[scan_idx]->finalized) { + lstate.task_idx = scan_idx++; + scan_assigned = true; + if (scan_idx == n_partitions) { + // We will never be able to assign another task, unblock blocked tasks + lock_guard sink_guard(sink.lock); + if (!sink.blocked_tasks.empty()) { + for (auto &state : sink.blocked_tasks) { + state.Callback(); + } + sink.blocked_tasks.clear(); + } + } } - } while (!std::atomic_compare_exchange_weak(&scan_idx, &lstate.task_idx, lstate.task_idx + 1)); + } if (scan_assigned) { // We successfully assigned a Scan task @@ -669,7 +679,8 @@ bool RadixHTGlobalSourceState::AssignTask(RadixHTGlobalSinkState &sink, RadixHTL return true; } - // We didn't manage to assign a Finalize task + // We didn't manage to assign a Finalize task because there are none left + sink.temporary_memory_state->SetRemainingSize(context, 0); return false; } @@ -741,6 +752,17 @@ void RadixHTLocalSourceState::Finalize(RadixHTGlobalSinkState &sink, RadixHTGlob // Mark partition as ready to scan partition.finalized = true; + // Unblock blocked tasks so they can scan this partition + { + lock_guard sink_guard(sink.lock); + if (!sink.blocked_tasks.empty()) { + for (auto &state : sink.blocked_tasks) { + state.Callback(); + } + sink.blocked_tasks.clear(); + } + } + // Make sure this thread's aggregate allocator does not get lost lock_guard guard(sink.lock); sink.stored_allocators.emplace_back(ht->GetAggregateAllocator()); @@ -756,6 +778,7 @@ void RadixHTLocalSourceState::Scan(RadixHTGlobalSinkState &sink, RadixHTGlobalSo if (data_collection.Count() == 0) { scan_status = RadixHTScanStatus::DONE; + lock_guard gstate_guard(gstate.lock); if (++gstate.scan_done == sink.partitions.size()) { gstate.finished = true; } @@ -776,6 +799,7 @@ void RadixHTLocalSourceState::Scan(RadixHTGlobalSinkState &sink, RadixHTGlobalSo } if (data_collection.ScanComplete(scan_state)) { + lock_guard gstate_guard(gstate.lock); if (++gstate.scan_done == sink.partitions.size()) { gstate.finished = true; } @@ -834,7 +858,6 @@ SourceResultType RadixPartitionedHashTable::GetData(ExecutionContext &context, D sink.scan_pin_properties == TupleDataPinProperties::DESTROY_AFTER_DONE); if (gstate.finished) { - sink.temporary_memory_state->SetRemainingSize(context.client, 0); return SourceResultType::FINISHED; } @@ -875,6 +898,15 @@ SourceResultType RadixPartitionedHashTable::GetData(ExecutionContext &context, D while (!gstate.finished && chunk.size() == 0) { if (!lstate.TaskFinished() || gstate.AssignTask(sink, lstate)) { lstate.ExecuteTask(sink, gstate, chunk); + } else { + lock_guard gstate_guard(gstate.lock); + if (gstate.scan_idx < sink.partitions.size()) { + lock_guard sink_guard(sink.lock); + sink.blocked_tasks.push_back(input.interrupt_state); + return SourceResultType::BLOCKED; + } else { + return SourceResultType::FINISHED; + } } } diff --git a/src/duckdb/src/function/scalar/nested_functions.cpp b/src/duckdb/src/function/scalar/nested_functions.cpp index a0e0aa5a9..59c5f2b23 100644 --- a/src/duckdb/src/function/scalar/nested_functions.cpp +++ b/src/duckdb/src/function/scalar/nested_functions.cpp @@ -2,6 +2,37 @@ namespace duckdb { +void MapUtil::ReinterpretMap(Vector &result, Vector &input, idx_t count) { + UnifiedVectorFormat input_data; + input.ToUnifiedFormat(count, input_data); + // Copy the list validity + FlatVector::SetValidity(result, input_data.validity); + + // Copy the struct validity + UnifiedVectorFormat input_struct_data; + ListVector::GetEntry(input).ToUnifiedFormat(count, input_struct_data); + auto &result_struct = ListVector::GetEntry(result); + FlatVector::SetValidity(result_struct, input_struct_data.validity); + + // Set the right vector type + result.SetVectorType(input.GetVectorType()); + + // Copy the list size + auto list_size = ListVector::GetListSize(input); + ListVector::SetListSize(result, list_size); + + // Copy the list buffer (the list_entry_t data) + result.CopyBuffer(input); + + auto &input_keys = MapVector::GetKeys(input); + auto &result_keys = MapVector::GetKeys(result); + result_keys.Reference(input_keys); + + auto &input_values = MapVector::GetValues(input); + auto &result_values = MapVector::GetValues(result); + result_values.Reference(input_values); +} + void BuiltinFunctions::RegisterNestedFunctions() { Register(); Register(); diff --git a/src/duckdb/src/function/table/version/pragma_version.cpp b/src/duckdb/src/function/table/version/pragma_version.cpp index d970a8983..8d37c6b57 100644 --- a/src/duckdb/src/function/table/version/pragma_version.cpp +++ b/src/duckdb/src/function/table/version/pragma_version.cpp @@ -1,8 +1,8 @@ #ifndef DUCKDB_VERSION -#define DUCKDB_VERSION "v0.9.3-dev3731" +#define DUCKDB_VERSION "v0.9.3-dev3861" #endif #ifndef DUCKDB_SOURCE_ID -#define DUCKDB_SOURCE_ID "d4c774b1f1" +#define DUCKDB_SOURCE_ID "a20a91c5a4" #endif #include "duckdb/function/table/system_functions.hpp" #include "duckdb/main/database.hpp" diff --git a/src/duckdb/src/include/duckdb.h b/src/duckdb/src/include/duckdb.h index eb54f2ed1..d0b6160d5 100644 --- a/src/duckdb/src/include/duckdb.h +++ b/src/duckdb/src/include/duckdb.h @@ -1535,6 +1535,18 @@ The error message can be obtained by calling duckdb_pending_error on the pending */ DUCKDB_API duckdb_pending_state duckdb_pending_execute_task(duckdb_pending_result pending_result); +/*! +If this returns DUCKDB_PENDING_RESULT_READY, the duckdb_execute_pending function can be called to obtain the result. +If this returns DUCKDB_PENDING_RESULT_NOT_READY, the duckdb_pending_execute_check_state function should be called again. +If this returns DUCKDB_PENDING_ERROR, an error occurred during execution. + +The error message can be obtained by calling duckdb_pending_error on the pending_result. + +* pending_result: The pending result. +* returns: The state of the pending result. +*/ +DUCKDB_API duckdb_pending_state duckdb_pending_execute_check_state(duckdb_pending_result pending_result); + /*! Fully execute a pending query result, returning the final query result. diff --git a/src/duckdb/src/include/duckdb/common/enums/pending_execution_result.hpp b/src/duckdb/src/include/duckdb/common/enums/pending_execution_result.hpp index e130e9734..8c8daa3e6 100644 --- a/src/duckdb/src/include/duckdb/common/enums/pending_execution_result.hpp +++ b/src/duckdb/src/include/duckdb/common/enums/pending_execution_result.hpp @@ -12,6 +12,12 @@ namespace duckdb { -enum class PendingExecutionResult : uint8_t { RESULT_READY, RESULT_NOT_READY, EXECUTION_ERROR, NO_TASKS_AVAILABLE }; +enum class PendingExecutionResult : uint8_t { + RESULT_READY, + RESULT_NOT_READY, + EXECUTION_ERROR, + BLOCKED, + NO_TASKS_AVAILABLE +}; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/types.hpp b/src/duckdb/src/include/duckdb/common/types.hpp index bf6d23e08..8c12ff83c 100644 --- a/src/duckdb/src/include/duckdb/common/types.hpp +++ b/src/duckdb/src/include/duckdb/common/types.hpp @@ -254,6 +254,19 @@ struct LogicalType { inline const ExtraTypeInfo *AuxInfo() const { return type_info_.get(); } + inline bool IsNested() const { + auto internal = InternalType(); + if (internal == PhysicalType::STRUCT) { + return true; + } + if (internal == PhysicalType::LIST) { + return true; + } + if (internal == PhysicalType::ARRAY) { + return true; + } + return false; + } inline shared_ptr GetAuxInfoShrPtr() const { return type_info_; diff --git a/src/duckdb/src/include/duckdb/common/types/vector.hpp b/src/duckdb/src/include/duckdb/common/types/vector.hpp index bf5d339e0..dd0152583 100644 --- a/src/duckdb/src/include/duckdb/common/types/vector.hpp +++ b/src/duckdb/src/include/duckdb/common/types/vector.hpp @@ -172,6 +172,11 @@ class Vector { auxiliary = std::move(new_buffer); }; + inline void CopyBuffer(Vector &other) { + buffer = other.buffer; + data = other.data; + } + //! This functions resizes the vector DUCKDB_API void Resize(idx_t cur_size, idx_t new_size); diff --git a/src/duckdb/src/include/duckdb/execution/executor.hpp b/src/duckdb/src/include/duckdb/execution/executor.hpp index 767f44f95..3c248c68d 100644 --- a/src/duckdb/src/include/duckdb/execution/executor.hpp +++ b/src/duckdb/src/include/duckdb/execution/executor.hpp @@ -48,17 +48,17 @@ class Executor { void Initialize(unique_ptr physical_plan); void CancelTasks(); - PendingExecutionResult ExecuteTask(); + PendingExecutionResult ExecuteTask(bool dry_run = false); void Reset(); vector GetTypes(); - unique_ptr FetchChunk(); - //! Push a new error void PushError(ErrorData exception); + ErrorData GetError(); + //! True if an error has been thrown bool HasError(); //! Throw the exception that was pushed using PushError. @@ -101,6 +101,7 @@ class Executor { bool ExecutionIsFinished(); private: + bool ResultCollectorIsBlocked(); void InitializeInternal(PhysicalOperator &physical_plan); void ScheduleEvents(const vector> &meta_pipelines); diff --git a/src/duckdb/src/include/duckdb/execution/operator/helper/physical_batch_collector.hpp b/src/duckdb/src/include/duckdb/execution/operator/helper/physical_batch_collector.hpp index 04c152094..88ae8f1b6 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/helper/physical_batch_collector.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/helper/physical_batch_collector.hpp @@ -33,10 +33,6 @@ class PhysicalBatchCollector : public PhysicalResultCollector { return true; } - bool IsSink() const override { - return true; - } - bool ParallelSink() const override { return true; } diff --git a/src/duckdb/src/include/duckdb/execution/operator/helper/physical_buffered_collector.hpp b/src/duckdb/src/include/duckdb/execution/operator/helper/physical_buffered_collector.hpp new file mode 100644 index 000000000..08cbd1b80 --- /dev/null +++ b/src/duckdb/src/include/duckdb/execution/operator/helper/physical_buffered_collector.hpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/execution/operator/helper/physical_buffered_collector.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/execution/operator/helper/physical_result_collector.hpp" +#include "duckdb/main/buffered_data/simple_buffered_data.hpp" + +namespace duckdb { + +class PhysicalBufferedCollector : public PhysicalResultCollector { +public: + PhysicalBufferedCollector(PreparedStatementData &data, bool parallel); + + bool parallel; + +public: + unique_ptr GetResult(GlobalSinkState &state) override; + +public: + // Sink interface + SinkResultType Sink(ExecutionContext &context, DataChunk &chunk, OperatorSinkInput &input) const override; + SinkCombineResultType Combine(ExecutionContext &context, OperatorSinkCombineInput &input) const override; + + unique_ptr GetLocalSinkState(ExecutionContext &context) const override; + unique_ptr GetGlobalSinkState(ClientContext &context) const override; + + bool ParallelSink() const override; + bool SinkOrderDependent() const override; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/execution/task_error_manager.hpp b/src/duckdb/src/include/duckdb/execution/task_error_manager.hpp index cd47b8425..4d7e64493 100644 --- a/src/duckdb/src/include/duckdb/execution/task_error_manager.hpp +++ b/src/duckdb/src/include/duckdb/execution/task_error_manager.hpp @@ -21,6 +21,17 @@ class TaskErrorManager { this->exceptions.push_back(std::move(error)); } + ErrorData GetError() { + lock_guard elock(error_lock); + D_ASSERT(!exceptions.empty()); + + // FIXME: Should we try to get the biggest priority error? + // In case the first exception is a StandardException but a regular Exception or a FatalException occurred + // Maybe we should throw the more critical exception instead, as that changes behavior. + auto &entry = exceptions[0]; + return entry; + } + bool HasError() { lock_guard elock(error_lock); return !exceptions.empty(); diff --git a/src/duckdb/src/include/duckdb/function/scalar/nested_functions.hpp b/src/duckdb/src/include/duckdb/function/scalar/nested_functions.hpp index ed51471e1..86fa9a19b 100644 --- a/src/duckdb/src/include/duckdb/function/scalar/nested_functions.hpp +++ b/src/duckdb/src/include/duckdb/function/scalar/nested_functions.hpp @@ -49,6 +49,10 @@ struct PositionFunctor { } }; +struct MapUtil { + static void ReinterpretMap(Vector &target, Vector &other, idx_t count); +}; + struct VariableReturnBindData : public FunctionData { LogicalType stype; diff --git a/src/duckdb/src/include/duckdb/main/buffered_data/buffered_data.hpp b/src/duckdb/src/include/duckdb/main/buffered_data/buffered_data.hpp new file mode 100644 index 000000000..a98bfad56 --- /dev/null +++ b/src/duckdb/src/include/duckdb/main/buffered_data/buffered_data.hpp @@ -0,0 +1,89 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/main/buffered_data.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/parallel/interrupt.hpp" +#include "duckdb/common/queue.hpp" +#include "duckdb/common/vector_size.hpp" +#include "duckdb/common/types/data_chunk.hpp" +#include "duckdb/common/optional_idx.hpp" +#include "duckdb/execution/physical_operator_states.hpp" +#include "duckdb/common/enums/pending_execution_result.hpp" + +namespace duckdb { + +class StreamQueryResult; +class ClientContextLock; + +struct BlockedSink { +public: + BlockedSink(InterruptState state, idx_t chunk_size) : state(state), chunk_size(chunk_size) { + } + +public: + //! The handle to reschedule the blocked sink + InterruptState state; + //! The amount of tuples this sink would add + idx_t chunk_size; +}; + +class BufferedData { +protected: + enum class Type { SIMPLE }; + +public: + BufferedData(Type type, weak_ptr context) : type(type), context(context) { + } + virtual ~BufferedData() { + } + +public: + virtual bool BufferIsFull() = 0; + virtual PendingExecutionResult ReplenishBuffer(StreamQueryResult &result, ClientContextLock &context_lock) = 0; + virtual unique_ptr Scan() = 0; + shared_ptr GetContext() { + return context.lock(); + } + bool Closed() const { + if (context.expired()) { + return false; + } + auto c = context.lock(); + return c == nullptr; + } + void Close() { + context.reset(); + } + +public: + template + TARGET &Cast() { + if (TARGET::TYPE != type) { + throw InternalException("Failed to cast buffered data to type - buffered data type mismatch"); + } + return reinterpret_cast(*this); + } + + template + const TARGET &Cast() const { + if (TARGET::TYPE != type) { + throw InternalException("Failed to cast buffered data to type - buffered data type mismatch"); + } + return reinterpret_cast(*this); + } + +protected: + Type type; + //! This is weak to avoid a cyclical reference + weak_ptr context; + //! Protect against populate/fetch race condition + mutex glock; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/buffered_data/simple_buffered_data.hpp b/src/duckdb/src/include/duckdb/main/buffered_data/simple_buffered_data.hpp new file mode 100644 index 000000000..a33928c48 --- /dev/null +++ b/src/duckdb/src/include/duckdb/main/buffered_data/simple_buffered_data.hpp @@ -0,0 +1,53 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/main/simple_buffered_data.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/main/buffered_data/buffered_data.hpp" +#include "duckdb/parallel/interrupt.hpp" +#include "duckdb/common/queue.hpp" +#include "duckdb/common/vector_size.hpp" +#include "duckdb/common/types/data_chunk.hpp" + +namespace duckdb { + +class StreamQueryResult; +class ClientContextLock; + +class SimpleBufferedData : public BufferedData { +public: + static constexpr const BufferedData::Type TYPE = BufferedData::Type::SIMPLE; + +private: + //! (roughly) The max amount of tuples we'll keep buffered at a time + static constexpr idx_t BUFFER_SIZE = 100000; + +public: + SimpleBufferedData(weak_ptr context); + ~SimpleBufferedData() override; + +public: + void Append(unique_ptr chunk); + void BlockSink(const BlockedSink &blocked_sink); + bool BufferIsFull() override; + PendingExecutionResult ReplenishBuffer(StreamQueryResult &result, ClientContextLock &context_lock) override; + unique_ptr Scan() override; + +private: + void UnblockSinks(); + +private: + //! Our handles to reschedule the blocked sink tasks + queue blocked_sinks; + //! The queue of chunks + queue> buffered_chunks; + //! The current capacity of the buffer (tuples) + atomic buffered_count; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/client_context.hpp b/src/duckdb/src/include/duckdb/main/client_context.hpp index e8c54ab65..b62dc04fd 100644 --- a/src/duckdb/src/include/duckdb/main/client_context.hpp +++ b/src/duckdb/src/include/duckdb/main/client_context.hpp @@ -43,6 +43,7 @@ struct CreateScalarFunctionInfo; class ScalarFunctionCatalogEntry; struct ActiveQueryContext; struct ParserOptions; +class SimpleBufferedData; struct ClientData; struct PendingQueryParameters { @@ -63,8 +64,9 @@ class ClientContextState { //! The ClientContext holds information relevant to the current client session //! during execution class ClientContext : public std::enable_shared_from_this { - friend class PendingQueryResult; - friend class StreamQueryResult; + friend class PendingQueryResult; // LockContext + friend class SimpleBufferedData; // ExecuteTaskInternal + friend class StreamQueryResult; // LockContext friend class ConnectionManager; public: @@ -174,10 +176,9 @@ class ClientContext : public std::enable_shared_from_this { //! Returns the parser options for this client context DUCKDB_API ParserOptions GetParserOptions() const; - DUCKDB_API unique_ptr Fetch(ClientContextLock &lock, StreamQueryResult &result); - //! Whether or not the given result object (streaming query result or pending query result) is active - DUCKDB_API bool IsActiveResult(ClientContextLock &lock, BaseQueryResult *result); + DUCKDB_API bool IsActiveResult(ClientContextLock &lock, BaseQueryResult &result); + DUCKDB_API void SetActiveResult(ClientContextLock &lock, BaseQueryResult &result); //! Returns the current executor Executor &GetExecutor(); @@ -237,7 +238,6 @@ class ClientContext : public std::enable_shared_from_this { void LogQueryInternal(ClientContextLock &lock, const string &query); unique_ptr FetchResultInternal(ClientContextLock &lock, PendingQueryResult &pending); - unique_ptr FetchInternal(ClientContextLock &lock, Executor &executor, BaseQueryResult &result); unique_ptr LockContext(); @@ -245,7 +245,7 @@ class ClientContext : public std::enable_shared_from_this { void BeginQueryInternal(ClientContextLock &lock, const string &query); ErrorData EndQueryInternal(ClientContextLock &lock, bool success, bool invalidate_transaction); - PendingExecutionResult ExecuteTaskInternal(ClientContextLock &lock, PendingQueryResult &result); + PendingExecutionResult ExecuteTaskInternal(ClientContextLock &lock, BaseQueryResult &result, bool dry_run = false); unique_ptr PendingStatementOrPreparedStatementInternal( ClientContextLock &lock, const string &query, unique_ptr statement, diff --git a/src/duckdb/src/include/duckdb/main/pending_query_result.hpp b/src/duckdb/src/include/duckdb/main/pending_query_result.hpp index 930363e29..8b95a025e 100644 --- a/src/duckdb/src/include/duckdb/main/pending_query_result.hpp +++ b/src/duckdb/src/include/duckdb/main/pending_query_result.hpp @@ -28,6 +28,7 @@ class PendingQueryResult : public BaseQueryResult { vector types, bool allow_stream_result); DUCKDB_API explicit PendingQueryResult(ErrorData error_message); DUCKDB_API ~PendingQueryResult() override; + DUCKDB_API bool AllowStreamResult() const; public: //! Executes a single task within the query, returning whether or not the query is ready. @@ -38,6 +39,7 @@ class PendingQueryResult : public BaseQueryResult { //! but tasks may become available in the future. //! The error message can be obtained by calling GetError() on the PendingQueryResult. DUCKDB_API PendingExecutionResult ExecuteTask(); + DUCKDB_API PendingExecutionResult CheckPulse(); //! Returns the result of the query as an actual query result. //! This returns (mostly) instantly if ExecuteTask has been called until RESULT_READY was returned. @@ -47,6 +49,7 @@ class PendingQueryResult : public BaseQueryResult { //! Function to determine whether execution is considered finished DUCKDB_API static bool IsFinished(PendingExecutionResult result); + DUCKDB_API static bool IsFinishedOrBlocked(PendingExecutionResult result); private: shared_ptr context; diff --git a/src/duckdb/src/include/duckdb/main/prepared_statement_data.hpp b/src/duckdb/src/include/duckdb/main/prepared_statement_data.hpp index ce0b7d1a1..684f2d530 100644 --- a/src/duckdb/src/include/duckdb/main/prepared_statement_data.hpp +++ b/src/duckdb/src/include/duckdb/main/prepared_statement_data.hpp @@ -46,6 +46,8 @@ class PreparedStatementData { idx_t catalog_version; //! The map of parameter index to the actual value entry bound_parameter_map_t value_map; + //! Whether we are creating a streaming result or not + bool is_streaming = false; public: void CheckParameterCount(idx_t parameter_count); diff --git a/src/duckdb/src/include/duckdb/main/stream_query_result.hpp b/src/duckdb/src/include/duckdb/main/stream_query_result.hpp index 0373f307e..5b8016c80 100644 --- a/src/duckdb/src/include/duckdb/main/stream_query_result.hpp +++ b/src/duckdb/src/include/duckdb/main/stream_query_result.hpp @@ -10,6 +10,9 @@ #include "duckdb/common/winapi.hpp" #include "duckdb/main/query_result.hpp" +#include "duckdb/parallel/interrupt.hpp" +#include "duckdb/common/queue.hpp" +#include "duckdb/main/buffered_data/simple_buffered_data.hpp" namespace duckdb { @@ -29,7 +32,8 @@ class StreamQueryResult : public QueryResult { //! Create a successful StreamQueryResult. StreamQueryResults should always be successful initially (it makes no //! sense to stream an error). DUCKDB_API StreamQueryResult(StatementType statement_type, StatementProperties properties, - shared_ptr context, vector types, vector names); + vector types, vector names, ClientProperties client_properties, + shared_ptr buffered_data); DUCKDB_API ~StreamQueryResult() override; public: @@ -49,9 +53,13 @@ class StreamQueryResult : public QueryResult { shared_ptr context; private: + unique_ptr FetchInternal(ClientContextLock &lock); unique_ptr LockContext(); void CheckExecutableInternal(ClientContextLock &lock); bool IsOpenInternal(ClientContextLock &lock); + +private: + shared_ptr buffered_data; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/parallel/pipeline.hpp b/src/duckdb/src/include/duckdb/parallel/pipeline.hpp index 27f9fa65f..ca60992eb 100644 --- a/src/duckdb/src/include/duckdb/parallel/pipeline.hpp +++ b/src/duckdb/src/include/duckdb/parallel/pipeline.hpp @@ -21,6 +21,26 @@ namespace duckdb { class Executor; class Event; class MetaPipeline; +class PipelineExecutor; +class Pipeline; + +class PipelineTask : public ExecutorTask { + static constexpr const idx_t PARTIAL_CHUNK_COUNT = 50; + +public: + explicit PipelineTask(Pipeline &pipeline_p, shared_ptr event_p); + + Pipeline &pipeline; + shared_ptr event; + unique_ptr pipeline_executor; + +public: + const PipelineExecutor &GetPipelineExecutor() const; + bool TaskBlockedOnResult() const override; + +public: + TaskExecutionResult ExecuteTask(TaskExecutionMode mode) override; +}; class PipelineBuildState { public: diff --git a/src/duckdb/src/include/duckdb/parallel/pipeline_executor.hpp b/src/duckdb/src/include/duckdb/parallel/pipeline_executor.hpp index 631944492..e19c4483d 100644 --- a/src/duckdb/src/include/duckdb/parallel/pipeline_executor.hpp +++ b/src/duckdb/src/include/duckdb/parallel/pipeline_executor.hpp @@ -51,14 +51,12 @@ class PipelineExecutor { //! This should only be called once per PipelineExecutor. PipelineExecuteResult PushFinalize(); - //! Initializes a chunk with the types that will flow out of ExecutePull + bool RemainingSinkChunk() const; + + //! Initializes a chunk with the types that will flow out of the chunk void InitializeChunk(DataChunk &chunk); //! Execute a pipeline without a sink, and retrieve a single DataChunk //! Returns an empty chunk when finished. - void ExecutePull(DataChunk &result); - //! Called after depleting the source using ExecutePull - //! This flushes profiler states - void PullFinalize(); //! Registers the task in the interrupt_state to allow Source/Sink operators to block the task void SetTaskForInterrupts(weak_ptr current_task); diff --git a/src/duckdb/src/include/duckdb/parallel/task.hpp b/src/duckdb/src/include/duckdb/parallel/task.hpp index 2245c74a0..25a85b710 100644 --- a/src/duckdb/src/include/duckdb/parallel/task.hpp +++ b/src/duckdb/src/include/duckdb/parallel/task.hpp @@ -27,6 +27,7 @@ class Task : public std::enable_shared_from_this { virtual ~Task() { } +public: //! Execute the task in the specified execution mode //! If mode is PROCESS_ALL, Execute should always finish processing and return TASK_FINISHED //! If mode is PROCESS_PARTIAL, Execute can return TASK_NOT_FINISHED, in which case Execute will be called again @@ -45,6 +46,10 @@ class Task : public std::enable_shared_from_this { virtual void Reschedule() { throw InternalException("Cannot reschedule task of base Task class"); } + + virtual bool TaskBlockedOnResult() const { + return false; + } }; //! Execute a task within an executor, including exception handling @@ -55,9 +60,11 @@ class ExecutorTask : public Task { ExecutorTask(ClientContext &context); virtual ~ExecutorTask(); +public: void Deschedule() override; void Reschedule() override; +public: Executor &executor; public: diff --git a/src/duckdb/src/include/duckdb/parallel/task_scheduler.hpp b/src/duckdb/src/include/duckdb/parallel/task_scheduler.hpp index b54da51ce..1c04dbb88 100644 --- a/src/duckdb/src/include/duckdb/parallel/task_scheduler.hpp +++ b/src/duckdb/src/include/duckdb/parallel/task_scheduler.hpp @@ -61,6 +61,9 @@ class TaskScheduler { //! Sets the amount of active threads executing tasks for the system; n-1 background threads will be launched. //! The main thread will also be used for execution void SetThreads(int32_t n); + + void RelaunchThreads(); + //! Returns the number of threads DUCKDB_API int32_t NumberOfThreads(); @@ -74,7 +77,7 @@ class TaskScheduler { void SetAllocatorFlushTreshold(idx_t threshold); private: - void SetThreadsInternal(int32_t n); + void RelaunchThreadsInternal(int32_t n); private: DatabaseInstance &db; @@ -88,6 +91,8 @@ class TaskScheduler { vector>> markers; //! The threshold after which to flush the allocator after completing a task atomic allocator_flush_threshold; + //! Requested thread count + atomic thread_count; }; } // namespace duckdb diff --git a/src/duckdb/src/main/buffered_data/simple_buffered_data.cpp b/src/duckdb/src/main/buffered_data/simple_buffered_data.cpp new file mode 100644 index 000000000..f84cae8a1 --- /dev/null +++ b/src/duckdb/src/main/buffered_data/simple_buffered_data.cpp @@ -0,0 +1,96 @@ +#include "duckdb/main/buffered_data/simple_buffered_data.hpp" +#include "duckdb/common/printer.hpp" +#include "duckdb/main/client_context.hpp" +#include "duckdb/main/stream_query_result.hpp" +#include "duckdb/common/helper.hpp" + +namespace duckdb { + +SimpleBufferedData::SimpleBufferedData(weak_ptr context) + : BufferedData(BufferedData::Type::SIMPLE, std::move(context)) { + buffered_count = 0; +} + +SimpleBufferedData::~SimpleBufferedData() { +} + +void SimpleBufferedData::BlockSink(const BlockedSink &blocked_sink) { + lock_guard lock(glock); + blocked_sinks.push(blocked_sink); +} + +bool SimpleBufferedData::BufferIsFull() { + return buffered_count >= BUFFER_SIZE; +} + +void SimpleBufferedData::UnblockSinks() { + if (Closed()) { + return; + } + if (buffered_count >= BUFFER_SIZE) { + return; + } + // Reschedule enough blocked sinks to populate the buffer + lock_guard lock(glock); + while (!blocked_sinks.empty()) { + auto &blocked_sink = blocked_sinks.front(); + if (buffered_count >= BUFFER_SIZE) { + // We have unblocked enough sinks already + break; + } + blocked_sink.state.Callback(); + blocked_sinks.pop(); + } +} + +PendingExecutionResult SimpleBufferedData::ReplenishBuffer(StreamQueryResult &result, ClientContextLock &context_lock) { + if (Closed()) { + return PendingExecutionResult::EXECUTION_ERROR; + } + if (BufferIsFull()) { + // The buffer isn't empty yet, just return + return PendingExecutionResult::RESULT_READY; + } + UnblockSinks(); + auto cc = context.lock(); + // Let the executor run until the buffer is no longer empty + auto res = cc->ExecuteTaskInternal(context_lock, result); + while (!PendingQueryResult::IsFinished(res)) { + if (buffered_count >= BUFFER_SIZE) { + break; + } + // Check if we need to unblock more sinks to reach the buffer size + UnblockSinks(); + res = cc->ExecuteTaskInternal(context_lock, result); + } + if (result.HasError()) { + Close(); + } + return res; +} + +unique_ptr SimpleBufferedData::Scan() { + if (Closed()) { + return nullptr; + } + lock_guard lock(glock); + if (buffered_chunks.empty()) { + Close(); + return nullptr; + } + auto chunk = std::move(buffered_chunks.front()); + buffered_chunks.pop(); + + if (chunk) { + buffered_count -= chunk->size(); + } + return chunk; +} + +void SimpleBufferedData::Append(unique_ptr chunk) { + unique_lock lock(glock); + buffered_count += chunk->size(); + buffered_chunks.push(std::move(chunk)); +} + +} // namespace duckdb diff --git a/src/duckdb/src/main/capi/pending-c.cpp b/src/duckdb/src/main/capi/pending-c.cpp index 734754ff2..688758724 100644 --- a/src/duckdb/src/main/capi/pending-c.cpp +++ b/src/duckdb/src/main/capi/pending-c.cpp @@ -66,6 +66,37 @@ const char *duckdb_pending_error(duckdb_pending_result pending_result) { return wrapper->statement->GetError().c_str(); } +duckdb_pending_state duckdb_pending_execute_check_state(duckdb_pending_result pending_result) { + if (!pending_result) { + return DUCKDB_PENDING_ERROR; + } + auto wrapper = reinterpret_cast(pending_result); + if (!wrapper->statement) { + return DUCKDB_PENDING_ERROR; + } + if (wrapper->statement->HasError()) { + return DUCKDB_PENDING_ERROR; + } + PendingExecutionResult return_value; + try { + return_value = wrapper->statement->CheckPulse(); + } catch (std::exception &ex) { + wrapper->statement->SetError(duckdb::ErrorData(ex)); + return DUCKDB_PENDING_ERROR; + } + switch (return_value) { + case PendingExecutionResult::BLOCKED: + case PendingExecutionResult::RESULT_READY: + return DUCKDB_PENDING_RESULT_READY; + case PendingExecutionResult::NO_TASKS_AVAILABLE: + return DUCKDB_PENDING_NO_TASKS_AVAILABLE; + case PendingExecutionResult::RESULT_NOT_READY: + return DUCKDB_PENDING_RESULT_NOT_READY; + default: + return DUCKDB_PENDING_ERROR; + } +} + duckdb_pending_state duckdb_pending_execute_task(duckdb_pending_result pending_result) { if (!pending_result) { return DUCKDB_PENDING_ERROR; @@ -85,6 +116,7 @@ duckdb_pending_state duckdb_pending_execute_task(duckdb_pending_result pending_r return DUCKDB_PENDING_ERROR; } switch (return_value) { + case PendingExecutionResult::BLOCKED: case PendingExecutionResult::RESULT_READY: return DUCKDB_PENDING_RESULT_READY; case PendingExecutionResult::NO_TASKS_AVAILABLE: @@ -115,13 +147,20 @@ duckdb_state duckdb_execute_pending(duckdb_pending_result pending_result, duckdb if (!pending_result || !out_result) { return DuckDBError; } + memset(out_result, 0, sizeof(duckdb_result)); auto wrapper = reinterpret_cast(pending_result); if (!wrapper->statement) { return DuckDBError; } duckdb::unique_ptr result; - result = wrapper->statement->Execute(); + try { + result = wrapper->statement->Execute(); + } catch (std::exception &ex) { + duckdb::ErrorData error(ex); + result = duckdb::make_uniq(std::move(error)); + } + wrapper->statement.reset(); return duckdb_translate_result(std::move(result), out_result); } diff --git a/src/duckdb/src/main/capi/result-c.cpp b/src/duckdb/src/main/capi/result-c.cpp index 5f4070725..c3b62c410 100644 --- a/src/duckdb/src/main/capi/result-c.cpp +++ b/src/duckdb/src/main/capi/result-c.cpp @@ -483,7 +483,7 @@ bool *duckdb_nullmask_data(duckdb_result *result, idx_t col) { } const char *duckdb_result_error(duckdb_result *result) { - if (!result) { + if (!result || !result->internal_data) { return nullptr; } auto &result_data = *(reinterpret_cast(result->internal_data)); diff --git a/src/duckdb/src/main/client_context.cpp b/src/duckdb/src/main/client_context.cpp index e533dc158..672ebefa4 100644 --- a/src/duckdb/src/main/client_context.cpp +++ b/src/duckdb/src/main/client_context.cpp @@ -47,16 +47,30 @@ namespace duckdb { struct ActiveQueryContext { +public: //! The query that is currently being executed string query; - //! The currently open result - BaseQueryResult *open_result = nullptr; //! Prepared statement data shared_ptr prepared; //! The query executor unique_ptr executor; //! The progress bar unique_ptr progress_bar; + +public: + void SetOpenResult(BaseQueryResult &result) { + open_result = &result; + } + bool IsOpenResult(BaseQueryResult &result) { + return open_result == &result; + } + bool HasOpenResult() const { + return open_result != nullptr; + } + +private: + //! The currently open result + BaseQueryResult *open_result = nullptr; }; ClientContext::ClientContext(shared_ptr database) @@ -101,42 +115,6 @@ unique_ptr ClientContext::ErrorResult(ErrorData error, const string &query) { return make_uniq(std::move(error)); } -unique_ptr ClientContext::Fetch(ClientContextLock &lock, StreamQueryResult &result) { - D_ASSERT(IsActiveResult(lock, &result)); - D_ASSERT(active_query->executor); - return FetchInternal(lock, *active_query->executor, result); -} - -unique_ptr ClientContext::FetchInternal(ClientContextLock &lock, Executor &executor, - BaseQueryResult &result) { - bool invalidate_query = true; - try { - // fetch the chunk and return it - auto chunk = executor.FetchChunk(); - if (!chunk || chunk->size() == 0) { - CleanupInternal(lock, &result); - } - return chunk; - } catch (std::exception &ex) { - ErrorData error(ex); - auto exception_type = error.Type(); - if (!Exception::InvalidatesTransaction(exception_type)) { - // standard exceptions do not invalidate the current transaction - invalidate_query = false; - } else if (Exception::InvalidatesDatabase(exception_type)) { - // fatal exceptions invalidate the entire database - auto &db_inst = DatabaseInstance::GetDatabase(*this); - ValidChecker::Invalidate(db_inst, error.RawMessage()); - } - ProcessError(error, active_query->query); - result.SetError(std::move(error)); - } catch (...) { // LCOV_EXCL_START - result.SetError(ErrorData("Unhandled exception in FetchInternal")); - } // LCOV_EXCL_STOP - CleanupInternal(lock, &result, invalidate_query); - return nullptr; -} - void ClientContext::BeginTransactionInternal(ClientContextLock &lock, bool requires_valid_transaction) { // check if we are on AutoCommit. In this case we should start a transaction D_ASSERT(!active_query); @@ -222,6 +200,10 @@ void ClientContext::CleanupInternal(ClientContextLock &lock, BaseQueryResult *re } active_query->progress_bar.reset(); + // Relaunch the threads if a SET THREADS command was issued + auto &scheduler = TaskScheduler::GetScheduler(*this); + scheduler.RelaunchThreads(); + auto error = EndQueryInternal(lock, result ? !result->HasError() : false, invalidate_transaction); if (result && !result->HasError()) { // if an error occurred while committing report it in the result @@ -243,54 +225,19 @@ const string &ClientContext::GetCurrentQuery() { unique_ptr ClientContext::FetchResultInternal(ClientContextLock &lock, PendingQueryResult &pending) { D_ASSERT(active_query); - D_ASSERT(active_query->open_result == &pending); + D_ASSERT(active_query->IsOpenResult(pending)); D_ASSERT(active_query->prepared); auto &executor = GetExecutor(); auto &prepared = *active_query->prepared; bool create_stream_result = prepared.properties.allow_stream_result && pending.allow_stream_result; - if (create_stream_result) { - D_ASSERT(!executor.HasResultCollector()); - active_query->progress_bar.reset(); - query_progress.Initialize(); - // successfully compiled SELECT clause, and it is the last statement - // return a StreamQueryResult so the client can call Fetch() on it and stream the result - auto stream_result = make_uniq(pending.statement_type, pending.properties, - shared_from_this(), pending.types, pending.names); - active_query->open_result = stream_result.get(); - return std::move(stream_result); - } unique_ptr result; - if (executor.HasResultCollector()) { - // we have a result collector - fetch the result directly from the result collector - result = executor.GetResult(); + D_ASSERT(executor.HasResultCollector()); + // we have a result collector - fetch the result directly from the result collector + result = executor.GetResult(); + if (!create_stream_result) { CleanupInternal(lock, result.get(), false); } else { - // no result collector - create a materialized result by continuously fetching - auto result_collection = make_uniq(Allocator::DefaultAllocator(), pending.types); - D_ASSERT(!result_collection->Types().empty()); - auto materialized_result = - make_uniq(pending.statement_type, pending.properties, pending.names, - std::move(result_collection), GetClientProperties()); - - auto &collection = materialized_result->Collection(); - D_ASSERT(!collection.Types().empty()); - ColumnDataAppendState append_state; - collection.InitializeAppend(append_state); - while (true) { - auto chunk = FetchInternal(lock, GetExecutor(), *materialized_result); - if (!chunk || chunk->size() == 0) { - break; - } -#ifdef DEBUG - for (idx_t i = 0; i < chunk->ColumnCount(); i++) { - if (pending.types[i].id() == LogicalTypeId::VARCHAR) { - chunk->data[i].UTFVerify(chunk->size()); - } - } -#endif - collection.Append(append_state, *chunk); - } - result = std::move(materialized_result); + active_query->SetOpenResult(*result); } return result; } @@ -417,42 +364,56 @@ unique_ptr ClientContext::PendingPreparedStatement(ClientCon query_progress.Restart(); } auto stream_result = parameters.allow_stream_result && statement.properties.allow_stream_result; - if (!stream_result && statement.properties.return_type == StatementReturnType::QUERY_RESULT) { - unique_ptr collector; - auto &client_config = ClientConfig::GetConfig(*this); - auto get_method = client_config.result_collector ? client_config.result_collector - : PhysicalResultCollector::GetResultCollector; - collector = get_method(*this, statement); - D_ASSERT(collector->type == PhysicalOperatorType::RESULT_COLLECTOR); - executor.Initialize(std::move(collector)); - } else { - executor.Initialize(*statement.plan); + + get_result_collector_t get_method = PhysicalResultCollector::GetResultCollector; + auto &client_config = ClientConfig::GetConfig(*this); + if (!stream_result && client_config.result_collector) { + get_method = client_config.result_collector; } + statement.is_streaming = stream_result; + auto collector = get_method(*this, statement); + D_ASSERT(collector->type == PhysicalOperatorType::RESULT_COLLECTOR); + executor.Initialize(std::move(collector)); + auto types = executor.GetTypes(); D_ASSERT(types == statement.types); - D_ASSERT(!active_query->open_result); + D_ASSERT(!active_query->HasOpenResult()); auto pending_result = make_uniq(shared_from_this(), *statement_p, std::move(types), stream_result); active_query->prepared = std::move(statement_p); - active_query->open_result = pending_result.get(); + active_query->SetOpenResult(*pending_result); return pending_result; } -PendingExecutionResult ClientContext::ExecuteTaskInternal(ClientContextLock &lock, PendingQueryResult &result) { +PendingExecutionResult ClientContext::ExecuteTaskInternal(ClientContextLock &lock, BaseQueryResult &result, + bool dry_run) { D_ASSERT(active_query); - D_ASSERT(active_query->open_result == &result); + D_ASSERT(active_query->IsOpenResult(result)); bool invalidate_transaction = true; try { - auto query_result = active_query->executor->ExecuteTask(); + auto query_result = active_query->executor->ExecuteTask(dry_run); if (active_query->progress_bar) { - active_query->progress_bar->Update(query_result == PendingExecutionResult::RESULT_READY); + auto is_finished = PendingQueryResult::IsFinishedOrBlocked(query_result); + active_query->progress_bar->Update(is_finished); query_progress = active_query->progress_bar->GetDetailedQueryProgress(); } return query_result; } catch (std::exception &ex) { auto error = ErrorData(ex); - if (!Exception::InvalidatesTransaction(error.Type())) { + if (error.Type() == ExceptionType::INTERRUPT) { + auto &executor = *active_query->executor; + if (!executor.HasError()) { + // Interrupted by the user + result.SetError(ex); + invalidate_transaction = true; + } else { + // Interrupted by an exception caused in a worker thread + auto error = executor.GetError(); + invalidate_transaction = Exception::InvalidatesTransaction(error.Type()); + result.SetError(error); + } + } else if (!Exception::InvalidatesTransaction(error.Type())) { invalidate_transaction = false; } else if (Exception::InvalidatesDatabase(error.Type())) { // fatal exceptions invalidate the entire database @@ -637,11 +598,18 @@ unique_ptr ClientContext::RunStatementInternal(ClientContextLock &l return ExecutePendingQueryInternal(lock, *pending); } -bool ClientContext::IsActiveResult(ClientContextLock &lock, BaseQueryResult *result) { +bool ClientContext::IsActiveResult(ClientContextLock &lock, BaseQueryResult &result) { if (!active_query) { return false; } - return active_query->open_result == result; + return active_query->IsOpenResult(result); +} + +void ClientContext::SetActiveResult(ClientContextLock &lock, BaseQueryResult &result) { + if (!active_query) { + return; + } + return active_query->SetOpenResult(result); } unique_ptr ClientContext::PendingStatementOrPreparedStatementInternal( @@ -700,7 +668,7 @@ unique_ptr ClientContext::PendingStatementOrPreparedStatemen unique_ptr ClientContext::PendingStatementOrPreparedStatement( ClientContextLock &lock, const string &query, unique_ptr statement, shared_ptr &prepared, const PendingQueryParameters ¶meters) { - unique_ptr result; + unique_ptr pending; try { BeginQueryInternal(lock, query); @@ -720,7 +688,7 @@ unique_ptr ClientContext::PendingStatementOrPreparedStatemen bool invalidate_query = true; try { if (statement) { - result = PendingStatementInternal(lock, query, std::move(statement), parameters); + pending = PendingStatementInternal(lock, query, std::move(statement), parameters); } else { if (prepared->RequireRebind(*this, parameters.parameters)) { // catalog was modified: rebind the statement before execution @@ -731,7 +699,7 @@ unique_ptr ClientContext::PendingStatementOrPreparedStatemen prepared = std::move(new_prepared); prepared->properties.bound_all_parameters = false; } - result = PendingPreparedStatement(lock, prepared, parameters); + pending = PendingPreparedStatement(lock, prepared, parameters); } } catch (std::exception &ex) { ErrorData error(ex); @@ -746,15 +714,15 @@ unique_ptr ClientContext::PendingStatementOrPreparedStatemen } } // other types of exceptions do invalidate the current transaction - result = ErrorResult(std::move(error), query); + pending = ErrorResult(std::move(error), query); } - if (result->HasError()) { + if (pending->HasError()) { // query failed: abort now EndQueryInternal(lock, false, invalidate_query); - return result; + return pending; } - D_ASSERT(active_query->open_result == result.get()); - return result; + D_ASSERT(active_query->IsOpenResult(*pending)); + return pending; } void ClientContext::LogQueryInternal(ClientContextLock &, const string &query) { @@ -835,6 +803,12 @@ unique_ptr ClientContext::Query(const string &query, bool allow_str last_result->next = std::move(current_result); last_result = last_result->next.get(); } + D_ASSERT(last_result); + if (last_result->HasError()) { + // Reset the interrupted flag, this was set by the task that found the error + // Next statements should not be bothered by that interruption + interrupted = false; + } } return result; } diff --git a/src/duckdb/src/main/database.cpp b/src/duckdb/src/main/database.cpp index d62f9f796..189131493 100644 --- a/src/duckdb/src/main/database.cpp +++ b/src/duckdb/src/main/database.cpp @@ -244,6 +244,7 @@ void DatabaseInstance::Initialize(const char *database_path, DBConfig *user_conf // only increase thread count after storage init because we get races on catalog otherwise scheduler->SetThreads(config.options.maximum_threads); + scheduler->RelaunchThreads(); } DuckDB::DuckDB(const char *path, DBConfig *new_config) : instance(make_shared()) { diff --git a/src/duckdb/src/main/pending_query_result.cpp b/src/duckdb/src/main/pending_query_result.cpp index 89ec1c451..a112286e2 100644 --- a/src/duckdb/src/main/pending_query_result.cpp +++ b/src/duckdb/src/main/pending_query_result.cpp @@ -32,7 +32,7 @@ unique_ptr PendingQueryResult::LockContext() { void PendingQueryResult::CheckExecutableInternal(ClientContextLock &lock) { bool invalidated = HasError() || !context; if (!invalidated) { - invalidated = !context->IsActiveResult(lock, this); + invalidated = !context->IsActiveResult(lock, *this); } if (invalidated) { if (HasError()) { @@ -48,15 +48,30 @@ PendingExecutionResult PendingQueryResult::ExecuteTask() { return ExecuteTaskInternal(*lock); } +PendingExecutionResult PendingQueryResult::CheckPulse() { + auto lock = LockContext(); + CheckExecutableInternal(*lock); + return context->ExecuteTaskInternal(*lock, *this, true); +} + +bool PendingQueryResult::AllowStreamResult() const { + return allow_stream_result; +} + PendingExecutionResult PendingQueryResult::ExecuteTaskInternal(ClientContextLock &lock) { CheckExecutableInternal(lock); - return context->ExecuteTaskInternal(lock, *this); + return context->ExecuteTaskInternal(lock, *this, false); } unique_ptr PendingQueryResult::ExecuteInternal(ClientContextLock &lock) { CheckExecutableInternal(lock); // Busy wait while execution is not finished - while (!IsFinished(ExecuteTaskInternal(lock))) { + if (allow_stream_result) { + while (!IsFinishedOrBlocked(ExecuteTaskInternal(lock))) { + } + } else { + while (!IsFinished(ExecuteTaskInternal(lock))) { + } } if (HasError()) { return make_uniq(error); @@ -76,10 +91,12 @@ void PendingQueryResult::Close() { } bool PendingQueryResult::IsFinished(PendingExecutionResult result) { - if (result == PendingExecutionResult::RESULT_READY || result == PendingExecutionResult::EXECUTION_ERROR) { - return true; - } - return false; + return (result == PendingExecutionResult::RESULT_READY || result == PendingExecutionResult::EXECUTION_ERROR); +} + +bool PendingQueryResult::IsFinishedOrBlocked(PendingExecutionResult result) { + return (result == PendingExecutionResult::RESULT_READY || result == PendingExecutionResult::EXECUTION_ERROR || + result == PendingExecutionResult::BLOCKED); } } // namespace duckdb diff --git a/src/duckdb/src/main/query_result.cpp b/src/duckdb/src/main/query_result.cpp index 1c96468b0..c24fab135 100644 --- a/src/duckdb/src/main/query_result.cpp +++ b/src/duckdb/src/main/query_result.cpp @@ -15,6 +15,8 @@ BaseQueryResult::BaseQueryResult(QueryResultType type, StatementType statement_t BaseQueryResult::BaseQueryResult(QueryResultType type, ErrorData error) : type(type), success(false), error(std::move(error)) { + // Assert that the error object is initialized + D_ASSERT(this->error.HasError()); } BaseQueryResult::~BaseQueryResult() { diff --git a/src/duckdb/src/main/stream_query_result.cpp b/src/duckdb/src/main/stream_query_result.cpp index 903e485e2..73ab07c39 100644 --- a/src/duckdb/src/main/stream_query_result.cpp +++ b/src/duckdb/src/main/stream_query_result.cpp @@ -3,16 +3,17 @@ #include "duckdb/main/client_context.hpp" #include "duckdb/main/materialized_query_result.hpp" #include "duckdb/common/box_renderer.hpp" +#include "duckdb/main/database.hpp" namespace duckdb { StreamQueryResult::StreamQueryResult(StatementType statement_type, StatementProperties properties, - shared_ptr context_p, vector types, - vector names) + vector types, vector names, + ClientProperties client_properties, shared_ptr data) : QueryResult(QueryResultType::STREAM_RESULT, statement_type, std::move(properties), std::move(types), - std::move(names), context_p->GetClientProperties()), - context(std::move(context_p)) { - D_ASSERT(context); + std::move(names), std::move(client_properties)), + buffered_data(std::move(data)) { + context = buffered_data->GetContext(); } StreamQueryResult::~StreamQueryResult() { @@ -40,14 +41,41 @@ unique_ptr StreamQueryResult::LockContext() { return context->LockContext(); } -void StreamQueryResult::CheckExecutableInternal(ClientContextLock &lock) { - if (!IsOpenInternal(lock)) { - string error_str = "Attempting to execute an unsuccessful or closed pending query result"; - if (HasError()) { - error_str += StringUtil::Format("\nError: %s", GetError()); +unique_ptr StreamQueryResult::FetchInternal(ClientContextLock &lock) { + bool invalidate_query = true; + unique_ptr chunk; + try { + // fetch the chunk and return it + auto res = buffered_data->ReplenishBuffer(*this, lock); + if (res == PendingExecutionResult::EXECUTION_ERROR) { + return chunk; } - throw InvalidInputException(error_str); - } + chunk = buffered_data->Scan(); + if (!chunk || chunk->ColumnCount() == 0 || chunk->size() == 0) { + context->CleanupInternal(lock, this); + chunk = nullptr; + } + return chunk; + } catch (std::exception &ex) { + ErrorData error(ex); + if (!Exception::InvalidatesTransaction(error.Type())) { + // standard exceptions do not invalidate the current transaction + invalidate_query = false; + } else if (Exception::InvalidatesDatabase(error.Type())) { + // fatal exceptions invalidate the entire database + auto &config = context->config; + if (!config.query_verification_enabled) { + auto &db_instance = DatabaseInstance::GetDatabase(*context); + ValidChecker::Invalidate(db_instance, error.RawMessage()); + } + } + context->ProcessError(error, context->GetCurrentQuery()); + SetError(std::move(error)); + } catch (...) { // LCOV_EXCL_START + SetError(ErrorData("Unhandled exception in FetchInternal")); + } // LCOV_EXCL_STOP + context->CleanupInternal(lock, this, invalidate_query); + return nullptr; } unique_ptr StreamQueryResult::FetchRaw() { @@ -55,12 +83,7 @@ unique_ptr StreamQueryResult::FetchRaw() { { auto lock = LockContext(); CheckExecutableInternal(*lock); - auto system_chunk = context->Fetch(*lock, *this); - if (system_chunk) { - chunk = make_uniq(); - chunk->Initialize(Allocator::DefaultAllocator(), system_chunk->GetTypes()); - system_chunk->Copy(*chunk, 0); - } + chunk = FetchInternal(*lock); } if (!chunk || chunk->ColumnCount() == 0 || chunk->size() == 0) { Close(); @@ -95,11 +118,21 @@ unique_ptr StreamQueryResult::Materialize() { bool StreamQueryResult::IsOpenInternal(ClientContextLock &lock) { bool invalidated = !success || !context; if (!invalidated) { - invalidated = !context->IsActiveResult(lock, this); + invalidated = !context->IsActiveResult(lock, *this); } return !invalidated; } +void StreamQueryResult::CheckExecutableInternal(ClientContextLock &lock) { + if (!IsOpenInternal(lock)) { + string error_str = "Attempting to execute an unsuccessful or closed pending query result"; + if (HasError()) { + error_str += StringUtil::Format("\nError: %s", GetError()); + } + throw InvalidInputException(error_str); + } +} + bool StreamQueryResult::IsOpen() { if (!success || !context) { return false; @@ -109,6 +142,7 @@ bool StreamQueryResult::IsOpen() { } void StreamQueryResult::Close() { + buffered_data->Close(); context.reset(); } diff --git a/src/duckdb/src/parallel/executor.cpp b/src/duckdb/src/parallel/executor.cpp index eed8e535d..730f04a09 100644 --- a/src/duckdb/src/parallel/executor.cpp +++ b/src/duckdb/src/parallel/executor.cpp @@ -442,6 +442,28 @@ void Executor::RescheduleTask(shared_ptr &task_p) { } } +bool Executor::ResultCollectorIsBlocked() { + if (completed_pipelines + 1 != total_pipelines) { + // The result collector is always in the last pipeline + return false; + } + lock_guard l(executor_lock); + if (to_be_rescheduled_tasks.empty()) { + return false; + } + for (auto &kv : to_be_rescheduled_tasks) { + auto &task = kv.second; + if (task->TaskBlockedOnResult()) { + // At least one of the blocked tasks is connected to a result collector + // This task could be the only task that could unblock the other non-result-collector tasks + // To prevent a scenario where we halt indefinitely, we return here so it can be unblocked by a call to + // Fetch + return true; + } + } + return false; +} + void Executor::AddToBeRescheduled(shared_ptr &task_p) { lock_guard l(executor_lock); if (cancelled) { @@ -457,7 +479,7 @@ bool Executor::ExecutionIsFinished() { return completed_pipelines >= total_pipelines || HasError(); } -PendingExecutionResult Executor::ExecuteTask() { +PendingExecutionResult Executor::ExecuteTask(bool dry_run) { // Only executor should return NO_TASKS_AVAILABLE D_ASSERT(execution_result != PendingExecutionResult::NO_TASKS_AVAILABLE); if (execution_result != PendingExecutionResult::RESULT_NOT_READY) { @@ -467,14 +489,29 @@ PendingExecutionResult Executor::ExecuteTask() { auto &scheduler = TaskScheduler::GetScheduler(context); while (completed_pipelines < total_pipelines) { // there are! if we don't already have a task, fetch one - if (!task) { - scheduler.GetTaskFromProducer(*producer, task); + auto current_task = task.get(); + if (dry_run) { + // Pretend we have no task, we don't want to execute anything + current_task = nullptr; + } else { + if (!task) { + scheduler.GetTaskFromProducer(*producer, task); + } + current_task = task.get(); } - if (!task && !HasError()) { + + if (!current_task && !HasError()) { // there are no tasks to be scheduled and there are tasks blocked + if (ResultCollectorIsBlocked()) { + // The blocked tasks are processing the Sink of a BufferedResultCollector + // We return here so the query result can be made and fetched from + // which will in turn unblock the Sink tasks. + return PendingExecutionResult::BLOCKED; + } return PendingExecutionResult::NO_TASKS_AVAILABLE; } - if (task) { + + if (current_task) { // if we have a task, partially process it auto result = task->Execute(TaskExecutionMode::PROCESS_PARTIAL); if (result == TaskExecutionResult::TASK_BLOCKED) { @@ -564,6 +601,10 @@ bool Executor::HasError() { return error_manager.HasError(); } +ErrorData Executor::GetError() { + return error_manager.GetError(); +} + void Executor::ThrowException() { error_manager.ThrowException(); } @@ -616,24 +657,4 @@ unique_ptr Executor::GetResult() { return result_collector.GetResult(*result_collector.sink_state); } -unique_ptr Executor::FetchChunk() { - D_ASSERT(physical_plan); - - auto chunk = make_uniq(); - root_executor->InitializeChunk(*chunk); - while (true) { - root_executor->ExecutePull(*chunk); - if (chunk->size() == 0) { - root_executor->PullFinalize(); - if (NextExecutor()) { - continue; - } - break; - } else { - break; - } - } - return chunk; -} - } // namespace duckdb diff --git a/src/duckdb/src/parallel/pipeline.cpp b/src/duckdb/src/parallel/pipeline.cpp index 1448b1007..aee671b92 100644 --- a/src/duckdb/src/parallel/pipeline.cpp +++ b/src/duckdb/src/parallel/pipeline.cpp @@ -15,54 +15,54 @@ namespace duckdb { -class PipelineTask : public ExecutorTask { - static constexpr const idx_t PARTIAL_CHUNK_COUNT = 50; +PipelineTask::PipelineTask(Pipeline &pipeline_p, shared_ptr event_p) + : ExecutorTask(pipeline_p.executor), pipeline(pipeline_p), event(std::move(event_p)) { +} + +bool PipelineTask::TaskBlockedOnResult() const { + // If this returns true, it means the pipeline this task belongs to has a cached chunk + // that was the result of the Sink method returning BLOCKED + return pipeline_executor->RemainingSinkChunk(); +} + +const PipelineExecutor &PipelineTask::GetPipelineExecutor() const { + return *pipeline_executor; +} -public: - explicit PipelineTask(Pipeline &pipeline_p, shared_ptr event_p) - : ExecutorTask(pipeline_p.executor), pipeline(pipeline_p), event(std::move(event_p)) { +TaskExecutionResult PipelineTask::ExecuteTask(TaskExecutionMode mode) { + if (!pipeline_executor) { + pipeline_executor = make_uniq(pipeline.GetClientContext(), pipeline); } - Pipeline &pipeline; - shared_ptr event; - unique_ptr pipeline_executor; + pipeline_executor->SetTaskForInterrupts(shared_from_this()); -public: - TaskExecutionResult ExecuteTask(TaskExecutionMode mode) override { - if (!pipeline_executor) { - pipeline_executor = make_uniq(pipeline.GetClientContext(), pipeline); - } + if (mode == TaskExecutionMode::PROCESS_PARTIAL) { + auto res = pipeline_executor->Execute(PARTIAL_CHUNK_COUNT); - pipeline_executor->SetTaskForInterrupts(shared_from_this()); - - if (mode == TaskExecutionMode::PROCESS_PARTIAL) { - auto res = pipeline_executor->Execute(PARTIAL_CHUNK_COUNT); - - switch (res) { - case PipelineExecuteResult::NOT_FINISHED: - return TaskExecutionResult::TASK_NOT_FINISHED; - case PipelineExecuteResult::INTERRUPTED: - return TaskExecutionResult::TASK_BLOCKED; - case PipelineExecuteResult::FINISHED: - break; - } - } else { - auto res = pipeline_executor->Execute(); - switch (res) { - case PipelineExecuteResult::NOT_FINISHED: - throw InternalException("Execute without limit should not return NOT_FINISHED"); - case PipelineExecuteResult::INTERRUPTED: - return TaskExecutionResult::TASK_BLOCKED; - case PipelineExecuteResult::FINISHED: - break; - } + switch (res) { + case PipelineExecuteResult::NOT_FINISHED: + return TaskExecutionResult::TASK_NOT_FINISHED; + case PipelineExecuteResult::INTERRUPTED: + return TaskExecutionResult::TASK_BLOCKED; + case PipelineExecuteResult::FINISHED: + break; + } + } else { + auto res = pipeline_executor->Execute(); + switch (res) { + case PipelineExecuteResult::NOT_FINISHED: + throw InternalException("Execute without limit should not return NOT_FINISHED"); + case PipelineExecuteResult::INTERRUPTED: + return TaskExecutionResult::TASK_BLOCKED; + case PipelineExecuteResult::FINISHED: + break; } - - event->FinishTask(); - pipeline_executor.reset(); - return TaskExecutionResult::TASK_FINISHED; } -}; + + event->FinishTask(); + pipeline_executor.reset(); + return TaskExecutionResult::TASK_FINISHED; +} Pipeline::Pipeline(Executor &executor_p) : executor(executor_p), ready(false), initialized(false), source(nullptr), sink(nullptr) { diff --git a/src/duckdb/src/parallel/pipeline_executor.cpp b/src/duckdb/src/parallel/pipeline_executor.cpp index 2c00aef94..fadfd8bd0 100644 --- a/src/duckdb/src/parallel/pipeline_executor.cpp +++ b/src/duckdb/src/parallel/pipeline_executor.cpp @@ -245,6 +245,10 @@ PipelineExecuteResult PipelineExecutor::Execute(idx_t max_chunks) { return PushFinalize(); } +bool PipelineExecutor::RemainingSinkChunk() const { + return remaining_sink_chunk; +} + PipelineExecuteResult PipelineExecutor::Execute() { return Execute(NumericLimits::Maximum()); } @@ -348,73 +352,6 @@ PipelineExecuteResult PipelineExecutor::PushFinalize() { return PipelineExecuteResult::FINISHED; } -// TODO: Refactoring the StreamingQueryResult to use Push-based execution should eliminate the need for this code -void PipelineExecutor::ExecutePull(DataChunk &result) { - if (IsFinished()) { - return; - } - auto &executor = pipeline.executor; - try { - D_ASSERT(!pipeline.sink); - D_ASSERT(!requires_batch_index); - auto &source_chunk = pipeline.operators.empty() ? result : *intermediate_chunks[0]; - while (result.size() == 0 && (!exhausted_source || !in_process_operators.empty())) { - if (in_process_operators.empty()) { - source_chunk.Reset(); - - auto done_signal = make_shared(); - interrupt_state = InterruptState(done_signal); - SourceResultType source_result; - - // Repeatedly try to fetch from the source until it doesn't block. Note that it may block multiple times - while (true) { - D_ASSERT(!exhausted_source); - source_result = FetchFromSource(source_chunk); - - // No interrupt happened, all good. - if (source_result != SourceResultType::BLOCKED) { - break; - } - - // Busy wait for async callback from source operator - done_signal->Await(); - } - - if (source_result == SourceResultType::FINISHED) { - exhausted_source = true; - if (source_chunk.size() == 0) { - break; - } - } - } - if (!pipeline.operators.empty()) { - auto state = Execute(source_chunk, result); - if (state == OperatorResultType::FINISHED) { - break; - } - } - } - } catch (std::exception &ex) { // LCOV_EXCL_START - if (executor.HasError()) { - executor.ThrowException(); - } - throw; - } catch (...) { - if (executor.HasError()) { - executor.ThrowException(); - } - throw; - } // LCOV_EXCL_STOP -} - -void PipelineExecutor::PullFinalize() { - if (finalized) { - throw InternalException("Calling PullFinalize on a pipeline that has been finalized already"); - } - finalized = true; - pipeline.executor.Flush(thread); -} - void PipelineExecutor::GoToSource(idx_t ¤t_idx, idx_t initial_idx) { // we go back to the first operator (the source) current_idx = initial_idx; diff --git a/src/duckdb/src/parallel/task_scheduler.cpp b/src/duckdb/src/parallel/task_scheduler.cpp index d0d3bed5b..9455e12eb 100644 --- a/src/duckdb/src/parallel/task_scheduler.cpp +++ b/src/duckdb/src/parallel/task_scheduler.cpp @@ -97,12 +97,12 @@ ProducerToken::~ProducerToken() { TaskScheduler::TaskScheduler(DatabaseInstance &db) : db(db), queue(make_uniq()), - allocator_flush_threshold(db.config.options.allocator_flush_threshold) { + allocator_flush_threshold(db.config.options.allocator_flush_threshold), thread_count(1) { } TaskScheduler::~TaskScheduler() { #ifndef DUCKDB_NO_THREADS - SetThreadsInternal(1); + RelaunchThreadsInternal(1); #endif } @@ -236,11 +236,10 @@ int32_t TaskScheduler::NumberOfThreads() { void TaskScheduler::SetThreads(int32_t n) { #ifndef DUCKDB_NO_THREADS - lock_guard t(thread_lock); if (n < 1) { throw SyntaxException("Must have at least 1 thread!"); } - SetThreadsInternal(n); + thread_count = n; #else if (n != 1) { throw NotImplementedException("DuckDB was compiled without threads! Setting threads > 1 is not allowed."); @@ -263,7 +262,13 @@ void TaskScheduler::YieldThread() { #endif } -void TaskScheduler::SetThreadsInternal(int32_t n) { +void TaskScheduler::RelaunchThreads() { + lock_guard t(thread_lock); + auto n = thread_count.load(); + RelaunchThreadsInternal(n); +} + +void TaskScheduler::RelaunchThreadsInternal(int32_t n) { #ifndef DUCKDB_NO_THREADS if (threads.size() == idx_t(n - 1)) { return; diff --git a/src/duckdb/ub_src_execution_operator_helper.cpp b/src/duckdb/ub_src_execution_operator_helper.cpp index 0821c06b2..6215a4b4b 100644 --- a/src/duckdb/ub_src_execution_operator_helper.cpp +++ b/src/duckdb/ub_src_execution_operator_helper.cpp @@ -1,5 +1,7 @@ #include "src/execution/operator/helper/physical_batch_collector.cpp" +#include "src/execution/operator/helper/physical_buffered_collector.cpp" + #include "src/execution/operator/helper/physical_create_secret.cpp" #include "src/execution/operator/helper/physical_execute.cpp" diff --git a/src/duckdb/ub_src_main_buffered_data.cpp b/src/duckdb/ub_src_main_buffered_data.cpp new file mode 100644 index 000000000..824214bff --- /dev/null +++ b/src/duckdb/ub_src_main_buffered_data.cpp @@ -0,0 +1,2 @@ +#include "src/main/buffered_data/simple_buffered_data.cpp" + diff --git a/src/include/sources.mk b/src/include/sources.mk index d134e9c62..cc12be870 100644 --- a/src/include/sources.mk +++ b/src/include/sources.mk @@ -1 +1 @@ -SOURCES=duckdb/ub_src_catalog.o duckdb/ub_src_catalog_catalog_entry.o duckdb/ub_src_catalog_catalog_entry_dependency.o duckdb/ub_src_catalog_default.o duckdb/ub_src_common_adbc.o duckdb/ub_src_common_adbc_nanoarrow.o duckdb/ub_src_common.o duckdb/ub_src_common_arrow_appender.o duckdb/ub_src_common_arrow.o duckdb/ub_src_common_crypto.o duckdb/ub_src_common_enums.o duckdb/ub_src_common_exception.o duckdb/ub_src_common_operator.o duckdb/ub_src_common_progress_bar.o duckdb/ub_src_common_row_operations.o duckdb/ub_src_common_serializer.o duckdb/ub_src_common_sort.o duckdb/ub_src_common_types.o duckdb/ub_src_common_types_column.o duckdb/ub_src_common_types_row.o duckdb/ub_src_common_value_operations.o duckdb/src/common/vector_operations/boolean_operators.o duckdb/src/common/vector_operations/comparison_operators.o duckdb/src/common/vector_operations/generators.o duckdb/src/common/vector_operations/is_distinct_from.o duckdb/src/common/vector_operations/null_operations.o duckdb/src/common/vector_operations/numeric_inplace_operators.o duckdb/src/common/vector_operations/vector_cast.o duckdb/src/common/vector_operations/vector_copy.o duckdb/src/common/vector_operations/vector_hash.o duckdb/src/common/vector_operations/vector_storage.o duckdb/ub_src_core_functions_aggregate_algebraic.o duckdb/ub_src_core_functions_aggregate_distributive.o duckdb/ub_src_core_functions_aggregate_holistic.o duckdb/ub_src_core_functions_aggregate_nested.o duckdb/ub_src_core_functions_aggregate_regression.o duckdb/ub_src_core_functions.o duckdb/ub_src_core_functions_scalar_array.o duckdb/ub_src_core_functions_scalar_bit.o duckdb/ub_src_core_functions_scalar_blob.o duckdb/ub_src_core_functions_scalar_date.o duckdb/ub_src_core_functions_scalar_debug.o duckdb/ub_src_core_functions_scalar_enum.o duckdb/ub_src_core_functions_scalar_generic.o duckdb/ub_src_core_functions_scalar_list.o duckdb/ub_src_core_functions_scalar_map.o duckdb/ub_src_core_functions_scalar_math.o duckdb/ub_src_core_functions_scalar_operators.o duckdb/ub_src_core_functions_scalar_random.o duckdb/ub_src_core_functions_scalar_secret.o duckdb/ub_src_core_functions_scalar_string.o duckdb/ub_src_core_functions_scalar_struct.o duckdb/ub_src_core_functions_scalar_union.o duckdb/ub_src_execution.o duckdb/ub_src_execution_expression_executor.o duckdb/ub_src_execution_index_art.o duckdb/ub_src_execution_index.o duckdb/ub_src_execution_nested_loop_join.o duckdb/ub_src_execution_operator_aggregate.o duckdb/ub_src_execution_operator_csv_scanner_buffer_manager.o duckdb/ub_src_execution_operator_csv_scanner_scanner.o duckdb/ub_src_execution_operator_csv_scanner_sniffer.o duckdb/ub_src_execution_operator_csv_scanner_state_machine.o duckdb/ub_src_execution_operator_csv_scanner_table_function.o duckdb/ub_src_execution_operator_csv_scanner_util.o duckdb/ub_src_execution_operator_filter.o duckdb/ub_src_execution_operator_helper.o duckdb/ub_src_execution_operator_join.o duckdb/ub_src_execution_operator_order.o duckdb/ub_src_execution_operator_persistent.o duckdb/ub_src_execution_operator_projection.o duckdb/ub_src_execution_operator_scan.o duckdb/ub_src_execution_operator_schema.o duckdb/ub_src_execution_operator_set.o duckdb/ub_src_execution_physical_plan.o duckdb/ub_src_function_aggregate_distributive.o duckdb/ub_src_function_aggregate.o duckdb/ub_src_function.o duckdb/ub_src_function_cast.o duckdb/ub_src_function_cast_union.o duckdb/ub_src_function_pragma.o duckdb/ub_src_function_scalar_compressed_materialization.o duckdb/ub_src_function_scalar.o duckdb/ub_src_function_scalar_generic.o duckdb/ub_src_function_scalar_list.o duckdb/ub_src_function_scalar_operators.o duckdb/ub_src_function_scalar_sequence.o duckdb/ub_src_function_scalar_string.o duckdb/ub_src_function_scalar_string_regexp.o duckdb/ub_src_function_scalar_struct.o duckdb/ub_src_function_scalar_system.o duckdb/ub_src_function_table_arrow.o duckdb/ub_src_function_table.o duckdb/ub_src_function_table_system.o duckdb/ub_src_function_table_version.o duckdb/ub_src_main.o duckdb/ub_src_main_capi.o duckdb/ub_src_main_capi_cast.o duckdb/ub_src_main_chunk_scan_state.o duckdb/ub_src_main_extension.o duckdb/ub_src_main_relation.o duckdb/ub_src_main_secret.o duckdb/ub_src_main_settings.o duckdb/ub_src_optimizer.o duckdb/ub_src_optimizer_compressed_materialization.o duckdb/ub_src_optimizer_join_order.o duckdb/ub_src_optimizer_matcher.o duckdb/ub_src_optimizer_pullup.o duckdb/ub_src_optimizer_pushdown.o duckdb/ub_src_optimizer_rule.o duckdb/ub_src_optimizer_statistics_expression.o duckdb/ub_src_optimizer_statistics_operator.o duckdb/ub_src_parallel.o duckdb/ub_src_parser.o duckdb/ub_src_parser_constraints.o duckdb/ub_src_parser_expression.o duckdb/ub_src_parser_parsed_data.o duckdb/ub_src_parser_query_node.o duckdb/ub_src_parser_statement.o duckdb/ub_src_parser_tableref.o duckdb/ub_src_parser_transform_constraint.o duckdb/ub_src_parser_transform_expression.o duckdb/ub_src_parser_transform_helpers.o duckdb/ub_src_parser_transform_statement.o duckdb/ub_src_parser_transform_tableref.o duckdb/ub_src_planner.o duckdb/ub_src_planner_binder_expression.o duckdb/ub_src_planner_binder_query_node.o duckdb/ub_src_planner_binder_statement.o duckdb/ub_src_planner_binder_tableref.o duckdb/ub_src_planner_expression.o duckdb/ub_src_planner_expression_binder.o duckdb/ub_src_planner_filter.o duckdb/ub_src_planner_operator.o duckdb/ub_src_planner_subquery.o duckdb/ub_src_storage.o duckdb/ub_src_storage_buffer.o duckdb/ub_src_storage_checkpoint.o duckdb/ub_src_storage_compression_alp.o duckdb/ub_src_storage_compression.o duckdb/ub_src_storage_compression_chimp.o duckdb/ub_src_storage_metadata.o duckdb/ub_src_storage_serialization.o duckdb/ub_src_storage_statistics.o duckdb/ub_src_storage_table.o duckdb/ub_src_transaction.o duckdb/src/verification/copied_statement_verifier.o duckdb/src/verification/deserialized_statement_verifier.o duckdb/src/verification/external_statement_verifier.o duckdb/src/verification/fetch_row_verifier.o duckdb/src/verification/no_operator_caching_verifier.o duckdb/src/verification/parsed_statement_verifier.o duckdb/src/verification/prepared_statement_verifier.o duckdb/src/verification/statement_verifier.o duckdb/src/verification/unoptimized_statement_verifier.o duckdb/third_party/fmt/format.o duckdb/third_party/fsst/fsst_avx512.o duckdb/third_party/fsst/libfsst.o duckdb/third_party/miniz/miniz.o duckdb/third_party/re2/re2/bitstate.o duckdb/third_party/re2/re2/compile.o duckdb/third_party/re2/re2/dfa.o duckdb/third_party/re2/re2/filtered_re2.o duckdb/third_party/re2/re2/mimics_pcre.o duckdb/third_party/re2/re2/nfa.o duckdb/third_party/re2/re2/onepass.o duckdb/third_party/re2/re2/parse.o duckdb/third_party/re2/re2/perl_groups.o duckdb/third_party/re2/re2/prefilter.o duckdb/third_party/re2/re2/prefilter_tree.o duckdb/third_party/re2/re2/prog.o duckdb/third_party/re2/re2/re2.o duckdb/third_party/re2/re2/regexp.o duckdb/third_party/re2/re2/set.o duckdb/third_party/re2/re2/simplify.o duckdb/third_party/re2/re2/stringpiece.o duckdb/third_party/re2/re2/tostring.o duckdb/third_party/re2/re2/unicode_casefold.o duckdb/third_party/re2/re2/unicode_groups.o duckdb/third_party/re2/util/rune.o duckdb/third_party/re2/util/strutil.o duckdb/third_party/hyperloglog/hyperloglog.o duckdb/third_party/hyperloglog/sds.o duckdb/third_party/skiplist/SkipList.o duckdb/third_party/fastpforlib/bitpacking.o duckdb/third_party/utf8proc/utf8proc.o duckdb/third_party/utf8proc/utf8proc_wrapper.o duckdb/third_party/libpg_query/pg_functions.o duckdb/third_party/libpg_query/postgres_parser.o duckdb/third_party/libpg_query/src_backend_nodes_list.o duckdb/third_party/libpg_query/src_backend_nodes_makefuncs.o duckdb/third_party/libpg_query/src_backend_nodes_value.o duckdb/third_party/libpg_query/src_backend_parser_gram.o duckdb/third_party/libpg_query/src_backend_parser_parser.o duckdb/third_party/libpg_query/src_backend_parser_scan.o duckdb/third_party/libpg_query/src_backend_parser_scansup.o duckdb/third_party/libpg_query/src_common_keywords.o duckdb/third_party/mbedtls/library/aes.o duckdb/third_party/mbedtls/library/aria.o duckdb/third_party/mbedtls/library/asn1parse.o duckdb/third_party/mbedtls/library/base64.o duckdb/third_party/mbedtls/library/bignum.o duckdb/third_party/mbedtls/library/camellia.o duckdb/third_party/mbedtls/library/cipher.o duckdb/third_party/mbedtls/library/cipher_wrap.o duckdb/third_party/mbedtls/library/constant_time.o duckdb/third_party/mbedtls/library/entropy.o duckdb/third_party/mbedtls/library/entropy_poll.o duckdb/third_party/mbedtls/library/gcm.o duckdb/third_party/mbedtls/library/md.o duckdb/third_party/mbedtls/library/oid.o duckdb/third_party/mbedtls/library/pem.o duckdb/third_party/mbedtls/library/pk.o duckdb/third_party/mbedtls/library/pk_wrap.o duckdb/third_party/mbedtls/library/pkparse.o duckdb/third_party/mbedtls/library/platform_util.o duckdb/third_party/mbedtls/library/rsa.o duckdb/third_party/mbedtls/library/rsa_alt_helpers.o duckdb/third_party/mbedtls/library/sha1.o duckdb/third_party/mbedtls/library/sha256.o duckdb/third_party/mbedtls/library/sha512.o duckdb/third_party/mbedtls/mbedtls_wrapper.o duckdb/extension/parquet/column_reader.o duckdb/extension/parquet/column_writer.o duckdb/extension/parquet/parquet_crypto.o duckdb/extension/parquet/parquet_extension.o duckdb/extension/parquet/parquet_metadata.o duckdb/extension/parquet/parquet_reader.o duckdb/extension/parquet/parquet_statistics.o duckdb/extension/parquet/parquet_timestamp.o duckdb/extension/parquet/parquet_writer.o duckdb/extension/parquet/serialize_parquet.o duckdb/extension/parquet/zstd_file_system.o duckdb/third_party/parquet/parquet_constants.o duckdb/third_party/parquet/parquet_types.o duckdb/third_party/thrift/thrift/protocol/TProtocol.o duckdb/third_party/thrift/thrift/transport/TTransportException.o duckdb/third_party/thrift/thrift/transport/TBufferTransports.o duckdb/third_party/snappy/snappy.o duckdb/third_party/snappy/snappy-sinksource.o duckdb/third_party/zstd/decompress/zstd_ddict.o duckdb/third_party/zstd/decompress/huf_decompress.o duckdb/third_party/zstd/decompress/zstd_decompress.o duckdb/third_party/zstd/decompress/zstd_decompress_block.o duckdb/third_party/zstd/common/entropy_common.o duckdb/third_party/zstd/common/fse_decompress.o duckdb/third_party/zstd/common/zstd_common.o duckdb/third_party/zstd/common/error_private.o duckdb/third_party/zstd/common/xxhash.o duckdb/third_party/zstd/compress/fse_compress.o duckdb/third_party/zstd/compress/hist.o duckdb/third_party/zstd/compress/huf_compress.o duckdb/third_party/zstd/compress/zstd_compress.o duckdb/third_party/zstd/compress/zstd_compress_literals.o duckdb/third_party/zstd/compress/zstd_compress_sequences.o duckdb/third_party/zstd/compress/zstd_compress_superblock.o duckdb/third_party/zstd/compress/zstd_double_fast.o duckdb/third_party/zstd/compress/zstd_fast.o duckdb/third_party/zstd/compress/zstd_lazy.o duckdb/third_party/zstd/compress/zstd_ldm.o duckdb/third_party/zstd/compress/zstd_opt.o +SOURCES=duckdb/ub_src_catalog.o duckdb/ub_src_catalog_catalog_entry.o duckdb/ub_src_catalog_catalog_entry_dependency.o duckdb/ub_src_catalog_default.o duckdb/ub_src_common_adbc.o duckdb/ub_src_common_adbc_nanoarrow.o duckdb/ub_src_common.o duckdb/ub_src_common_arrow_appender.o duckdb/ub_src_common_arrow.o duckdb/ub_src_common_crypto.o duckdb/ub_src_common_enums.o duckdb/ub_src_common_exception.o duckdb/ub_src_common_operator.o duckdb/ub_src_common_progress_bar.o duckdb/ub_src_common_row_operations.o duckdb/ub_src_common_serializer.o duckdb/ub_src_common_sort.o duckdb/ub_src_common_types.o duckdb/ub_src_common_types_column.o duckdb/ub_src_common_types_row.o duckdb/ub_src_common_value_operations.o duckdb/src/common/vector_operations/boolean_operators.o duckdb/src/common/vector_operations/comparison_operators.o duckdb/src/common/vector_operations/generators.o duckdb/src/common/vector_operations/is_distinct_from.o duckdb/src/common/vector_operations/null_operations.o duckdb/src/common/vector_operations/numeric_inplace_operators.o duckdb/src/common/vector_operations/vector_cast.o duckdb/src/common/vector_operations/vector_copy.o duckdb/src/common/vector_operations/vector_hash.o duckdb/src/common/vector_operations/vector_storage.o duckdb/ub_src_core_functions_aggregate_algebraic.o duckdb/ub_src_core_functions_aggregate_distributive.o duckdb/ub_src_core_functions_aggregate_holistic.o duckdb/ub_src_core_functions_aggregate_nested.o duckdb/ub_src_core_functions_aggregate_regression.o duckdb/ub_src_core_functions.o duckdb/ub_src_core_functions_scalar_array.o duckdb/ub_src_core_functions_scalar_bit.o duckdb/ub_src_core_functions_scalar_blob.o duckdb/ub_src_core_functions_scalar_date.o duckdb/ub_src_core_functions_scalar_debug.o duckdb/ub_src_core_functions_scalar_enum.o duckdb/ub_src_core_functions_scalar_generic.o duckdb/ub_src_core_functions_scalar_list.o duckdb/ub_src_core_functions_scalar_map.o duckdb/ub_src_core_functions_scalar_math.o duckdb/ub_src_core_functions_scalar_operators.o duckdb/ub_src_core_functions_scalar_random.o duckdb/ub_src_core_functions_scalar_secret.o duckdb/ub_src_core_functions_scalar_string.o duckdb/ub_src_core_functions_scalar_struct.o duckdb/ub_src_core_functions_scalar_union.o duckdb/ub_src_execution.o duckdb/ub_src_execution_expression_executor.o duckdb/ub_src_execution_index_art.o duckdb/ub_src_execution_index.o duckdb/ub_src_execution_nested_loop_join.o duckdb/ub_src_execution_operator_aggregate.o duckdb/ub_src_execution_operator_csv_scanner_buffer_manager.o duckdb/ub_src_execution_operator_csv_scanner_scanner.o duckdb/ub_src_execution_operator_csv_scanner_sniffer.o duckdb/ub_src_execution_operator_csv_scanner_state_machine.o duckdb/ub_src_execution_operator_csv_scanner_table_function.o duckdb/ub_src_execution_operator_csv_scanner_util.o duckdb/ub_src_execution_operator_filter.o duckdb/ub_src_execution_operator_helper.o duckdb/ub_src_execution_operator_join.o duckdb/ub_src_execution_operator_order.o duckdb/ub_src_execution_operator_persistent.o duckdb/ub_src_execution_operator_projection.o duckdb/ub_src_execution_operator_scan.o duckdb/ub_src_execution_operator_schema.o duckdb/ub_src_execution_operator_set.o duckdb/ub_src_execution_physical_plan.o duckdb/ub_src_function_aggregate_distributive.o duckdb/ub_src_function_aggregate.o duckdb/ub_src_function.o duckdb/ub_src_function_cast.o duckdb/ub_src_function_cast_union.o duckdb/ub_src_function_pragma.o duckdb/ub_src_function_scalar_compressed_materialization.o duckdb/ub_src_function_scalar.o duckdb/ub_src_function_scalar_generic.o duckdb/ub_src_function_scalar_list.o duckdb/ub_src_function_scalar_operators.o duckdb/ub_src_function_scalar_sequence.o duckdb/ub_src_function_scalar_string.o duckdb/ub_src_function_scalar_string_regexp.o duckdb/ub_src_function_scalar_struct.o duckdb/ub_src_function_scalar_system.o duckdb/ub_src_function_table_arrow.o duckdb/ub_src_function_table.o duckdb/ub_src_function_table_system.o duckdb/ub_src_function_table_version.o duckdb/ub_src_main.o duckdb/ub_src_main_buffered_data.o duckdb/ub_src_main_capi.o duckdb/ub_src_main_capi_cast.o duckdb/ub_src_main_chunk_scan_state.o duckdb/ub_src_main_extension.o duckdb/ub_src_main_relation.o duckdb/ub_src_main_secret.o duckdb/ub_src_main_settings.o duckdb/ub_src_optimizer.o duckdb/ub_src_optimizer_compressed_materialization.o duckdb/ub_src_optimizer_join_order.o duckdb/ub_src_optimizer_matcher.o duckdb/ub_src_optimizer_pullup.o duckdb/ub_src_optimizer_pushdown.o duckdb/ub_src_optimizer_rule.o duckdb/ub_src_optimizer_statistics_expression.o duckdb/ub_src_optimizer_statistics_operator.o duckdb/ub_src_parallel.o duckdb/ub_src_parser.o duckdb/ub_src_parser_constraints.o duckdb/ub_src_parser_expression.o duckdb/ub_src_parser_parsed_data.o duckdb/ub_src_parser_query_node.o duckdb/ub_src_parser_statement.o duckdb/ub_src_parser_tableref.o duckdb/ub_src_parser_transform_constraint.o duckdb/ub_src_parser_transform_expression.o duckdb/ub_src_parser_transform_helpers.o duckdb/ub_src_parser_transform_statement.o duckdb/ub_src_parser_transform_tableref.o duckdb/ub_src_planner.o duckdb/ub_src_planner_binder_expression.o duckdb/ub_src_planner_binder_query_node.o duckdb/ub_src_planner_binder_statement.o duckdb/ub_src_planner_binder_tableref.o duckdb/ub_src_planner_expression.o duckdb/ub_src_planner_expression_binder.o duckdb/ub_src_planner_filter.o duckdb/ub_src_planner_operator.o duckdb/ub_src_planner_subquery.o duckdb/ub_src_storage.o duckdb/ub_src_storage_buffer.o duckdb/ub_src_storage_checkpoint.o duckdb/ub_src_storage_compression_alp.o duckdb/ub_src_storage_compression.o duckdb/ub_src_storage_compression_chimp.o duckdb/ub_src_storage_metadata.o duckdb/ub_src_storage_serialization.o duckdb/ub_src_storage_statistics.o duckdb/ub_src_storage_table.o duckdb/ub_src_transaction.o duckdb/src/verification/copied_statement_verifier.o duckdb/src/verification/deserialized_statement_verifier.o duckdb/src/verification/external_statement_verifier.o duckdb/src/verification/fetch_row_verifier.o duckdb/src/verification/no_operator_caching_verifier.o duckdb/src/verification/parsed_statement_verifier.o duckdb/src/verification/prepared_statement_verifier.o duckdb/src/verification/statement_verifier.o duckdb/src/verification/unoptimized_statement_verifier.o duckdb/third_party/fmt/format.o duckdb/third_party/fsst/fsst_avx512.o duckdb/third_party/fsst/libfsst.o duckdb/third_party/miniz/miniz.o duckdb/third_party/re2/re2/bitstate.o duckdb/third_party/re2/re2/compile.o duckdb/third_party/re2/re2/dfa.o duckdb/third_party/re2/re2/filtered_re2.o duckdb/third_party/re2/re2/mimics_pcre.o duckdb/third_party/re2/re2/nfa.o duckdb/third_party/re2/re2/onepass.o duckdb/third_party/re2/re2/parse.o duckdb/third_party/re2/re2/perl_groups.o duckdb/third_party/re2/re2/prefilter.o duckdb/third_party/re2/re2/prefilter_tree.o duckdb/third_party/re2/re2/prog.o duckdb/third_party/re2/re2/re2.o duckdb/third_party/re2/re2/regexp.o duckdb/third_party/re2/re2/set.o duckdb/third_party/re2/re2/simplify.o duckdb/third_party/re2/re2/stringpiece.o duckdb/third_party/re2/re2/tostring.o duckdb/third_party/re2/re2/unicode_casefold.o duckdb/third_party/re2/re2/unicode_groups.o duckdb/third_party/re2/util/rune.o duckdb/third_party/re2/util/strutil.o duckdb/third_party/hyperloglog/hyperloglog.o duckdb/third_party/hyperloglog/sds.o duckdb/third_party/skiplist/SkipList.o duckdb/third_party/fastpforlib/bitpacking.o duckdb/third_party/utf8proc/utf8proc.o duckdb/third_party/utf8proc/utf8proc_wrapper.o duckdb/third_party/libpg_query/pg_functions.o duckdb/third_party/libpg_query/postgres_parser.o duckdb/third_party/libpg_query/src_backend_nodes_list.o duckdb/third_party/libpg_query/src_backend_nodes_makefuncs.o duckdb/third_party/libpg_query/src_backend_nodes_value.o duckdb/third_party/libpg_query/src_backend_parser_gram.o duckdb/third_party/libpg_query/src_backend_parser_parser.o duckdb/third_party/libpg_query/src_backend_parser_scan.o duckdb/third_party/libpg_query/src_backend_parser_scansup.o duckdb/third_party/libpg_query/src_common_keywords.o duckdb/third_party/mbedtls/library/aes.o duckdb/third_party/mbedtls/library/aria.o duckdb/third_party/mbedtls/library/asn1parse.o duckdb/third_party/mbedtls/library/base64.o duckdb/third_party/mbedtls/library/bignum.o duckdb/third_party/mbedtls/library/camellia.o duckdb/third_party/mbedtls/library/cipher.o duckdb/third_party/mbedtls/library/cipher_wrap.o duckdb/third_party/mbedtls/library/constant_time.o duckdb/third_party/mbedtls/library/entropy.o duckdb/third_party/mbedtls/library/entropy_poll.o duckdb/third_party/mbedtls/library/gcm.o duckdb/third_party/mbedtls/library/md.o duckdb/third_party/mbedtls/library/oid.o duckdb/third_party/mbedtls/library/pem.o duckdb/third_party/mbedtls/library/pk.o duckdb/third_party/mbedtls/library/pk_wrap.o duckdb/third_party/mbedtls/library/pkparse.o duckdb/third_party/mbedtls/library/platform_util.o duckdb/third_party/mbedtls/library/rsa.o duckdb/third_party/mbedtls/library/rsa_alt_helpers.o duckdb/third_party/mbedtls/library/sha1.o duckdb/third_party/mbedtls/library/sha256.o duckdb/third_party/mbedtls/library/sha512.o duckdb/third_party/mbedtls/mbedtls_wrapper.o duckdb/extension/parquet/column_reader.o duckdb/extension/parquet/column_writer.o duckdb/extension/parquet/parquet_crypto.o duckdb/extension/parquet/parquet_extension.o duckdb/extension/parquet/parquet_metadata.o duckdb/extension/parquet/parquet_reader.o duckdb/extension/parquet/parquet_statistics.o duckdb/extension/parquet/parquet_timestamp.o duckdb/extension/parquet/parquet_writer.o duckdb/extension/parquet/serialize_parquet.o duckdb/extension/parquet/zstd_file_system.o duckdb/third_party/parquet/parquet_constants.o duckdb/third_party/parquet/parquet_types.o duckdb/third_party/thrift/thrift/protocol/TProtocol.o duckdb/third_party/thrift/thrift/transport/TTransportException.o duckdb/third_party/thrift/thrift/transport/TBufferTransports.o duckdb/third_party/snappy/snappy.o duckdb/third_party/snappy/snappy-sinksource.o duckdb/third_party/zstd/decompress/zstd_ddict.o duckdb/third_party/zstd/decompress/huf_decompress.o duckdb/third_party/zstd/decompress/zstd_decompress.o duckdb/third_party/zstd/decompress/zstd_decompress_block.o duckdb/third_party/zstd/common/entropy_common.o duckdb/third_party/zstd/common/fse_decompress.o duckdb/third_party/zstd/common/zstd_common.o duckdb/third_party/zstd/common/error_private.o duckdb/third_party/zstd/common/xxhash.o duckdb/third_party/zstd/compress/fse_compress.o duckdb/third_party/zstd/compress/hist.o duckdb/third_party/zstd/compress/huf_compress.o duckdb/third_party/zstd/compress/zstd_compress.o duckdb/third_party/zstd/compress/zstd_compress_literals.o duckdb/third_party/zstd/compress/zstd_compress_sequences.o duckdb/third_party/zstd/compress/zstd_compress_superblock.o duckdb/third_party/zstd/compress/zstd_double_fast.o duckdb/third_party/zstd/compress/zstd_fast.o duckdb/third_party/zstd/compress/zstd_lazy.o duckdb/third_party/zstd/compress/zstd_ldm.o duckdb/third_party/zstd/compress/zstd_opt.o From f72a289c452b79714f4341e7c395e71749ad9f8b Mon Sep 17 00:00:00 2001 From: Tom Ebergen Date: Mon, 26 Feb 2024 15:24:45 +0100 Subject: [PATCH 3/5] change is finished to is finished or blocked --- src/statement.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/statement.cpp b/src/statement.cpp index e0d87d676..effa001ee 100644 --- a/src/statement.cpp +++ b/src/statement.cpp @@ -337,7 +337,7 @@ bool FetchArrowChunk(ChunkScanState &scan_state, ClientProperties options, Appen do { execution_result = pending_query->ExecuteTask(); R_CheckUserInterrupt(); - } while (!PendingQueryResult::IsFinished(execution_result)); + } while (!PendingQueryResult::IsFinishedOrBlocked(execution_result)); if (execution_result == PendingExecutionResult::EXECUTION_ERROR) { cpp11::stop("rapi_execute: Failed to run query\nError: %s", pending_query->GetError().c_str()); } From 7576cb329da7e660b70671a40752435cb42350c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Tue, 27 Feb 2024 11:39:28 +0100 Subject: [PATCH 4/5] chore: Update vendored sources to duckdb/duckdb@583508495b61d44a246d13eb106a309f3982af14 Merge pull request duckdb/duckdb#10650 from hannes/noprintf Merge pull request duckdb/duckdb#10658 from hannes/csvpathlength Merge pull request duckdb/duckdb#10612 from samansmink/delay-secret-storage-directory-initialization Merge pull request duckdb/duckdb#10611 from Mytherin/currenttimealias Merge pull request duckdb/duckdb#10616 from hannes/jdbctestchanged Merge pull request duckdb/duckdb#10607 from carlopi/secret_folder Merge pull request duckdb/duckdb#10603 from carlopi/extensions_patches Merge pull request duckdb/duckdb#10606 from Mytherin/duckdbmemoryinit Merge pull request duckdb/duckdb#10604 from Mytherin/aggregatelimit Merge pull request duckdb/duckdb#10592 from carlopi/error_message_lock Merge pull request duckdb/duckdb#10600 from samansmink/secret-manager-fixes-2 Merge pull request duckdb/duckdb#10589 from pdet/csv_result_buffer_hold Merge pull request duckdb/duckdb#10605 from Tishj/julia_remove_dataframe Merge pull request duckdb/duckdb#10596 from pdet/null_padding_projection_finalize Merge pull request duckdb/duckdb#10590 from Tmonster/investigate-cardinality-estimates-parquet Merge pull request duckdb/duckdb#10598 from szarnyasg/add-support-options Merge pull request duckdb/duckdb#10579 from Tishj/unittest_require_flag Merge pull request duckdb/duckdb#10202 from Tishj/sorted_aggregate_function_small_vector_size Merge pull request duckdb/duckdb#10553 from Tmonster/fuzzer_column_binding_issues Merge pull request duckdb/duckdb#10513 from Maxxen/array-tupledata-fix Merge pull request duckdb/duckdb#10584 from Mause/datetime-delta Merge pull request duckdb/duckdb#10582 from carlopi/fix_extension_distribution Merge pull request duckdb/duckdb#10580 from Maxxen/bump-spatial Merge pull request duckdb/duckdb#10532 from Tishj/deduplicate_columns Merge pull request duckdb/duckdb#10573 from carlopi/fixthreads Merge pull request duckdb/duckdb#10572 from carlopi/noexit Merge pull request duckdb/duckdb#10571 from Tishj/time_cast_heap_overflow Merge pull request duckdb/duckdb#10569 from Tishj/fix_export_of_types Merge pull request duckdb/duckdb#10568 from Tishj/pytest_does_not_throw_httpexception Merge pull request duckdb/duckdb#10567 from Tishj/require_no_alternative_verify Merge pull request duckdb/duckdb#10565 from Tishj/arrow_support_time_tz Merge pull request duckdb/duckdb#10563 from Tishj/python_exception_bug Merge pull request duckdb/duckdb#10559 from Mytherin/issue10528 Merge pull request duckdb/duckdb#10548 from ywelsch/yw/duckdb-only-server-threads Merge pull request duckdb/duckdb#10564 from carlopi/fix01init Merge pull request duckdb/duckdb#10560 from carlopi/rowsorts Merge pull request duckdb/duckdb#10558 from pdet/bug_9952 Merge pull request duckdb/duckdb#10555 from carlopi/refactorcommit Merge pull request duckdb/duckdb#10557 from pdet/reset-quoted-proj Merge pull request duckdb/duckdb#10504 from Mytherin/queryid Merge pull request duckdb/duckdb#10487 from felipecrv/upper_bound Merge pull request duckdb/duckdb#10552 from pdet/fix_glob_partition_test Merge pull request duckdb/duckdb#10551 from pdet/segfault-fix Merge pull request duckdb/duckdb#10554 from Mytherin/benchmarkerrors Merge pull request duckdb/duckdb#10485 from Tmonster/list_zip_no_args Merge pull request duckdb/duckdb#10536 from motherduckdb/flo/add-order-to-tests Merge pull request duckdb/duckdb#10549 from lnkuiper/radix_ht_reservation Merge pull request duckdb/duckdb#10525 from Tishj/parallel_checkpoint_error_race Merge pull request duckdb/duckdb#10541 from Mytherin/arrayextractunnamedstruct Merge pull request duckdb/duckdb#10538 from Maxxen/copy-file-path-fix Merge pull request duckdb/duckdb#10544 from hawkfish/tz2024a Merge pull request duckdb/duckdb#10542 from carlopi/fixes Merge pull request duckdb/duckdb#10408 from Mause/feature/jdbc-set-date Merge pull request duckdb/duckdb#10531 from ywelsch/yw/client-context-in-attach Merge pull request duckdb/duckdb#10537 from Mytherin/structcastoutoforder Merge pull request duckdb/duckdb#10534 from Mytherin/applypatchesfix Merge pull request duckdb/duckdb#10488 from Tishj/python_editable_build Merge pull request duckdb/duckdb#10527 from Mytherin/linenoiseenterbehavior Merge pull request duckdb/duckdb#10530 from pdet/bug_null_csv_ignore Merge pull request duckdb/duckdb#10533 from Tishj/python_fixes_2024_2_8 Merge pull request duckdb/duckdb#10526 from lnkuiper/null_to_json_cast Merge pull request duckdb/duckdb#10529 from philippmd/philippmd/errno-ioexception Merge pull request duckdb/duckdb#10503 from lnkuiper/temporary_memory_manager_tweaks Merge pull request duckdb/duckdb#10500 from Mytherin/issue910 Merge pull request duckdb/duckdb#10357 from kryonix/main Merge pull request duckdb/duckdb#10521 from Mause/patch-4 Merge pull request duckdb/duckdb#10518 from motherduckdb/secrets-changes Merge pull request duckdb/duckdb#10514 from Mytherin/exceptionstacktrace Merge pull request duckdb/duckdb#10512 from Tishj/executor_race_condition Merge pull request duckdb/duckdb#10510 from Tishj/gcc_is_stupid Merge pull request duckdb/duckdb#10509 from pdet/csv_big_data Merge pull request duckdb/duckdb#10505 from Mytherin/issue10363 Merge pull request duckdb/duckdb#10507 from carlopi/bundle-libraries Merge pull request duckdb/duckdb#10459 from Maxxen/copy-tmp-ext Merge pull request duckdb/duckdb#10483 from Maxxen/array-bugfix Merge pull request duckdb/duckdb#10502 from Mytherin/autocompletefix Merge pull request duckdb/duckdb#10499 from Mytherin/issue1164 Merge pull request duckdb/duckdb#10498 from Mytherin/disablecompletionrendering Merge pull request duckdb/duckdb#10496 from Mytherin/memorymanagement Merge pull request duckdb/duckdb#10492 from SChakravorti21/fix-empty-normalized-version Merge pull request duckdb/duckdb#10484 from Flogex/extension-build-script Merge pull request duckdb/duckdb#10493 from Mytherin/altcommands Merge pull request duckdb/duckdb#10475 from rdavis120/main Merge pull request duckdb/duckdb#10480 from Mytherin/autocompletedisplay Merge pull request duckdb/duckdb#10473 from pdet/bug_10283 Merge pull request duckdb/duckdb#10479 from hannes/windowsarm Merge pull request duckdb/duckdb#10471 from Maxxen/custom-index-fork Merge pull request duckdb/duckdb#10474 from Mytherin/gzipfs Merge pull request duckdb/duckdb#10472 from pdet/bug_10273 Merge pull request duckdb/duckdb#10470 from Mytherin/clearcontinuationmarkers Merge pull request duckdb/duckdb#10467 from carlopi/fixes Merge pull request duckdb/duckdb#10465 from pdet/type_misdetection Merge pull request duckdb/duckdb#10462 from pdet/double_quoted_header Merge pull request duckdb/duckdb#10461 from Mytherin/linenoisecleanup Merge pull request duckdb/duckdb#10464 from Tishj/improve_julia_dev_readme --- .../extension/parquet/parquet_extension.cpp | 5 +- .../catalog_entry/type_catalog_entry.cpp | 22 +- .../catalog_entry/view_catalog_entry.cpp | 2 + src/duckdb/src/common/arrow/arrow_wrapper.cpp | 19 +- src/duckdb/src/common/enum_util.cpp | 103 +++-- src/duckdb/src/common/exception.cpp | 10 + src/duckdb/src/common/file_system.cpp | 2 +- src/duckdb/src/common/gzip_file_system.cpp | 36 +- src/duckdb/src/common/local_file_system.cpp | 41 +- src/duckdb/src/common/radix_partitioning.cpp | 2 +- .../src/common/row_operations/row_gather.cpp | 3 +- .../common/row_operations/row_heap_gather.cpp | 50 +-- .../row_operations/row_heap_scatter.cpp | 190 ++++----- .../src/common/row_operations/row_matcher.cpp | 2 +- .../src/common/row_operations/row_scatter.cpp | 3 +- src/duckdb/src/common/sort/radix_sort.cpp | 3 +- src/duckdb/src/common/sort/sort_state.cpp | 8 +- src/duckdb/src/common/sort/sorted_block.cpp | 8 +- src/duckdb/src/common/string_util.cpp | 67 ++++ src/duckdb/src/common/types.cpp | 67 +++- .../types/column/column_data_allocator.cpp | 2 +- .../common/types/row/row_data_collection.cpp | 2 +- .../types/row/row_data_collection_scanner.cpp | 4 +- .../common/types/row/tuple_data_allocator.cpp | 2 +- .../types/row/tuple_data_collection.cpp | 43 ++- .../types/row/tuple_data_scatter_gather.cpp | 361 ++++++------------ src/duckdb/src/common/types/time.cpp | 4 + src/duckdb/src/common/types/vector.cpp | 12 +- src/duckdb/src/common/types/vector_cache.cpp | 6 +- .../src/execution/aggregate_hashtable.cpp | 2 +- .../src/execution/index/fixed_size_buffer.cpp | 6 +- src/duckdb/src/execution/join_hashtable.cpp | 11 +- .../aggregate/physical_hash_aggregate.cpp | 13 +- .../physical_ungrouped_aggregate.cpp | 9 +- .../csv_scanner/buffer_manager/csv_buffer.cpp | 19 +- .../buffer_manager/csv_buffer_manager.cpp | 36 +- .../scanner/string_value_scanner.cpp | 112 ++++-- .../table_function/csv_file_scanner.cpp | 9 +- .../table_function/global_csv_state.cpp | 11 +- .../operator/csv_scanner/util/csv_error.cpp | 1 + .../join/perfect_hash_join_executor.cpp | 2 +- .../operator/join/physical_hash_join.cpp | 30 +- .../persistent/physical_copy_to_file.cpp | 11 +- .../scan/physical_column_data_scan.cpp | 13 +- .../execution/operator/set/physical_cte.cpp | 134 +++---- .../operator/set/physical_recursive_cte.cpp | 3 + .../physical_plan/plan_copy_to_file.cpp | 4 +- .../src/execution/physical_plan/plan_cte.cpp | 8 +- .../physical_plan/plan_recursive_cte.cpp | 4 +- .../execution/radix_partitioned_hashtable.cpp | 87 +++-- .../aggregate/sorted_aggregate_function.cpp | 2 +- src/duckdb/src/function/cast/list_casts.cpp | 98 +++-- src/duckdb/src/function/cast/struct_cast.cpp | 45 ++- .../src/function/scalar/list/list_extract.cpp | 3 +- .../src/function/scalar/list/list_zip.cpp | 11 +- .../function/scalar/struct/struct_extract.cpp | 79 +++- src/duckdb/src/function/table/arrow.cpp | 26 +- .../src/function/table/arrow_conversion.cpp | 31 +- .../pragma_detailed_profiling_output.cpp | 173 --------- .../table/pragma_last_profiling_output.cpp | 101 ----- src/duckdb/src/function/table/range.cpp | 6 +- src/duckdb/src/function/table/read_csv.cpp | 9 +- .../function/table/system/duckdb_columns.cpp | 2 +- .../function/table/system/duckdb_memory.cpp | 63 +++ .../function/table/system/duckdb_secrets.cpp | 8 +- .../table/system/pragma_table_info.cpp | 2 +- .../function/table/system/test_all_types.cpp | 2 +- .../src/function/table/system_functions.cpp | 3 +- .../function/table/version/pragma_version.cpp | 4 +- .../catalog_entry/view_catalog_entry.hpp | 2 + .../src/include/duckdb/common/enum_util.hpp | 16 +- .../duckdb/common/enums/memory_tag.hpp | 32 ++ .../src/include/duckdb/common/exception.hpp | 6 + .../src/include/duckdb/common/helper.hpp | 2 +- .../src/include/duckdb/common/http_state.hpp | 3 +- .../common/operator/integer_cast_operator.hpp | 3 + .../common/row_operations/row_operations.hpp | 26 +- .../src/include/duckdb/common/string_util.hpp | 5 + .../src/include/duckdb/common/types.hpp | 39 ++ .../common/types/row/row_data_collection.hpp | 4 +- .../types/row/tuple_data_collection.hpp | 21 +- .../common/types/row/tuple_data_states.hpp | 5 +- .../duckdb/common/types/validity_mask.hpp | 4 +- .../include/duckdb/common/types/vector.hpp | 5 - .../src/include/duckdb/execution/executor.hpp | 3 - .../duckdb/execution/index/art/art.hpp | 10 +- .../duckdb/execution/index/index_type.hpp | 28 +- .../duckdb/execution/join_hashtable.hpp | 2 +- .../operator/csv_scanner/base_scanner.hpp | 2 +- .../operator/csv_scanner/csv_buffer.hpp | 4 +- .../csv_scanner/csv_buffer_manager.hpp | 7 +- .../operator/csv_scanner/csv_file_scanner.hpp | 2 +- .../operator/csv_scanner/global_csv_state.hpp | 1 + .../csv_scanner/string_value_scanner.hpp | 32 +- .../execution/operator/set/physical_cte.hpp | 29 +- .../operator/set/physical_recursive_cte.hpp | 4 + .../execution/physical_plan_generator.hpp | 2 +- .../duckdb/function/cast/bound_cast_data.hpp | 11 +- .../function/scalar/nested_functions.hpp | 5 +- .../include/duckdb/function/table/arrow.hpp | 2 - .../function/table/system_functions.hpp | 12 +- .../include/duckdb/main/attached_database.hpp | 4 +- .../include/duckdb/main/client_context.hpp | 11 +- .../duckdb/main/client_context_state.hpp | 37 ++ .../src/include/duckdb/main/client_data.hpp | 3 - src/duckdb/src/include/duckdb/main/config.hpp | 6 +- .../src/include/duckdb/main/database.hpp | 4 +- .../include/duckdb/main/query_profiler.hpp | 32 -- .../src/include/duckdb/main/query_result.hpp | 4 + .../src/include/duckdb/main/secret/secret.hpp | 9 + .../duckdb/main/secret/secret_manager.hpp | 49 ++- .../duckdb/main/secret/secret_storage.hpp | 20 +- .../src/include/duckdb/main/settings.hpp | 9 - .../duckdb/parallel/task_scheduler.hpp | 7 +- .../parser/parsed_data/create_view_info.hpp | 2 + .../src/include/duckdb/planner/binder.hpp | 2 + .../planner/operator/logical_cteref.hpp | 4 +- .../operator/logical_recursive_cte.hpp | 2 + .../subquery/flatten_dependent_join.hpp | 3 + .../planner/subquery/rewrite_cte_scan.hpp | 29 ++ .../duckdb/storage/buffer/block_handle.hpp | 14 +- .../duckdb/storage/buffer/buffer_pool.hpp | 6 +- .../buffer/temporary_file_information.hpp | 7 + .../include/duckdb/storage/buffer_manager.hpp | 8 +- .../storage/standard_buffer_manager.hpp | 26 +- .../duckdb/storage/storage_extension.hpp | 5 +- .../storage/temporary_memory_manager.hpp | 6 +- .../transaction/transaction_context.hpp | 8 +- src/duckdb/src/main/attached_database.cpp | 6 +- src/duckdb/src/main/client_context.cpp | 82 ++-- src/duckdb/src/main/client_data.cpp | 1 - src/duckdb/src/main/config.cpp | 9 - src/duckdb/src/main/database.cpp | 13 +- src/duckdb/src/main/database_manager.cpp | 2 +- .../src/main/extension/extension_load.cpp | 2 + src/duckdb/src/main/query_result.cpp | 24 ++ src/duckdb/src/main/secret/secret_manager.cpp | 55 +-- src/duckdb/src/main/secret/secret_storage.cpp | 56 ++- src/duckdb/src/main/settings/settings.cpp | 56 +-- .../statistics/operator/propagate_filter.cpp | 3 +- src/duckdb/src/parallel/executor.cpp | 49 ++- src/duckdb/src/parallel/task_scheduler.cpp | 26 +- src/duckdb/src/parser/column_list.cpp | 2 +- src/duckdb/src/planner/bind_context.cpp | 2 +- .../expression/bind_columnref_expression.cpp | 14 +- .../expression/bind_unnest_expression.cpp | 26 +- .../binder/query_node/bind_cte_node.cpp | 5 +- .../query_node/bind_recursive_cte_node.cpp | 3 + .../binder/query_node/plan_cte_node.cpp | 4 +- .../query_node/plan_recursive_cte_node.cpp | 4 +- .../planner/binder/query_node/plan_setop.cpp | 4 +- .../binder/query_node/plan_subquery.cpp | 10 + .../planner/binder/statement/bind_copy.cpp | 31 +- .../planner/binder/statement/bind_create.cpp | 25 +- .../binder/tableref/bind_basetableref.cpp | 20 +- .../planner/binder/tableref/plan_cteref.cpp | 10 +- .../src/planner/expression_iterator.cpp | 1 + .../subquery/flatten_dependent_join.cpp | 99 ++++- .../src/planner/subquery/rewrite_cte_scan.cpp | 36 ++ .../src/storage/buffer/block_handle.cpp | 22 +- .../src/storage/buffer/block_manager.cpp | 2 +- src/duckdb/src/storage/buffer/buffer_pool.cpp | 14 +- .../buffer/buffer_pool_reservation.cpp | 7 +- src/duckdb/src/storage/buffer_manager.cpp | 4 +- .../write_overflow_strings_to_disk.cpp | 2 +- .../compression/string_uncompressed.cpp | 4 +- .../compression/validity_uncompressed.cpp | 6 +- .../src/storage/metadata/metadata_manager.cpp | 4 +- .../serialization/serialize_create_info.cpp | 2 + .../src/storage/standard_buffer_manager.cpp | 76 ++-- .../src/storage/table/column_segment.cpp | 4 +- .../storage/table/row_group_collection.cpp | 31 +- src/duckdb/src/storage/table_index_list.cpp | 8 +- .../src/storage/temporary_memory_manager.cpp | 8 +- src/duckdb/src/storage/wal_replay.cpp | 10 +- src/duckdb/src/transaction/commit_state.cpp | 106 ++--- .../src/transaction/transaction_context.cpp | 19 + .../fast_float/fast_float/fast_float.h | 2 +- src/duckdb/ub_src_function_table.cpp | 4 - src/duckdb/ub_src_function_table_system.cpp | 2 + src/duckdb/ub_src_planner_subquery.cpp | 2 + 181 files changed, 2277 insertions(+), 1660 deletions(-) delete mode 100644 src/duckdb/src/function/table/pragma_detailed_profiling_output.cpp delete mode 100644 src/duckdb/src/function/table/pragma_last_profiling_output.cpp create mode 100644 src/duckdb/src/function/table/system/duckdb_memory.cpp create mode 100644 src/duckdb/src/include/duckdb/common/enums/memory_tag.hpp create mode 100644 src/duckdb/src/include/duckdb/main/client_context_state.hpp create mode 100644 src/duckdb/src/include/duckdb/planner/subquery/rewrite_cte_scan.hpp create mode 100644 src/duckdb/src/planner/subquery/rewrite_cte_scan.cpp diff --git a/src/duckdb/extension/parquet/parquet_extension.cpp b/src/duckdb/extension/parquet/parquet_extension.cpp index 57906656e..7a4f97a53 100644 --- a/src/duckdb/extension/parquet/parquet_extension.cpp +++ b/src/duckdb/extension/parquet/parquet_extension.cpp @@ -628,7 +628,10 @@ class ParquetScanFunction { static idx_t ParquetScanMaxThreads(ClientContext &context, const FunctionData *bind_data) { auto &data = bind_data->Cast(); - return std::max(data.initial_file_row_groups, idx_t(1)) * data.files.size(); + if (data.files.size() > 1) { + return TaskScheduler::GetScheduler(context).NumberOfThreads(); + } + return MaxValue(data.initial_file_row_groups, (idx_t)1); } // This function looks for the next available row group. If not available, it will open files from bind_data.files diff --git a/src/duckdb/src/catalog/catalog_entry/type_catalog_entry.cpp b/src/duckdb/src/catalog/catalog_entry/type_catalog_entry.cpp index 53954feda..4e04ee26b 100644 --- a/src/duckdb/src/catalog/catalog_entry/type_catalog_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/type_catalog_entry.cpp @@ -34,14 +34,20 @@ unique_ptr TypeCatalogEntry::GetInfo() const { } string TypeCatalogEntry::ToSQL() const { - switch (user_type.id()) { - case (LogicalTypeId::ENUM): { - auto create_type_info = GetInfo(); - return create_type_info->ToString(); - } - default: - throw InternalException("Logical Type can't be used as a User Defined Type"); - } + std::stringstream ss; + ss << "CREATE TYPE "; + ss << KeywordHelper::WriteOptionallyQuoted(name); + ss << " AS "; + + auto user_type_copy = user_type; + + // Strip off the potential alias so ToString doesn't just output the alias + user_type_copy.SetAlias(""); + D_ASSERT(user_type_copy.GetAlias().empty()); + + ss << user_type_copy.ToString(); + ss << ";"; + return ss.str(); } } // namespace duckdb diff --git a/src/duckdb/src/catalog/catalog_entry/view_catalog_entry.cpp b/src/duckdb/src/catalog/catalog_entry/view_catalog_entry.cpp index 0459eb2c0..1670eee90 100644 --- a/src/duckdb/src/catalog/catalog_entry/view_catalog_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/view_catalog_entry.cpp @@ -14,6 +14,7 @@ void ViewCatalogEntry::Initialize(CreateViewInfo &info) { query = std::move(info.query); this->aliases = info.aliases; this->types = info.types; + this->names = info.names; this->temporary = info.temporary; this->sql = info.sql; this->internal = info.internal; @@ -32,6 +33,7 @@ unique_ptr ViewCatalogEntry::GetInfo() const { result->sql = sql; result->query = unique_ptr_cast(query->Copy()); result->aliases = aliases; + result->names = names; result->types = types; result->temporary = temporary; result->comment = comment; diff --git a/src/duckdb/src/common/arrow/arrow_wrapper.cpp b/src/duckdb/src/common/arrow/arrow_wrapper.cpp index be914f74d..d439d9907 100644 --- a/src/duckdb/src/common/arrow/arrow_wrapper.cpp +++ b/src/duckdb/src/common/arrow/arrow_wrapper.cpp @@ -66,10 +66,16 @@ int ResultArrowArrayStreamWrapper::MyStreamGetSchema(struct ArrowArrayStream *st if (!stream->release) { return -1; } + out->release = nullptr; auto my_stream = reinterpret_cast(stream->private_data); if (!my_stream->column_types.empty()) { - ArrowConverter::ToArrowSchema(out, my_stream->column_types, my_stream->column_names, - my_stream->result->client_properties); + try { + ArrowConverter::ToArrowSchema(out, my_stream->column_types, my_stream->column_names, + my_stream->result->client_properties); + } catch (std::runtime_error &e) { + my_stream->last_error = ErrorData(e); + return -1; + } return 0; } @@ -89,8 +95,13 @@ int ResultArrowArrayStreamWrapper::MyStreamGetSchema(struct ArrowArrayStream *st my_stream->column_types = result.types; my_stream->column_names = result.names; } - ArrowConverter::ToArrowSchema(out, my_stream->column_types, my_stream->column_names, - my_stream->result->client_properties); + try { + ArrowConverter::ToArrowSchema(out, my_stream->column_types, my_stream->column_names, + my_stream->result->client_properties); + } catch (std::runtime_error &e) { + my_stream->last_error = ErrorData(e); + return -1; + } return 0; } diff --git a/src/duckdb/src/common/enum_util.cpp b/src/duckdb/src/common/enum_util.cpp index 27b4364d4..70b4f7036 100644 --- a/src/duckdb/src/common/enum_util.cpp +++ b/src/duckdb/src/common/enum_util.cpp @@ -28,6 +28,7 @@ #include "duckdb/common/enums/join_type.hpp" #include "duckdb/common/enums/joinref_type.hpp" #include "duckdb/common/enums/logical_operator_type.hpp" +#include "duckdb/common/enums/memory_tag.hpp" #include "duckdb/common/enums/on_create_conflict.hpp" #include "duckdb/common/enums/on_entry_not_found.hpp" #include "duckdb/common/enums/operator_result_type.hpp" @@ -62,7 +63,6 @@ #include "duckdb/common/types/conflict_manager.hpp" #include "duckdb/common/types/hyperloglog.hpp" #include "duckdb/common/types/row/partitioned_tuple_data.hpp" -#include "duckdb/common/types/row/tuple_data_collection.hpp" #include "duckdb/common/types/row/tuple_data_states.hpp" #include "duckdb/common/types/timestamp.hpp" #include "duckdb/common/types/vector.hpp" @@ -3483,6 +3483,79 @@ MapInvalidReason EnumUtil::FromString(const char *value) { throw NotImplementedException(StringUtil::Format("Enum value: '%s' not implemented", value)); } +template<> +const char* EnumUtil::ToChars(MemoryTag value) { + switch(value) { + case MemoryTag::BASE_TABLE: + return "BASE_TABLE"; + case MemoryTag::HASH_TABLE: + return "HASH_TABLE"; + case MemoryTag::PARQUET_READER: + return "PARQUET_READER"; + case MemoryTag::CSV_READER: + return "CSV_READER"; + case MemoryTag::ORDER_BY: + return "ORDER_BY"; + case MemoryTag::ART_INDEX: + return "ART_INDEX"; + case MemoryTag::COLUMN_DATA: + return "COLUMN_DATA"; + case MemoryTag::METADATA: + return "METADATA"; + case MemoryTag::OVERFLOW_STRINGS: + return "OVERFLOW_STRINGS"; + case MemoryTag::IN_MEMORY_TABLE: + return "IN_MEMORY_TABLE"; + case MemoryTag::ALLOCATOR: + return "ALLOCATOR"; + case MemoryTag::EXTENSION: + return "EXTENSION"; + default: + throw NotImplementedException(StringUtil::Format("Enum value: '%d' not implemented", value)); + } +} + +template<> +MemoryTag EnumUtil::FromString(const char *value) { + if (StringUtil::Equals(value, "BASE_TABLE")) { + return MemoryTag::BASE_TABLE; + } + if (StringUtil::Equals(value, "HASH_TABLE")) { + return MemoryTag::HASH_TABLE; + } + if (StringUtil::Equals(value, "PARQUET_READER")) { + return MemoryTag::PARQUET_READER; + } + if (StringUtil::Equals(value, "CSV_READER")) { + return MemoryTag::CSV_READER; + } + if (StringUtil::Equals(value, "ORDER_BY")) { + return MemoryTag::ORDER_BY; + } + if (StringUtil::Equals(value, "ART_INDEX")) { + return MemoryTag::ART_INDEX; + } + if (StringUtil::Equals(value, "COLUMN_DATA")) { + return MemoryTag::COLUMN_DATA; + } + if (StringUtil::Equals(value, "METADATA")) { + return MemoryTag::METADATA; + } + if (StringUtil::Equals(value, "OVERFLOW_STRINGS")) { + return MemoryTag::OVERFLOW_STRINGS; + } + if (StringUtil::Equals(value, "IN_MEMORY_TABLE")) { + return MemoryTag::IN_MEMORY_TABLE; + } + if (StringUtil::Equals(value, "ALLOCATOR")) { + return MemoryTag::ALLOCATOR; + } + if (StringUtil::Equals(value, "EXTENSION")) { + return MemoryTag::EXTENSION; + } + throw NotImplementedException(StringUtil::Format("Enum value: '%s' not implemented", value)); +} + template<> const char* EnumUtil::ToChars(NType value) { switch(value) { @@ -6868,33 +6941,5 @@ WindowExcludeMode EnumUtil::FromString(const char *value) { throw NotImplementedException(StringUtil::Format("Enum value: '%s' not implemented", value)); } -template<> -const char* EnumUtil::ToChars(WithinCollection value) { - switch(value) { - case WithinCollection::NO: - return "NO"; - case WithinCollection::LIST: - return "LIST"; - case WithinCollection::ARRAY: - return "ARRAY"; - default: - throw NotImplementedException(StringUtil::Format("Enum value: '%d' not implemented", value)); - } -} - -template<> -WithinCollection EnumUtil::FromString(const char *value) { - if (StringUtil::Equals(value, "NO")) { - return WithinCollection::NO; - } - if (StringUtil::Equals(value, "LIST")) { - return WithinCollection::LIST; - } - if (StringUtil::Equals(value, "ARRAY")) { - return WithinCollection::ARRAY; - } - throw NotImplementedException(StringUtil::Format("Enum value: '%s' not implemented", value)); -} - } diff --git a/src/duckdb/src/common/exception.cpp b/src/duckdb/src/common/exception.cpp index ba742c0a9..8de09b847 100644 --- a/src/duckdb/src/common/exception.cpp +++ b/src/duckdb/src/common/exception.cpp @@ -32,7 +32,13 @@ string Exception::ToJSON(ExceptionType type, const string &message) { } string Exception::ToJSON(ExceptionType type, const string &message, const unordered_map &extra_info) { +#ifdef DUCKDB_DEBUG_STACKTRACE + auto extended_extra_info = extra_info; + extended_extra_info["stack_trace"] = Exception::GetStackTrace(); + return StringUtil::ToJSONMap(type, message, extended_extra_info); +#else return StringUtil::ToJSONMap(type, message, extra_info); +#endif } bool Exception::UncaughtException() { @@ -301,6 +307,10 @@ DependencyException::DependencyException(const string &msg) : Exception(Exceptio IOException::IOException(const string &msg) : Exception(ExceptionType::IO, msg) { } +IOException::IOException(const string &msg, const unordered_map &extra_info) + : Exception(ExceptionType::IO, msg, extra_info) { +} + MissingExtensionException::MissingExtensionException(const string &msg) : Exception(ExceptionType::MISSING_EXTENSION, msg) { } diff --git a/src/duckdb/src/common/file_system.cpp b/src/duckdb/src/common/file_system.cpp index f1a72e0b8..f2124165f 100644 --- a/src/duckdb/src/common/file_system.cpp +++ b/src/duckdb/src/common/file_system.cpp @@ -215,7 +215,7 @@ string FileSystem::GetWorkingDirectory() { string FileSystem::JoinPath(const string &a, const string &b) { // FIXME: sanitize paths - return a + PathSeparator(a) + b; + return a.empty() ? b : a + PathSeparator(a) + b; } string FileSystem::ConvertSeparators(const string &path) { diff --git a/src/duckdb/src/common/gzip_file_system.cpp b/src/duckdb/src/common/gzip_file_system.cpp index 51774bd8d..414bcbfda 100644 --- a/src/duckdb/src/common/gzip_file_system.cpp +++ b/src/duckdb/src/common/gzip_file_system.cpp @@ -68,7 +68,7 @@ struct MiniZStreamWrapper : public StreamWrapper { ~MiniZStreamWrapper() override; CompressedFile *file = nullptr; - duckdb_miniz::mz_stream *mz_stream_ptr = nullptr; + unique_ptr mz_stream_ptr; bool writing = false; duckdb_miniz::mz_ulong crc; idx_t total_size; @@ -98,8 +98,8 @@ MiniZStreamWrapper::~MiniZStreamWrapper() { void MiniZStreamWrapper::Initialize(CompressedFile &file, bool write) { Close(); this->file = &file; - mz_stream_ptr = new duckdb_miniz::mz_stream(); - memset(mz_stream_ptr, 0, sizeof(duckdb_miniz::mz_stream)); + mz_stream_ptr = make_uniq(); + memset(mz_stream_ptr.get(), 0, sizeof(duckdb_miniz::mz_stream)); this->writing = write; // TODO use custom alloc/free methods in miniz to throw exceptions on OOM @@ -111,7 +111,7 @@ void MiniZStreamWrapper::Initialize(CompressedFile &file, bool write) { MiniZStream::InitializeGZIPHeader(gzip_hdr); file.child_handle->Write(gzip_hdr, GZIP_HEADER_MINSIZE); - auto ret = mz_deflateInit2((duckdb_miniz::mz_streamp)mz_stream_ptr, duckdb_miniz::MZ_DEFAULT_LEVEL, MZ_DEFLATED, + auto ret = mz_deflateInit2(mz_stream_ptr.get(), duckdb_miniz::MZ_DEFAULT_LEVEL, MZ_DEFLATED, -MZ_DEFAULT_WINDOW_BITS, 1, 0); if (ret != duckdb_miniz::MZ_OK) { throw InternalException("Failed to initialize miniz"); @@ -135,7 +135,7 @@ void MiniZStreamWrapper::Initialize(CompressedFile &file, bool write) { } file.child_handle->Seek(data_start); // stream is now set to beginning of payload data - auto ret = duckdb_miniz::mz_inflateInit2((duckdb_miniz::mz_streamp)mz_stream_ptr, -MZ_DEFAULT_WINDOW_BITS); + auto ret = duckdb_miniz::mz_inflateInit2(mz_stream_ptr.get(), -MZ_DEFAULT_WINDOW_BITS); if (ret != duckdb_miniz::MZ_OK) { throw InternalException("Failed to initialize miniz"); } @@ -182,8 +182,8 @@ bool MiniZStreamWrapper::Read(StreamData &sd) { Close(); return true; } - duckdb_miniz::mz_inflateEnd(mz_stream_ptr); - auto sta = duckdb_miniz::mz_inflateInit2((duckdb_miniz::mz_streamp)mz_stream_ptr, -MZ_DEFAULT_WINDOW_BITS); + duckdb_miniz::mz_inflateEnd(mz_stream_ptr.get()); + auto sta = duckdb_miniz::mz_inflateInit2(mz_stream_ptr.get(), -MZ_DEFAULT_WINDOW_BITS); if (sta != duckdb_miniz::MZ_OK) { throw InternalException("Failed to initialize miniz"); } @@ -195,7 +195,7 @@ bool MiniZStreamWrapper::Read(StreamData &sd) { mz_stream_ptr->avail_in = (uint32_t)(sd.in_buff_end - sd.in_buff_start); mz_stream_ptr->next_out = data_ptr_cast(sd.out_buff_end); mz_stream_ptr->avail_out = (uint32_t)((sd.out_buff.get() + sd.out_buf_size) - sd.out_buff_end); - auto ret = duckdb_miniz::mz_inflate(mz_stream_ptr, duckdb_miniz::MZ_NO_FLUSH); + auto ret = duckdb_miniz::mz_inflate(mz_stream_ptr.get(), duckdb_miniz::MZ_NO_FLUSH); if (ret != duckdb_miniz::MZ_OK && ret != duckdb_miniz::MZ_STREAM_END) { throw IOException("Failed to decode gzip stream: %s", duckdb_miniz::mz_error(ret)); } @@ -228,7 +228,7 @@ void MiniZStreamWrapper::Write(CompressedFile &file, StreamData &sd, data_ptr_t mz_stream_ptr->next_out = sd.out_buff_start; mz_stream_ptr->avail_out = output_remaining; - auto res = mz_deflate(mz_stream_ptr, duckdb_miniz::MZ_NO_FLUSH); + auto res = mz_deflate(mz_stream_ptr.get(), duckdb_miniz::MZ_NO_FLUSH); if (res != duckdb_miniz::MZ_OK) { D_ASSERT(res != duckdb_miniz::MZ_STREAM_END); throw InternalException("Failed to compress GZIP block"); @@ -254,7 +254,7 @@ void MiniZStreamWrapper::FlushStream() { mz_stream_ptr->next_out = sd.out_buff_start; mz_stream_ptr->avail_out = output_remaining; - auto res = mz_deflate(mz_stream_ptr, duckdb_miniz::MZ_FINISH); + auto res = mz_deflate(mz_stream_ptr.get(), duckdb_miniz::MZ_FINISH); sd.out_buff_start += (output_remaining - mz_stream_ptr->avail_out); if (sd.out_buff_start > sd.out_buff.get()) { file->child_handle->Write(sd.out_buff.get(), sd.out_buff_start - sd.out_buff.get()); @@ -282,11 +282,10 @@ void MiniZStreamWrapper::Close() { MiniZStream::InitializeGZIPFooter(gzip_footer, crc, total_size); file->child_handle->Write(gzip_footer, MiniZStream::GZIP_FOOTER_SIZE); - duckdb_miniz::mz_deflateEnd(mz_stream_ptr); + duckdb_miniz::mz_deflateEnd(mz_stream_ptr.get()); } else { - duckdb_miniz::mz_inflateEnd(mz_stream_ptr); + duckdb_miniz::mz_inflateEnd(mz_stream_ptr.get()); } - delete mz_stream_ptr; mz_stream_ptr = nullptr; file = nullptr; } @@ -321,8 +320,8 @@ string GZipFileSystem::UncompressGZIPString(const string &in) { // decompress file auto body_ptr = in.data(); - auto mz_stream_ptr = new duckdb_miniz::mz_stream(); - memset(mz_stream_ptr, 0, sizeof(duckdb_miniz::mz_stream)); + auto mz_stream_ptr = make_uniq(); + memset(mz_stream_ptr.get(), 0, sizeof(duckdb_miniz::mz_stream)); uint8_t gzip_hdr[GZIP_HEADER_MINSIZE]; @@ -349,7 +348,7 @@ string GZipFileSystem::UncompressGZIPString(const string &in) { } // stream is now set to beginning of payload data - auto status = duckdb_miniz::mz_inflateInit2(mz_stream_ptr, -MZ_DEFAULT_WINDOW_BITS); + auto status = duckdb_miniz::mz_inflateInit2(mz_stream_ptr.get(), -MZ_DEFAULT_WINDOW_BITS); if (status != duckdb_miniz::MZ_OK) { throw InternalException("Failed to initialize miniz"); } @@ -364,13 +363,14 @@ string GZipFileSystem::UncompressGZIPString(const string &in) { while (status == duckdb_miniz::MZ_OK) { mz_stream_ptr->next_out = decompress_buffer; mz_stream_ptr->avail_out = sizeof(decompress_buffer); - status = mz_inflate(mz_stream_ptr, duckdb_miniz::MZ_NO_FLUSH); + status = mz_inflate(mz_stream_ptr.get(), duckdb_miniz::MZ_NO_FLUSH); if (status != duckdb_miniz::MZ_STREAM_END && status != duckdb_miniz::MZ_OK) { throw IOException("Failed to uncompress"); } decompressed.append(char_ptr_cast(decompress_buffer), mz_stream_ptr->total_out - decompressed.size()); } - duckdb_miniz::mz_inflateEnd(mz_stream_ptr); + duckdb_miniz::mz_inflateEnd(mz_stream_ptr.get()); + if (decompressed.empty()) { throw IOException("Failed to uncompress"); } diff --git a/src/duckdb/src/common/local_file_system.cpp b/src/duckdb/src/common/local_file_system.cpp index 347a21f19..b1d292513 100644 --- a/src/duckdb/src/common/local_file_system.cpp +++ b/src/duckdb/src/common/local_file_system.cpp @@ -310,7 +310,7 @@ unique_ptr LocalFileSystem::OpenFile(const string &path_p, uint8_t f } int fd = open(path.c_str(), open_flags, 0666); if (fd == -1) { - throw IOException("Cannot open file \"%s\": %s", path, strerror(errno)); + throw IOException("Cannot open file \"%s\": %s", {{"errno", std::to_string(errno)}}, path, strerror(errno)); } // #if defined(__DARWIN__) || defined(__APPLE__) // if (flags & FileFlags::FILE_FLAGS_DIRECT_IO) { @@ -333,6 +333,8 @@ unique_ptr LocalFileSystem::OpenFile(const string &path_p, uint8_t f fl.l_start = 0; fl.l_len = 0; rc = fcntl(fd, F_SETLK, &fl); + // Retain the original error. + int retained_errno = errno; if (rc == -1) { string message; // try to find out who is holding the lock using F_GETLK @@ -352,8 +354,9 @@ unique_ptr LocalFileSystem::OpenFile(const string &path_p, uint8_t f "using the -readonly parameter in the CLI"; } } - message += ". See also https://duckdb.org/faq#how-does-duckdb-handle-concurrency"; - throw IOException("Could not set lock on file \"%s\": %s", path, message); + message += ". See also https://duckdb.org/docs/connect/concurrency"; + throw IOException("Could not set lock on file \"%s\": %s", {{"errno", std::to_string(retained_errno)}}, + path, message); } } } @@ -364,8 +367,8 @@ void LocalFileSystem::SetFilePointer(FileHandle &handle, idx_t location) { int fd = handle.Cast().fd; off_t offset = lseek(fd, location, SEEK_SET); if (offset == (off_t)-1) { - throw IOException("Could not seek to location %lld for file \"%s\": %s", location, handle.path, - strerror(errno)); + throw IOException("Could not seek to location %lld for file \"%s\": %s", {{"errno", std::to_string(errno)}}, + location, handle.path, strerror(errno)); } } @@ -373,7 +376,8 @@ idx_t LocalFileSystem::GetFilePointer(FileHandle &handle) { int fd = handle.Cast().fd; off_t position = lseek(fd, 0, SEEK_CUR); if (position == (off_t)-1) { - throw IOException("Could not get file position file \"%s\": %s", handle.path, strerror(errno)); + throw IOException("Could not get file position file \"%s\": %s", {{"errno", std::to_string(errno)}}, + handle.path, strerror(errno)); } return position; } @@ -384,7 +388,8 @@ void LocalFileSystem::Read(FileHandle &handle, void *buffer, int64_t nr_bytes, i while (nr_bytes > 0) { int64_t bytes_read = pread(fd, read_buffer, nr_bytes, location); if (bytes_read == -1) { - throw IOException("Could not read from file \"%s\": %s", handle.path, strerror(errno)); + throw IOException("Could not read from file \"%s\": %s", {{"errno", std::to_string(errno)}}, handle.path, + strerror(errno)); } if (bytes_read == 0) { throw IOException( @@ -400,7 +405,8 @@ int64_t LocalFileSystem::Read(FileHandle &handle, void *buffer, int64_t nr_bytes int fd = handle.Cast().fd; int64_t bytes_read = read(fd, buffer, nr_bytes); if (bytes_read == -1) { - throw IOException("Could not read from file \"%s\": %s", handle.path, strerror(errno)); + throw IOException("Could not read from file \"%s\": %s", {{"errno", std::to_string(errno)}}, handle.path, + strerror(errno)); } return bytes_read; } @@ -411,7 +417,8 @@ void LocalFileSystem::Write(FileHandle &handle, void *buffer, int64_t nr_bytes, while (nr_bytes > 0) { int64_t bytes_written = pwrite(fd, write_buffer, nr_bytes, location); if (bytes_written < 0) { - throw IOException("Could not write file \"%s\": %s", handle.path, strerror(errno)); + throw IOException("Could not write file \"%s\": %s", {{"errno", std::to_string(errno)}}, handle.path, + strerror(errno)); } D_ASSERT(bytes_written >= 0 && bytes_written); write_buffer += bytes_written; @@ -423,7 +430,8 @@ int64_t LocalFileSystem::Write(FileHandle &handle, void *buffer, int64_t nr_byte int fd = handle.Cast().fd; int64_t bytes_written = write(fd, buffer, nr_bytes); if (bytes_written == -1) { - throw IOException("Could not write file \"%s\": %s", handle.path, strerror(errno)); + throw IOException("Could not write file \"%s\": %s", {{"errno", std::to_string(errno)}}, handle.path, + strerror(errno)); } return bytes_written; } @@ -454,7 +462,8 @@ FileType LocalFileSystem::GetFileType(FileHandle &handle) { void LocalFileSystem::Truncate(FileHandle &handle, int64_t new_size) { int fd = handle.Cast().fd; if (ftruncate(fd, new_size) != 0) { - throw IOException("Could not truncate file \"%s\": %s", handle.path, strerror(errno)); + throw IOException("Could not truncate file \"%s\": %s", {{"errno", std::to_string(errno)}}, handle.path, + strerror(errno)); } } @@ -478,10 +487,11 @@ void LocalFileSystem::CreateDirectory(const string &directory) { if (stat(directory.c_str(), &st) != 0) { /* Directory does not exist. EEXIST for race condition */ if (mkdir(directory.c_str(), 0755) != 0 && errno != EEXIST) { - throw IOException("Failed to create directory \"%s\"!", directory); + throw IOException("Failed to create directory \"%s\"!", {{"errno", std::to_string(errno)}}, directory); } } else if (!S_ISDIR(st.st_mode)) { - throw IOException("Failed to create directory \"%s\": path exists but is not a directory!", directory); + throw IOException("Failed to create directory \"%s\": path exists but is not a directory!", + {{"errno", std::to_string(errno)}}, directory); } } @@ -531,7 +541,8 @@ void LocalFileSystem::RemoveDirectory(const string &directory) { void LocalFileSystem::RemoveFile(const string &filename) { if (std::remove(filename.c_str()) != 0) { - throw IOException("Could not remove file \"%s\": %s", filename, strerror(errno)); + throw IOException("Could not remove file \"%s\": %s", {{"errno", std::to_string(errno)}}, filename, + strerror(errno)); } } @@ -580,7 +591,7 @@ void LocalFileSystem::FileSync(FileHandle &handle) { void LocalFileSystem::MoveFile(const string &source, const string &target) { //! FIXME: rename does not guarantee atomicity or overwriting target file if it exists if (rename(source.c_str(), target.c_str()) != 0) { - throw IOException("Could not rename file!"); + throw IOException("Could not rename file!", {{"errno", std::to_string(errno)}}); } } diff --git a/src/duckdb/src/common/radix_partitioning.cpp b/src/duckdb/src/common/radix_partitioning.cpp index b4dc39bd3..69b26e050 100644 --- a/src/duckdb/src/common/radix_partitioning.cpp +++ b/src/duckdb/src/common/radix_partitioning.cpp @@ -204,7 +204,7 @@ void RadixPartitionedTupleData::ComputePartitionIndices(Vector &row_locations, i Vector &partition_indices) const { Vector intermediate(LogicalType::HASH); partitions[0]->Gather(row_locations, *FlatVector::IncrementalSelectionVector(), count, hash_col_idx, intermediate, - *FlatVector::IncrementalSelectionVector()); + *FlatVector::IncrementalSelectionVector(), nullptr); RadixBitsSwitch(radix_bits, intermediate, partition_indices, count); } diff --git a/src/duckdb/src/common/row_operations/row_gather.cpp b/src/duckdb/src/common/row_operations/row_gather.cpp index ee2d1cb02..0073cefcc 100644 --- a/src/duckdb/src/common/row_operations/row_gather.cpp +++ b/src/duckdb/src/common/row_operations/row_gather.cpp @@ -113,7 +113,8 @@ static void GatherNestedVector(Vector &rows, const SelectionVector &row_sel, Vec } // Deserialise into the selected locations - RowOperations::HeapGather(col, count, col_sel, col_no, data_locations.get(), mask_locations.get()); + NestedValidity parent_validity(mask_locations.get(), col_no); + RowOperations::HeapGather(col, count, col_sel, data_locations.get(), &parent_validity); } void RowOperations::Gather(Vector &rows, const SelectionVector &row_sel, Vector &col, const SelectionVector &col_sel, diff --git a/src/duckdb/src/common/row_operations/row_heap_gather.cpp b/src/duckdb/src/common/row_operations/row_heap_gather.cpp index 881178512..dbcfea6c1 100644 --- a/src/duckdb/src/common/row_operations/row_heap_gather.cpp +++ b/src/duckdb/src/common/row_operations/row_heap_gather.cpp @@ -49,7 +49,8 @@ static void HeapGatherStructVector(Vector &v, const idx_t vcount, const Selectio // now deserialize into the struct vectors auto &children = StructVector::GetEntries(v); for (idx_t i = 0; i < child_types.size(); i++) { - RowOperations::HeapGather(*children[i], vcount, sel, i, key_locations, struct_validitymask_locations); + NestedValidity parent_validity(struct_validitymask_locations, i); + RowOperations::HeapGather(*children[i], vcount, sel, key_locations, &parent_validity); } } @@ -122,7 +123,7 @@ static void HeapGatherListVector(Vector &v, const idx_t vcount, const SelectionV } // now deserialize and add to listvector - RowOperations::HeapGather(list_vec_to_append, next, *FlatVector::IncrementalSelectionVector(), 0, + RowOperations::HeapGather(list_vec_to_append, next, *FlatVector::IncrementalSelectionVector(), list_entry_locations, nullptr); ListVector::Append(v, list_vec_to_append, next); @@ -136,7 +137,6 @@ static void HeapGatherListVector(Vector &v, const idx_t vcount, const SelectionV static void HeapGatherArrayVector(Vector &v, const idx_t vcount, const SelectionVector &sel, data_ptr_t *key_locations) { // Setup - auto &validity = FlatVector::Validity(v); auto &child_type = ArrayType::GetChildType(v.GetType()); auto array_size = ArrayType::GetSize(v.GetType()); auto &child_vector = ArrayVector::GetEntry(v); @@ -148,19 +148,7 @@ static void HeapGatherArrayVector(Vector &v, const idx_t vcount, const Selection // array must have a validitymask for its elements auto array_validitymask_size = (array_size + 7) / 8; - auto &child_validity = FlatVector::Validity(child_vector); - for (idx_t i = 0; i < vcount; i++) { - // row idx - const auto col_idx = sel.get_index(i); - if (!validity.RowIsValid(col_idx)) { - // we still need to zero out the child validity corresponding to this row - for (idx_t elem_idx = 0; elem_idx < array_size; elem_idx++) { - child_validity.Set(col_idx * array_size + elem_idx, false); - } - continue; - } - // Setup validity mask data_ptr_t array_validitymask_location = key_locations[i]; key_locations[i] += array_validitymask_size; @@ -173,20 +161,14 @@ static void HeapGatherArrayVector(Vector &v, const idx_t vcount, const Selection key_locations[i] += array_size * sizeof(idx_t); } - auto array_start = col_idx * array_size; - auto elem_remaining = array_size; + // row idx + const auto row_idx = sel.get_index(i); - idx_t offset_in_byte = 0; + auto array_start = row_idx * array_size; + auto elem_remaining = array_size; while (elem_remaining > 0) { auto chunk_size = MinValue(static_cast(STANDARD_VECTOR_SIZE), elem_remaining); - for (idx_t elem_idx = 0; elem_idx < chunk_size; elem_idx++) { - child_validity.Set(array_start + elem_idx, *(array_validitymask_location) & (1 << offset_in_byte)); - if (++offset_in_byte == 8) { - array_validitymask_location++; - offset_in_byte = 0; - } - } SelectionVector array_sel(STANDARD_VECTOR_SIZE); @@ -207,7 +189,9 @@ static void HeapGatherArrayVector(Vector &v, const idx_t vcount, const Selection } } - RowOperations::HeapGather(child_vector, chunk_size, array_sel, 0, array_entry_locations, nullptr); + // Pass on this array's validity mask to the child vector + NestedValidity parent_validity(array_validitymask_location); + RowOperations::HeapGather(child_vector, chunk_size, array_sel, array_entry_locations, &parent_validity); elem_remaining -= chunk_size; array_start += chunk_size; @@ -215,20 +199,14 @@ static void HeapGatherArrayVector(Vector &v, const idx_t vcount, const Selection } } -void RowOperations::HeapGather(Vector &v, const idx_t &vcount, const SelectionVector &sel, const idx_t &col_no, - data_ptr_t *key_locations, data_ptr_t *validitymask_locations) { +void RowOperations::HeapGather(Vector &v, const idx_t &vcount, const SelectionVector &sel, data_ptr_t *key_locations, + optional_ptr parent_validity) { v.SetVectorType(VectorType::FLAT_VECTOR); auto &validity = FlatVector::Validity(v); - if (validitymask_locations) { - // Precompute mask indexes - idx_t entry_idx; - idx_t idx_in_entry; - ValidityBytes::GetEntryIndex(col_no, entry_idx, idx_in_entry); - + if (parent_validity) { for (idx_t i = 0; i < vcount; i++) { - ValidityBytes row_mask(validitymask_locations[i]); - const auto valid = row_mask.RowIsValid(row_mask.GetValidityEntry(entry_idx), idx_in_entry); + const auto valid = parent_validity->IsValid(i); const auto col_idx = sel.get_index(i); validity.Set(col_idx, valid); } diff --git a/src/duckdb/src/common/row_operations/row_heap_scatter.cpp b/src/duckdb/src/common/row_operations/row_heap_scatter.cpp index 6b13bfffc..135361d1b 100644 --- a/src/duckdb/src/common/row_operations/row_heap_scatter.cpp +++ b/src/duckdb/src/common/row_operations/row_heap_scatter.cpp @@ -7,6 +7,46 @@ namespace duckdb { using ValidityBytes = TemplatedValidityMask; +NestedValidity::NestedValidity(data_ptr_t validitymask_location) + : list_validity_location(validitymask_location), struct_validity_locations(nullptr), entry_idx(0), idx_in_entry(0) { +} + +NestedValidity::NestedValidity(data_ptr_t *validitymask_locations, idx_t child_vector_index) + : list_validity_location(nullptr), struct_validity_locations(validitymask_locations), entry_idx(0), + idx_in_entry(0) { + ValidityBytes::GetEntryIndex(child_vector_index, entry_idx, idx_in_entry); +} + +void NestedValidity::SetInvalid(idx_t idx) { + if (list_validity_location) { + // Is List + idx_t list_entry_idx; + idx_t list_idx_in_entry; + ValidityBytes::GetEntryIndex(idx, list_entry_idx, list_idx_in_entry); + const auto bit = ~(1UL << list_idx_in_entry); + list_validity_location[list_entry_idx] &= bit; + } else { + // Is Struct + const auto bit = ~(1UL << idx_in_entry); + *(struct_validity_locations[idx] + entry_idx) &= bit; + } +} + +bool NestedValidity::IsValid(idx_t idx) { + if (list_validity_location) { + // Is List + idx_t list_entry_idx; + idx_t list_idx_in_entry; + ValidityBytes::GetEntryIndex(idx, list_entry_idx, list_idx_in_entry); + const auto bit = (1UL << list_idx_in_entry); + return list_validity_location[list_entry_idx] & bit; + } else { + // Is Struct + const auto bit = (1UL << idx_in_entry); + return *(struct_validity_locations[idx] + entry_idx) & bit; + } +} + static void ComputeStringEntrySizes(UnifiedVectorFormat &vdata, idx_t entry_sizes[], const idx_t ser_count, const SelectionVector &sel, const idx_t offset) { auto strings = UnifiedVectorFormat::GetData(vdata); @@ -86,11 +126,12 @@ static void ComputeArrayEntrySizes(Vector &v, UnifiedVectorFormat &vdata, idx_t auto child_vector = ArrayVector::GetEntry(v); idx_t array_entry_sizes[STANDARD_VECTOR_SIZE]; + const idx_t array_validitymask_size = (array_size + 7) / 8; for (idx_t i = 0; i < ser_count; i++) { // Validity for the array elements - entry_sizes[i] += (array_size + 7) / 8; + entry_sizes[i] += array_validitymask_size; // serialize size of each entry (if non-constant size) if (!TypeIsConstantSize(ArrayType::GetChildType(v.GetType()).InternalType())) { @@ -160,10 +201,11 @@ void RowOperations::ComputeEntrySizes(Vector &v, idx_t entry_sizes[], idx_t vcou } template -static void TemplatedHeapScatter(UnifiedVectorFormat &vdata, const SelectionVector &sel, idx_t count, idx_t col_idx, - data_ptr_t *key_locations, data_ptr_t *validitymask_locations, idx_t offset) { +static void TemplatedHeapScatter(UnifiedVectorFormat &vdata, const SelectionVector &sel, idx_t count, + data_ptr_t *key_locations, optional_ptr parent_validity, + idx_t offset) { auto source = UnifiedVectorFormat::GetData(vdata); - if (!validitymask_locations) { + if (!parent_validity) { for (idx_t i = 0; i < count; i++) { auto idx = sel.get_index(i); auto source_idx = vdata.sel->get_index(idx + offset); @@ -173,10 +215,6 @@ static void TemplatedHeapScatter(UnifiedVectorFormat &vdata, const SelectionVect key_locations[i] += sizeof(T); } } else { - idx_t entry_idx; - idx_t idx_in_entry; - ValidityBytes::GetEntryIndex(col_idx, entry_idx, idx_in_entry); - const auto bit = ~(1UL << idx_in_entry); for (idx_t i = 0; i < count; i++) { auto idx = sel.get_index(i); auto source_idx = vdata.sel->get_index(idx + offset); @@ -187,19 +225,20 @@ static void TemplatedHeapScatter(UnifiedVectorFormat &vdata, const SelectionVect // set the validitymask if (!vdata.validity.RowIsValid(source_idx)) { - *(validitymask_locations[i] + entry_idx) &= bit; + parent_validity->SetInvalid(i); } } } } -static void HeapScatterStringVector(Vector &v, idx_t vcount, const SelectionVector &sel, idx_t ser_count, idx_t col_idx, - data_ptr_t *key_locations, data_ptr_t *validitymask_locations, idx_t offset) { +static void HeapScatterStringVector(Vector &v, idx_t vcount, const SelectionVector &sel, idx_t ser_count, + data_ptr_t *key_locations, optional_ptr parent_validity, + idx_t offset) { UnifiedVectorFormat vdata; v.ToUnifiedFormat(vcount, vdata); auto strings = UnifiedVectorFormat::GetData(vdata); - if (!validitymask_locations) { + if (!parent_validity) { for (idx_t i = 0; i < ser_count; i++) { auto idx = sel.get_index(i); auto source_idx = vdata.sel->get_index(idx + offset); @@ -214,10 +253,6 @@ static void HeapScatterStringVector(Vector &v, idx_t vcount, const SelectionVect } } } else { - idx_t entry_idx; - idx_t idx_in_entry; - ValidityBytes::GetEntryIndex(col_idx, entry_idx, idx_in_entry); - const auto bit = ~(1UL << idx_in_entry); for (idx_t i = 0; i < ser_count; i++) { auto idx = sel.get_index(i); auto source_idx = vdata.sel->get_index(idx + offset); @@ -231,26 +266,21 @@ static void HeapScatterStringVector(Vector &v, idx_t vcount, const SelectionVect key_locations[i] += string_entry.GetSize(); } else { // set the validitymask - *(validitymask_locations[i] + entry_idx) &= bit; + parent_validity->SetInvalid(i); } } } } -static void HeapScatterStructVector(Vector &v, idx_t vcount, const SelectionVector &sel, idx_t ser_count, idx_t col_idx, - data_ptr_t *key_locations, data_ptr_t *validitymask_locations, idx_t offset) { +static void HeapScatterStructVector(Vector &v, idx_t vcount, const SelectionVector &sel, idx_t ser_count, + data_ptr_t *key_locations, optional_ptr parent_validity, + idx_t offset) { UnifiedVectorFormat vdata; v.ToUnifiedFormat(vcount, vdata); auto &children = StructVector::GetEntries(v); idx_t num_children = children.size(); - // the whole struct itself can be NULL - idx_t entry_idx; - idx_t idx_in_entry; - ValidityBytes::GetEntryIndex(col_idx, entry_idx, idx_in_entry); - const auto bit = ~(1UL << idx_in_entry); - // struct must have a validitymask for its fields const idx_t struct_validitymask_size = (num_children + 7) / 8; data_ptr_t struct_validitymask_locations[STANDARD_VECTOR_SIZE]; @@ -263,28 +293,25 @@ static void HeapScatterStructVector(Vector &v, idx_t vcount, const SelectionVect // set whether the whole struct is null auto idx = sel.get_index(i); auto source_idx = vdata.sel->get_index(idx) + offset; - if (validitymask_locations && !vdata.validity.RowIsValid(source_idx)) { - *(validitymask_locations[i] + entry_idx) &= bit; + if (parent_validity && !vdata.validity.RowIsValid(source_idx)) { + parent_validity->SetInvalid(i); } } // now serialize the struct vectors for (idx_t i = 0; i < children.size(); i++) { auto &struct_vector = *children[i]; - RowOperations::HeapScatter(struct_vector, vcount, sel, ser_count, i, key_locations, - struct_validitymask_locations, offset); + NestedValidity struct_validity(struct_validitymask_locations, i); + RowOperations::HeapScatter(struct_vector, vcount, sel, ser_count, key_locations, &struct_validity, offset); } } -static void HeapScatterListVector(Vector &v, idx_t vcount, const SelectionVector &sel, idx_t ser_count, idx_t col_no, - data_ptr_t *key_locations, data_ptr_t *validitymask_locations, idx_t offset) { +static void HeapScatterListVector(Vector &v, idx_t vcount, const SelectionVector &sel, idx_t ser_count, + data_ptr_t *key_locations, optional_ptr parent_validity, + idx_t offset) { UnifiedVectorFormat vdata; v.ToUnifiedFormat(vcount, vdata); - idx_t entry_idx; - idx_t idx_in_entry; - ValidityBytes::GetEntryIndex(col_no, entry_idx, idx_in_entry); - auto list_data = ListVector::GetData(v); auto &child_vector = ListVector::GetEntry(v); @@ -299,10 +326,9 @@ static void HeapScatterListVector(Vector &v, idx_t vcount, const SelectionVector auto idx = sel.get_index(i); auto source_idx = vdata.sel->get_index(idx + offset); if (!vdata.validity.RowIsValid(source_idx)) { - if (validitymask_locations) { + if (parent_validity) { // set the row validitymask for this column to invalid - ValidityBytes row_mask(validitymask_locations[i]); - row_mask.SetInvalidUnsafe(entry_idx, idx_in_entry); + parent_validity->SetInvalid(i); } continue; } @@ -366,8 +392,8 @@ static void HeapScatterListVector(Vector &v, idx_t vcount, const SelectionVector // now serialize to the locations RowOperations::HeapScatter(child_vector, ListVector::GetListSize(v), - *FlatVector::IncrementalSelectionVector(), next, 0, list_entry_locations, - nullptr, entry_offset); + *FlatVector::IncrementalSelectionVector(), next, list_entry_locations, nullptr, + entry_offset); // update for next iteration entry_remaining -= next; @@ -376,8 +402,9 @@ static void HeapScatterListVector(Vector &v, idx_t vcount, const SelectionVector } } -static void HeapScatterArrayVector(Vector &v, idx_t vcount, const SelectionVector &sel, idx_t ser_count, idx_t col_idx, - data_ptr_t *key_locations, data_ptr_t *validitymask_locations, idx_t offset) { +static void HeapScatterArrayVector(Vector &v, idx_t vcount, const SelectionVector &sel, idx_t ser_count, + data_ptr_t *key_locations, optional_ptr parent_validity, + idx_t offset) { auto &child_vector = ArrayVector::GetEntry(v); auto array_size = ArrayType::GetSize(v.GetType()); @@ -397,23 +424,17 @@ static void HeapScatterArrayVector(Vector &v, idx_t vcount, const SelectionVecto // array must have a validitymask for its elements auto array_validitymask_size = (array_size + 7) / 8; - idx_t entry_idx; - idx_t idx_in_entry; - ValidityBytes::GetEntryIndex(col_idx, entry_idx, idx_in_entry); - const auto bit = ~(1UL << idx_in_entry); - for (idx_t i = 0; i < ser_count; i++) { + // Set if the whole array itself is null in the parent entry auto source_idx = vdata.sel->get_index(sel.get_index(i) + offset); - - // First off, set the validity of the mask itself in the parent entry - if (validitymask_locations && !vdata.validity.RowIsValid(source_idx)) { - *(validitymask_locations[i] + entry_idx) &= bit; + if (parent_validity && !vdata.validity.RowIsValid(source_idx)) { + parent_validity->SetInvalid(i); } // Now we can serialize the array itself // Every array starts with a validity mask for the children data_ptr_t array_validitymask_location = key_locations[i]; - memset(array_validitymask_location, -1, (array_size + 7) / 8); + memset(array_validitymask_location, -1, array_validitymask_size); key_locations[i] += array_validitymask_size; // If the array contains variable size entries, we reserve spaces for them here @@ -427,24 +448,10 @@ static void HeapScatterArrayVector(Vector &v, idx_t vcount, const SelectionVecto auto array_start = source_idx * array_size; auto elem_remaining = array_size; - idx_t offset_in_byte = 0; - while (elem_remaining > 0) { // the array elements can span multiple vectors, so we divide it into chunks auto chunk_size = MinValue(static_cast(STANDARD_VECTOR_SIZE), elem_remaining); - // serialize list validity - for (idx_t elem_idx = 0; elem_idx < chunk_size; elem_idx++) { - auto idx_in_array = child_vdata.sel->get_index(array_start + elem_idx); - if (!child_vdata.validity.RowIsValid(idx_in_array)) { - *(array_validitymask_location) &= ~(1UL << offset_in_byte); - } - if (++offset_in_byte == 8) { - array_validitymask_location++; - offset_in_byte = 0; - } - } - // Setup the locations for the elements if (child_type_is_var_size) { // The elements are variable sized @@ -467,9 +474,10 @@ static void HeapScatterArrayVector(Vector &v, idx_t vcount, const SelectionVecto } } + NestedValidity array_parent_validity(array_validitymask_location); RowOperations::HeapScatter(child_vector, ArrayVector::GetTotalSize(v), - *FlatVector::IncrementalSelectionVector(), chunk_size, 0, array_entry_locations, - nullptr, array_start); + *FlatVector::IncrementalSelectionVector(), chunk_size, array_entry_locations, + &array_parent_validity, array_start); // update for next iteration elem_remaining -= chunk_size; @@ -478,26 +486,26 @@ static void HeapScatterArrayVector(Vector &v, idx_t vcount, const SelectionVecto } } -void RowOperations::HeapScatter(Vector &v, idx_t vcount, const SelectionVector &sel, idx_t ser_count, idx_t col_idx, - data_ptr_t *key_locations, data_ptr_t *validitymask_locations, idx_t offset) { +void RowOperations::HeapScatter(Vector &v, idx_t vcount, const SelectionVector &sel, idx_t ser_count, + data_ptr_t *key_locations, optional_ptr parent_validity, idx_t offset) { if (TypeIsConstantSize(v.GetType().InternalType())) { UnifiedVectorFormat vdata; v.ToUnifiedFormat(vcount, vdata); - RowOperations::HeapScatterVData(vdata, v.GetType().InternalType(), sel, ser_count, col_idx, key_locations, - validitymask_locations, offset); + RowOperations::HeapScatterVData(vdata, v.GetType().InternalType(), sel, ser_count, key_locations, + parent_validity, offset); } else { switch (v.GetType().InternalType()) { case PhysicalType::VARCHAR: - HeapScatterStringVector(v, vcount, sel, ser_count, col_idx, key_locations, validitymask_locations, offset); + HeapScatterStringVector(v, vcount, sel, ser_count, key_locations, parent_validity, offset); break; case PhysicalType::STRUCT: - HeapScatterStructVector(v, vcount, sel, ser_count, col_idx, key_locations, validitymask_locations, offset); + HeapScatterStructVector(v, vcount, sel, ser_count, key_locations, parent_validity, offset); break; case PhysicalType::LIST: - HeapScatterListVector(v, vcount, sel, ser_count, col_idx, key_locations, validitymask_locations, offset); + HeapScatterListVector(v, vcount, sel, ser_count, key_locations, parent_validity, offset); break; case PhysicalType::ARRAY: - HeapScatterArrayVector(v, vcount, sel, ser_count, col_idx, key_locations, validitymask_locations, offset); + HeapScatterArrayVector(v, vcount, sel, ser_count, key_locations, parent_validity, offset); break; default: // LCOV_EXCL_START @@ -509,48 +517,48 @@ void RowOperations::HeapScatter(Vector &v, idx_t vcount, const SelectionVector & } void RowOperations::HeapScatterVData(UnifiedVectorFormat &vdata, PhysicalType type, const SelectionVector &sel, - idx_t ser_count, idx_t col_idx, data_ptr_t *key_locations, - data_ptr_t *validitymask_locations, idx_t offset) { + idx_t ser_count, data_ptr_t *key_locations, + optional_ptr parent_validity, idx_t offset) { switch (type) { case PhysicalType::BOOL: case PhysicalType::INT8: - TemplatedHeapScatter(vdata, sel, ser_count, col_idx, key_locations, validitymask_locations, offset); + TemplatedHeapScatter(vdata, sel, ser_count, key_locations, parent_validity, offset); break; case PhysicalType::INT16: - TemplatedHeapScatter(vdata, sel, ser_count, col_idx, key_locations, validitymask_locations, offset); + TemplatedHeapScatter(vdata, sel, ser_count, key_locations, parent_validity, offset); break; case PhysicalType::INT32: - TemplatedHeapScatter(vdata, sel, ser_count, col_idx, key_locations, validitymask_locations, offset); + TemplatedHeapScatter(vdata, sel, ser_count, key_locations, parent_validity, offset); break; case PhysicalType::INT64: - TemplatedHeapScatter(vdata, sel, ser_count, col_idx, key_locations, validitymask_locations, offset); + TemplatedHeapScatter(vdata, sel, ser_count, key_locations, parent_validity, offset); break; case PhysicalType::UINT8: - TemplatedHeapScatter(vdata, sel, ser_count, col_idx, key_locations, validitymask_locations, offset); + TemplatedHeapScatter(vdata, sel, ser_count, key_locations, parent_validity, offset); break; case PhysicalType::UINT16: - TemplatedHeapScatter(vdata, sel, ser_count, col_idx, key_locations, validitymask_locations, offset); + TemplatedHeapScatter(vdata, sel, ser_count, key_locations, parent_validity, offset); break; case PhysicalType::UINT32: - TemplatedHeapScatter(vdata, sel, ser_count, col_idx, key_locations, validitymask_locations, offset); + TemplatedHeapScatter(vdata, sel, ser_count, key_locations, parent_validity, offset); break; case PhysicalType::UINT64: - TemplatedHeapScatter(vdata, sel, ser_count, col_idx, key_locations, validitymask_locations, offset); + TemplatedHeapScatter(vdata, sel, ser_count, key_locations, parent_validity, offset); break; case PhysicalType::INT128: - TemplatedHeapScatter(vdata, sel, ser_count, col_idx, key_locations, validitymask_locations, offset); + TemplatedHeapScatter(vdata, sel, ser_count, key_locations, parent_validity, offset); break; case PhysicalType::UINT128: - TemplatedHeapScatter(vdata, sel, ser_count, col_idx, key_locations, validitymask_locations, offset); + TemplatedHeapScatter(vdata, sel, ser_count, key_locations, parent_validity, offset); break; case PhysicalType::FLOAT: - TemplatedHeapScatter(vdata, sel, ser_count, col_idx, key_locations, validitymask_locations, offset); + TemplatedHeapScatter(vdata, sel, ser_count, key_locations, parent_validity, offset); break; case PhysicalType::DOUBLE: - TemplatedHeapScatter(vdata, sel, ser_count, col_idx, key_locations, validitymask_locations, offset); + TemplatedHeapScatter(vdata, sel, ser_count, key_locations, parent_validity, offset); break; case PhysicalType::INTERVAL: - TemplatedHeapScatter(vdata, sel, ser_count, col_idx, key_locations, validitymask_locations, offset); + TemplatedHeapScatter(vdata, sel, ser_count, key_locations, parent_validity, offset); break; default: throw NotImplementedException("FIXME: Serialize to of constant type column to row-format"); diff --git a/src/duckdb/src/common/row_operations/row_matcher.cpp b/src/duckdb/src/common/row_operations/row_matcher.cpp index a74e2ef5c..8d6770042 100644 --- a/src/duckdb/src/common/row_operations/row_matcher.cpp +++ b/src/duckdb/src/common/row_operations/row_matcher.cpp @@ -176,7 +176,7 @@ static idx_t GenericNestedMatch(Vector &lhs_vector, const TupleDataVectorFormat Vector key(type); const auto gather_function = TupleDataCollection::GetGatherFunction(type); gather_function.function(rhs_layout, rhs_row_locations, col_idx, sel, count, key, - *FlatVector::IncrementalSelectionVector(), key, gather_function.child_functions); + *FlatVector::IncrementalSelectionVector(), nullptr, gather_function.child_functions); // Densify the input column Vector sliced(lhs_vector, sel, count); diff --git a/src/duckdb/src/common/row_operations/row_scatter.cpp b/src/duckdb/src/common/row_operations/row_scatter.cpp index 6d11c4375..5cf44bc48 100644 --- a/src/duckdb/src/common/row_operations/row_scatter.cpp +++ b/src/duckdb/src/common/row_operations/row_scatter.cpp @@ -107,7 +107,8 @@ static void ScatterNestedVector(Vector &vec, UnifiedVectorFormat &col, Vector &r } // Serialise the data - RowOperations::HeapScatter(vec, vcount, sel, count, col_no, data_locations, validitymask_locations); + NestedValidity parent_validity(validitymask_locations, col_no); + RowOperations::HeapScatter(vec, vcount, sel, count, data_locations, &parent_validity); } void RowOperations::Scatter(DataChunk &columns, UnifiedVectorFormat col_data[], const RowLayout &layout, Vector &rows, diff --git a/src/duckdb/src/common/sort/radix_sort.cpp b/src/duckdb/src/common/sort/radix_sort.cpp index 179d5f9d2..9d40f90d7 100644 --- a/src/duckdb/src/common/sort/radix_sort.cpp +++ b/src/duckdb/src/common/sort/radix_sort.cpp @@ -248,7 +248,8 @@ void RadixSort(BufferManager &buffer_manager, const data_ptr_t &dataptr, const i } else if (sorting_size <= SortConstants::MSD_RADIX_SORT_SIZE_THRESHOLD) { RadixSortLSD(buffer_manager, dataptr, count, col_offset, sort_layout.entry_size, sorting_size); } else { - auto temp_block = buffer_manager.Allocate(MaxValue(count * sort_layout.entry_size, (idx_t)Storage::BLOCK_SIZE)); + auto temp_block = buffer_manager.Allocate(MemoryTag::ORDER_BY, + MaxValue(count * sort_layout.entry_size, (idx_t)Storage::BLOCK_SIZE)); auto preallocated_array = make_unsafe_uniq_array(sorting_size * SortConstants::MSD_RADIX_LOCATIONS); RadixSortMSD(dataptr, temp_block.Ptr(), count, col_offset, sort_layout.entry_size, sorting_size, 0, preallocated_array.get(), false); diff --git a/src/duckdb/src/common/sort/sort_state.cpp b/src/duckdb/src/common/sort/sort_state.cpp index a91736367..9c8594866 100644 --- a/src/duckdb/src/common/sort/sort_state.cpp +++ b/src/duckdb/src/common/sort/sort_state.cpp @@ -269,7 +269,7 @@ unique_ptr LocalSortState::ConcatenateBlocks(RowDataCollection &ro auto buffer_manager = &row_data.buffer_manager; const idx_t &entry_size = row_data.entry_size; idx_t capacity = MaxValue(((idx_t)Storage::BLOCK_SIZE + entry_size - 1) / entry_size, row_data.count); - auto new_block = make_uniq(*buffer_manager, capacity, entry_size); + auto new_block = make_uniq(MemoryTag::ORDER_BY, *buffer_manager, capacity, entry_size); new_block->count = row_data.count; auto new_block_handle = buffer_manager->Pin(new_block->block); data_ptr_t new_block_ptr = new_block_handle.Ptr(); @@ -294,8 +294,8 @@ void LocalSortState::ReOrder(SortedData &sd, data_ptr_t sorting_ptr, RowDataColl auto unordered_data_handle = buffer_manager->Pin(unordered_data_block->block); const data_ptr_t unordered_data_ptr = unordered_data_handle.Ptr(); // Create new block that will hold re-ordered row data - auto ordered_data_block = - make_uniq(*buffer_manager, unordered_data_block->capacity, unordered_data_block->entry_size); + auto ordered_data_block = make_uniq(MemoryTag::ORDER_BY, *buffer_manager, + unordered_data_block->capacity, unordered_data_block->entry_size); ordered_data_block->count = count; auto ordered_data_handle = buffer_manager->Pin(ordered_data_block->block); data_ptr_t ordered_data_ptr = ordered_data_handle.Ptr(); @@ -323,7 +323,7 @@ void LocalSortState::ReOrder(SortedData &sd, data_ptr_t sorting_ptr, RowDataColl std::accumulate(heap.blocks.begin(), heap.blocks.end(), (idx_t)0, [](idx_t a, const unique_ptr &b) { return a + b->byte_offset; }); idx_t heap_block_size = MaxValue(total_byte_offset, (idx_t)Storage::BLOCK_SIZE); - auto ordered_heap_block = make_uniq(*buffer_manager, heap_block_size, 1); + auto ordered_heap_block = make_uniq(MemoryTag::ORDER_BY, *buffer_manager, heap_block_size, 1); ordered_heap_block->count = count; ordered_heap_block->byte_offset = total_byte_offset; auto ordered_heap_handle = buffer_manager->Pin(ordered_heap_block->block); diff --git a/src/duckdb/src/common/sort/sorted_block.cpp b/src/duckdb/src/common/sort/sorted_block.cpp index f6d3ef4c4..22127abe1 100644 --- a/src/duckdb/src/common/sort/sorted_block.cpp +++ b/src/duckdb/src/common/sort/sorted_block.cpp @@ -27,9 +27,10 @@ idx_t SortedData::Count() { void SortedData::CreateBlock() { auto capacity = MaxValue(((idx_t)Storage::BLOCK_SIZE + layout.GetRowWidth() - 1) / layout.GetRowWidth(), state.block_capacity); - data_blocks.push_back(make_uniq(buffer_manager, capacity, layout.GetRowWidth())); + data_blocks.push_back(make_uniq(MemoryTag::ORDER_BY, buffer_manager, capacity, layout.GetRowWidth())); if (!layout.AllConstant() && state.external) { - heap_blocks.push_back(make_uniq(buffer_manager, (idx_t)Storage::BLOCK_SIZE, 1)); + heap_blocks.push_back( + make_uniq(MemoryTag::ORDER_BY, buffer_manager, (idx_t)Storage::BLOCK_SIZE, 1)); D_ASSERT(data_blocks.size() == heap_blocks.size()); } } @@ -105,7 +106,8 @@ void SortedBlock::InitializeWrite() { void SortedBlock::CreateBlock() { auto capacity = MaxValue(((idx_t)Storage::BLOCK_SIZE + sort_layout.entry_size - 1) / sort_layout.entry_size, state.block_capacity); - radix_sorting_data.push_back(make_uniq(buffer_manager, capacity, sort_layout.entry_size)); + radix_sorting_data.push_back( + make_uniq(MemoryTag::ORDER_BY, buffer_manager, capacity, sort_layout.entry_size)); } void SortedBlock::AppendSortedBlocks(vector> &sorted_blocks) { diff --git a/src/duckdb/src/common/string_util.cpp b/src/duckdb/src/common/string_util.cpp index 79d99a88e..4df16fc13 100644 --- a/src/duckdb/src/common/string_util.cpp +++ b/src/duckdb/src/common/string_util.cpp @@ -583,4 +583,71 @@ string StringUtil::ToJSONMap(ExceptionType type, const string &message, const un return result; } +string StringUtil::GetFileName(const string &file_path) { + + idx_t pos = file_path.find_last_of("/\\"); + if (pos == string::npos) { + return file_path; + } + auto end = file_path.size() - 1; + + // If the rest of the string is just slashes or dots, trim them + if (file_path.find_first_not_of("/\\.", pos) == string::npos) { + // Trim the trailing slashes and dots + while (end > 0 && (file_path[end] == '/' || file_path[end] == '.' || file_path[end] == '\\')) { + end--; + } + + // Now find the next slash + pos = file_path.find_last_of("/\\", end); + if (pos == string::npos) { + return file_path.substr(0, end + 1); + } + } + + return file_path.substr(pos + 1, end - pos); +} + +string StringUtil::GetFileExtension(const string &file_name) { + auto name = GetFileName(file_name); + idx_t pos = name.find_last_of('.'); + // We dont consider e.g. `.gitignore` to have an extension + if (pos == string::npos || pos == 0) { + return ""; + } + return name.substr(pos + 1); +} + +string StringUtil::GetFileStem(const string &file_name) { + auto name = GetFileName(file_name); + if (name.size() > 1 && name[0] == '.') { + return name; + } + idx_t pos = name.find_last_of('.'); + if (pos == string::npos) { + return name; + } + return name.substr(0, pos); +} + +string StringUtil::GetFilePath(const string &file_path) { + + // Trim the trailing slashes + auto end = file_path.size() - 1; + while (end > 0 && (file_path[end] == '/' || file_path[end] == '\\')) { + end--; + } + + auto pos = file_path.find_last_of("/\\", end); + if (pos == string::npos) { + return ""; + } + + while (pos > 0 && (file_path[pos] == '/' || file_path[pos] == '\\')) { + pos--; + } + + return file_path.substr(0, pos + 1); +} + } // namespace duckdb diff --git a/src/duckdb/src/common/types.cpp b/src/duckdb/src/common/types.cpp index 8fbec1d57..c7c4d5cfa 100644 --- a/src/duckdb/src/common/types.cpp +++ b/src/duckdb/src/common/types.cpp @@ -569,6 +569,10 @@ bool LogicalType::IsValid() const { return id() != LogicalTypeId::INVALID && id() != LogicalTypeId::UNKNOWN; } +bool LogicalType::Contains(LogicalTypeId type_id) const { + return Contains([&](const LogicalType &type) { return type.id() == type_id; }); +} + bool LogicalType::GetDecimalProperties(uint8_t &width, uint8_t &scale) const { switch (id_) { case LogicalTypeId::SQLNULL: @@ -1053,9 +1057,39 @@ LogicalType LogicalType::MaxLogicalType(ClientContext &context, const LogicalTyp void LogicalType::Verify() const { #ifdef DEBUG - if (id_ == LogicalTypeId::DECIMAL) { + switch (id_) { + case LogicalTypeId::DECIMAL: D_ASSERT(DecimalType::GetWidth(*this) >= 1 && DecimalType::GetWidth(*this) <= Decimal::MAX_WIDTH_DECIMAL); D_ASSERT(DecimalType::GetScale(*this) >= 0 && DecimalType::GetScale(*this) <= DecimalType::GetWidth(*this)); + break; + case LogicalTypeId::STRUCT: { + // verify child types + case_insensitive_set_t child_names; + bool all_empty = true; + for (auto &entry : StructType::GetChildTypes(*this)) { + if (entry.first.empty()) { + D_ASSERT(all_empty); + } else { + // check for duplicate struct names + all_empty = false; + auto existing_entry = child_names.find(entry.first); + D_ASSERT(existing_entry == child_names.end()); + child_names.insert(entry.first); + } + entry.second.Verify(); + } + break; + } + case LogicalTypeId::LIST: + ListType::GetChildType(*this).Verify(); + break; + case LogicalTypeId::MAP: { + MapType::KeyType(*this).Verify(); + MapType::ValueType(*this).Verify(); + break; + } + default: + break; } #endif } @@ -1444,6 +1478,37 @@ bool ArrayType::IsAnySize(const LogicalType &type) { return info->Cast().size == 0; } +LogicalType ArrayType::ConvertToList(const LogicalType &type) { + switch (type.id()) { + case LogicalTypeId::ARRAY: { + return LogicalType::LIST(ConvertToList(ArrayType::GetChildType(type))); + } + case LogicalTypeId::LIST: + return LogicalType::LIST(ConvertToList(ListType::GetChildType(type))); + case LogicalTypeId::STRUCT: { + auto children = StructType::GetChildTypes(type); + for (auto &child : children) { + child.second = ConvertToList(child.second); + } + return LogicalType::STRUCT(children); + } + case LogicalTypeId::MAP: { + auto key_type = ConvertToList(MapType::KeyType(type)); + auto value_type = ConvertToList(MapType::ValueType(type)); + return LogicalType::MAP(key_type, value_type); + } + case LogicalTypeId::UNION: { + auto children = UnionType::CopyMemberTypes(type); + for (auto &child : children) { + child.second = ConvertToList(child.second); + } + return LogicalType::UNION(children); + } + default: + return type; + } +} + LogicalType LogicalType::ARRAY(const LogicalType &child, idx_t size) { D_ASSERT(size > 0); D_ASSERT(size < ArrayType::MAX_ARRAY_SIZE); diff --git a/src/duckdb/src/common/types/column/column_data_allocator.cpp b/src/duckdb/src/common/types/column/column_data_allocator.cpp index 3e7d1843d..690595f0a 100644 --- a/src/duckdb/src/common/types/column/column_data_allocator.cpp +++ b/src/duckdb/src/common/types/column/column_data_allocator.cpp @@ -65,7 +65,7 @@ BufferHandle ColumnDataAllocator::AllocateBlock(idx_t size) { BlockMetaData data; data.size = 0; data.capacity = block_size; - auto pin = alloc.buffer_manager->Allocate(block_size, false, &data.handle); + auto pin = alloc.buffer_manager->Allocate(MemoryTag::COLUMN_DATA, block_size, false, &data.handle); blocks.push_back(std::move(data)); return pin; } diff --git a/src/duckdb/src/common/types/row/row_data_collection.cpp b/src/duckdb/src/common/types/row/row_data_collection.cpp index 47a12efc6..722992321 100644 --- a/src/duckdb/src/common/types/row/row_data_collection.cpp +++ b/src/duckdb/src/common/types/row/row_data_collection.cpp @@ -43,7 +43,7 @@ idx_t RowDataCollection::AppendToBlock(RowDataBlock &block, BufferHandle &handle } RowDataBlock &RowDataCollection::CreateBlock() { - blocks.push_back(make_uniq(buffer_manager, block_capacity, entry_size)); + blocks.push_back(make_uniq(MemoryTag::ORDER_BY, buffer_manager, block_capacity, entry_size)); return *blocks.back(); } diff --git a/src/duckdb/src/common/types/row/row_data_collection_scanner.cpp b/src/duckdb/src/common/types/row/row_data_collection_scanner.cpp index a7b4e2dbf..50135d887 100644 --- a/src/duckdb/src/common/types/row/row_data_collection_scanner.cpp +++ b/src/duckdb/src/common/types/row/row_data_collection_scanner.cpp @@ -99,8 +99,8 @@ void RowDataCollectionScanner::AlignHeapBlocks(RowDataCollection &swizzled_block } // Finally, we allocate a new heap block and copy data to it - swizzled_string_heap.blocks.emplace_back( - make_uniq(buffer_manager, MaxValue(total_size, (idx_t)Storage::BLOCK_SIZE), 1)); + swizzled_string_heap.blocks.emplace_back(make_uniq( + MemoryTag::ORDER_BY, buffer_manager, MaxValue(total_size, (idx_t)Storage::BLOCK_SIZE), 1)); auto new_heap_handle = buffer_manager.Pin(swizzled_string_heap.blocks.back()->block); auto new_heap_ptr = new_heap_handle.Ptr(); for (auto &ptr_and_size : ptrs_and_sizes) { diff --git a/src/duckdb/src/common/types/row/tuple_data_allocator.cpp b/src/duckdb/src/common/types/row/tuple_data_allocator.cpp index ce5c4b522..6c92c7b22 100644 --- a/src/duckdb/src/common/types/row/tuple_data_allocator.cpp +++ b/src/duckdb/src/common/types/row/tuple_data_allocator.cpp @@ -9,7 +9,7 @@ namespace duckdb { using ValidityBytes = TupleDataLayout::ValidityBytes; TupleDataBlock::TupleDataBlock(BufferManager &buffer_manager, idx_t capacity_p) : capacity(capacity_p), size(0) { - buffer_manager.Allocate(capacity, false, &handle); + buffer_manager.Allocate(MemoryTag::HASH_TABLE, capacity, false, &handle); } TupleDataBlock::TupleDataBlock(TupleDataBlock &&other) noexcept { diff --git a/src/duckdb/src/common/types/row/tuple_data_collection.cpp b/src/duckdb/src/common/types/row/tuple_data_collection.cpp index f8ab6df14..745d2f820 100644 --- a/src/duckdb/src/common/types/row/tuple_data_collection.cpp +++ b/src/duckdb/src/common/types/row/tuple_data_collection.cpp @@ -160,6 +160,20 @@ void TupleDataCollection::InitializeChunkState(TupleDataChunkState &chunk_state, GetAllColumnIDsInternal(column_ids, types.size()); } InitializeVectorFormat(chunk_state.vector_data, types); + + for (auto &col : column_ids) { + auto &type = types[col]; + if (type.Contains(LogicalTypeId::ARRAY)) { + auto cast_type = ArrayType::ConvertToList(type); + chunk_state.cached_cast_vector_cache.push_back( + make_uniq(Allocator::DefaultAllocator(), cast_type)); + chunk_state.cached_cast_vectors.push_back(make_uniq(*chunk_state.cached_cast_vector_cache.back())); + } else { + chunk_state.cached_cast_vectors.emplace_back(); + chunk_state.cached_cast_vector_cache.emplace_back(); + } + } + chunk_state.column_ids = std::move(column_ids); } @@ -260,9 +274,6 @@ static inline void ToUnifiedFormatInternal(TupleDataVectorFormat &format, Vector } format.unified.data = reinterpret_cast(format.array_list_entries.get()); - // Set the array size in the child format - format.children[0].parent_array_size = array_size; - ToUnifiedFormatInternal(format.children[0], ArrayVector::GetEntry(vector), ArrayVector::GetTotalSize(vector)); } break; default: @@ -419,6 +430,23 @@ void TupleDataCollection::InitializeScan(TupleDataScanState &state, vector(Allocator::DefaultAllocator(), cast_type)); + chunk_state.cached_cast_vectors.push_back(make_uniq(*chunk_state.cached_cast_vector_cache.back())); + } else { + chunk_state.cached_cast_vectors.emplace_back(); + chunk_state.cached_cast_vector_cache.emplace_back(); + } + } + state.chunk_state.column_ids = std::move(column_ids); } @@ -506,7 +534,6 @@ bool TupleDataCollection::NextScanIndex(TupleDataScanState &state, idx_t &segmen chunk_index = state.chunk_index++; return true; } - void TupleDataCollection::ScanAtIndex(TupleDataPinState &pin_state, TupleDataChunkState &chunk_state, const vector &column_ids, idx_t segment_index, idx_t chunk_index, DataChunk &result) { @@ -514,8 +541,14 @@ void TupleDataCollection::ScanAtIndex(TupleDataPinState &pin_state, TupleDataChu auto &chunk = segment.chunks[chunk_index]; segment.allocator->InitializeChunkState(segment, pin_state, chunk_state, chunk_index, false); result.Reset(); + + for (idx_t i = 0; i < column_ids.size(); i++) { + if (chunk_state.cached_cast_vectors[i]) { + chunk_state.cached_cast_vectors[i]->ResetFromCache(*chunk_state.cached_cast_vector_cache[i]); + } + } Gather(chunk_state.row_locations, *FlatVector::IncrementalSelectionVector(), chunk.count, column_ids, result, - *FlatVector::IncrementalSelectionVector()); + *FlatVector::IncrementalSelectionVector(), chunk_state.cached_cast_vectors); result.SetCardinality(chunk.count); } diff --git a/src/duckdb/src/common/types/row/tuple_data_scatter_gather.cpp b/src/duckdb/src/common/types/row/tuple_data_scatter_gather.cpp index 06bccdb01..998a552e8 100644 --- a/src/duckdb/src/common/types/row/tuple_data_scatter_gather.cpp +++ b/src/duckdb/src/common/types/row/tuple_data_scatter_gather.cpp @@ -814,7 +814,7 @@ static void TupleDataStructWithinCollectionScatter(const Vector &source, const T } } -template +template static void TupleDataCollectionWithinCollectionScatter(const Vector &child_list, const TupleDataVectorFormat &child_list_format, const SelectionVector &append_sel, const idx_t append_count, @@ -870,7 +870,7 @@ static void TupleDataCollectionWithinCollectionScatter(const Vector &child_list, // Recurse D_ASSERT(child_functions.size() == 1); - auto &child_vec = COLLECTION::GetEntry(child_list); + auto &child_vec = COLLECTION_VECTOR::GetEntry(child_list); auto &child_format = child_list_format.children[0]; auto &combined_child_list_data = child_format.combined_list_data->combined_data; const auto &child_function = child_functions[0]; @@ -882,20 +882,11 @@ static void TupleDataCollectionWithinCollectionScatter(const Vector &child_list, // Get Scatter Function //------------------------------------------------------------------------------ template -tuple_data_scatter_function_t TupleDataGetScatterFunction(WithinCollection within_collection) { - switch (within_collection) { - case WithinCollection::NO: - return TupleDataTemplatedScatter; - case WithinCollection::ARRAY: - case WithinCollection::LIST: - return TupleDataTemplatedWithinCollectionScatter; - default: - throw NotImplementedException("Unimplemented within collection type"); - } +tuple_data_scatter_function_t TupleDataGetScatterFunction(bool within_collection) { + return within_collection ? TupleDataTemplatedWithinCollectionScatter : TupleDataTemplatedScatter; } -TupleDataScatterFunction TupleDataCollection::GetScatterFunction(const LogicalType &type, - WithinCollection within_collection) { +TupleDataScatterFunction TupleDataCollection::GetScatterFunction(const LogicalType &type, bool within_collection) { TupleDataScatterFunction result; switch (type.InternalType()) { case PhysicalType::BOOL: @@ -944,53 +935,21 @@ TupleDataScatterFunction TupleDataCollection::GetScatterFunction(const LogicalTy result.function = TupleDataGetScatterFunction(within_collection); break; case PhysicalType::STRUCT: { - switch (within_collection) { - case WithinCollection::NO: - result.function = TupleDataStructScatter; - break; - case WithinCollection::LIST: - case WithinCollection::ARRAY: - result.function = TupleDataStructWithinCollectionScatter; - break; - default: - throw NotImplementedException("Unimplemented within collection type"); - } + result.function = within_collection ? TupleDataStructWithinCollectionScatter : TupleDataStructScatter; for (const auto &child_type : StructType::GetChildTypes(type)) { result.child_functions.push_back(GetScatterFunction(child_type.second, within_collection)); } break; } case PhysicalType::LIST: - switch (within_collection) { - case WithinCollection::NO: - result.function = TupleDataListScatter; - break; - case WithinCollection::LIST: - result.function = TupleDataCollectionWithinCollectionScatter; - break; - case WithinCollection::ARRAY: - result.function = TupleDataCollectionWithinCollectionScatter; - break; - default: - throw NotImplementedException("Unimplemented within collection type"); - } - result.child_functions.emplace_back(GetScatterFunction(ListType::GetChildType(type), WithinCollection::LIST)); + result.function = + within_collection ? TupleDataCollectionWithinCollectionScatter : TupleDataListScatter; + result.child_functions.emplace_back(GetScatterFunction(ListType::GetChildType(type), true)); break; case PhysicalType::ARRAY: - switch (within_collection) { - case WithinCollection::NO: - result.function = TupleDataArrayScatter; - break; - case WithinCollection::LIST: - result.function = TupleDataCollectionWithinCollectionScatter; - break; - case WithinCollection::ARRAY: - result.function = TupleDataCollectionWithinCollectionScatter; - break; - default: - throw NotImplementedException("Unimplemented within collection type"); - } - result.child_functions.emplace_back(GetScatterFunction(ArrayType::GetChildType(type), WithinCollection::ARRAY)); + result.function = + within_collection ? TupleDataCollectionWithinCollectionScatter : TupleDataArrayScatter; + result.child_functions.emplace_back(GetScatterFunction(ArrayType::GetChildType(type), true)); break; default: throw InternalException("Unsupported type for TupleDataCollection::GetScatterFunction"); @@ -1003,35 +962,39 @@ TupleDataScatterFunction TupleDataCollection::GetScatterFunction(const LogicalTy //------------------------------------------------------------------------------- void TupleDataCollection::Gather(Vector &row_locations, const SelectionVector &scan_sel, const idx_t scan_count, - DataChunk &result, const SelectionVector &target_sel) const { + DataChunk &result, const SelectionVector &target_sel, + vector> &cached_cast_vectors) const { D_ASSERT(result.ColumnCount() == layout.ColumnCount()); vector column_ids; column_ids.reserve(layout.ColumnCount()); for (idx_t col_idx = 0; col_idx < layout.ColumnCount(); col_idx++) { column_ids.emplace_back(col_idx); } - Gather(row_locations, scan_sel, scan_count, column_ids, result, target_sel); + Gather(row_locations, scan_sel, scan_count, column_ids, result, target_sel, cached_cast_vectors); } void TupleDataCollection::Gather(Vector &row_locations, const SelectionVector &scan_sel, const idx_t scan_count, const vector &column_ids, DataChunk &result, - const SelectionVector &target_sel) const { + const SelectionVector &target_sel, + vector> &cached_cast_vectors) const { for (idx_t col_idx = 0; col_idx < column_ids.size(); col_idx++) { - Gather(row_locations, scan_sel, scan_count, column_ids[col_idx], result.data[col_idx], target_sel); + Gather(row_locations, scan_sel, scan_count, column_ids[col_idx], result.data[col_idx], target_sel, + cached_cast_vectors[col_idx].get()); } } void TupleDataCollection::Gather(Vector &row_locations, const SelectionVector &scan_sel, const idx_t scan_count, - const column_t column_id, Vector &result, const SelectionVector &target_sel) const { + const column_t column_id, Vector &result, const SelectionVector &target_sel, + optional_ptr cached_cast_vector) const { const auto &gather_function = gather_functions[column_id]; - gather_function.function(layout, row_locations, column_id, scan_sel, scan_count, result, target_sel, result, - gather_function.child_functions); + gather_function.function(layout, row_locations, column_id, scan_sel, scan_count, result, target_sel, + cached_cast_vector, gather_function.child_functions); } template static void TupleDataTemplatedGather(const TupleDataLayout &layout, Vector &row_locations, const idx_t col_idx, const SelectionVector &scan_sel, const idx_t scan_count, Vector &target, - const SelectionVector &target_sel, Vector &dummy_vector, + const SelectionVector &target_sel, optional_ptr dummy_vector, const vector &child_functions) { // Source auto source_locations = FlatVector::GetData(row_locations); @@ -1060,7 +1023,7 @@ static void TupleDataTemplatedGather(const TupleDataLayout &layout, Vector &row_ static void TupleDataStructGather(const TupleDataLayout &layout, Vector &row_locations, const idx_t col_idx, const SelectionVector &scan_sel, const idx_t scan_count, Vector &target, - const SelectionVector &target_sel, Vector &dummy_vector, + const SelectionVector &target_sel, optional_ptr dummy_vector, const vector &child_functions) { // Source auto source_locations = FlatVector::GetData(row_locations); @@ -1112,7 +1075,7 @@ static void TupleDataStructGather(const TupleDataLayout &layout, Vector &row_loc //------------------------------------------------------------------------------ static void TupleDataListGather(const TupleDataLayout &layout, Vector &row_locations, const idx_t col_idx, const SelectionVector &scan_sel, const idx_t scan_count, Vector &target, - const SelectionVector &target_sel, Vector &dummy_vector, + const SelectionVector &target_sel, optional_ptr dummy_vector, const vector &child_functions) { // Source auto source_locations = FlatVector::GetData(row_locations); @@ -1163,72 +1126,7 @@ static void TupleDataListGather(const TupleDataLayout &layout, Vector &row_locat D_ASSERT(child_functions.size() == 1); const auto &child_function = child_functions[0]; child_function.function(layout, heap_locations, list_size_before, scan_sel, scan_count, - ListVector::GetEntry(target), target_sel, target, child_function.child_functions); -} - -static void TupleDataArrayGather(const TupleDataLayout &layout, Vector &row_locations, const idx_t col_idx, - const SelectionVector &scan_sel, const idx_t scan_count, Vector &target, - const SelectionVector &target_sel, Vector &dummy_vector, - const vector &child_functions) { - // Source - auto source_locations = FlatVector::GetData(row_locations); - - // Setup fake list_entry_t's for the target - ArrayVector::AllocateDummyListEntries(target); - - // Target - auto target_list_entries = FlatVector::GetData(target); - auto &target_validity = FlatVector::Validity(target); - auto &target_child = ArrayVector::GetEntry(target); - auto &target_child_validity = FlatVector::Validity(target_child); - auto target_array_size = ArrayType::GetSize(target.GetType()); - - // Precompute mask indexes - idx_t entry_idx; - idx_t idx_in_entry; - ValidityBytes::GetEntryIndex(col_idx, entry_idx, idx_in_entry); - - // Load pointers to the data from the row - Vector heap_locations(LogicalType::POINTER); - auto source_heap_locations = FlatVector::GetData(heap_locations); - auto &source_heap_validity = FlatVector::Validity(heap_locations); - - const auto offset_in_row = layout.GetOffsets()[col_idx]; - uint64_t target_list_offset = 0; - for (idx_t i = 0; i < scan_count; i++) { - const auto source_idx = scan_sel.get_index(i); - const auto target_idx = target_sel.get_index(i); - - const auto &source_row = source_locations[source_idx]; - ValidityBytes row_mask(source_row); - if (row_mask.RowIsValid(row_mask.GetValidityEntry(entry_idx), idx_in_entry)) { - auto &source_heap_location = source_heap_locations[source_idx]; - source_heap_location = Load(source_row + offset_in_row); - - // Load list size and skip over - const auto list_length = Load(source_heap_location); - source_heap_location += sizeof(uint64_t); - - // Initialize list entry, and increment offset - target_list_entries[target_idx] = {target_list_offset, list_length}; - target_list_offset += list_length; - } else { - source_heap_validity.SetInvalid(source_idx); - target_validity.SetInvalid(target_idx); - // We also need to invalidate the corresponding elements in the child array. - for (idx_t elem_idx = 0; elem_idx < target_array_size; elem_idx++) { - target_child_validity.SetInvalid(target_idx * target_array_size + elem_idx); - } - } - } - - auto list_size_before = 0; - - // Recurse - D_ASSERT(child_functions.size() == 1); - const auto &child_function = child_functions[0]; - child_function.function(layout, heap_locations, list_size_before, scan_sel, scan_count, target_child, target_sel, - target, child_function.child_functions); + ListVector::GetEntry(target), target_sel, &target, child_function.child_functions); } //------------------------------------------------------------------------------ @@ -1238,7 +1136,8 @@ template static void TupleDataTemplatedWithinCollectionGather(const TupleDataLayout &layout, Vector &heap_locations, const idx_t list_size_before, const SelectionVector &scan_sel, const idx_t scan_count, Vector &target, - const SelectionVector &target_sel, Vector &list_vector, + const SelectionVector &target_sel, + optional_ptr list_vector, const vector &child_functions) { // Source auto source_heap_locations = FlatVector::GetData(heap_locations); @@ -1249,7 +1148,7 @@ static void TupleDataTemplatedWithinCollectionGather(const TupleDataLayout &layo auto &target_validity = FlatVector::Validity(target); // List parent - const auto list_entries = FlatVector::GetData(list_vector); + const auto list_entries = FlatVector::GetData(*list_vector); uint64_t target_offset = list_size_before; for (idx_t i = 0; i < scan_count; i++) { @@ -1285,7 +1184,7 @@ static void TupleDataTemplatedWithinCollectionGather(const TupleDataLayout &layo static void TupleDataStructWithinCollectionGather(const TupleDataLayout &layout, Vector &heap_locations, const idx_t list_size_before, const SelectionVector &scan_sel, const idx_t scan_count, Vector &target, - const SelectionVector &target_sel, Vector &list_vector, + const SelectionVector &target_sel, optional_ptr list_vector, const vector &child_functions) { // Source auto source_heap_locations = FlatVector::GetData(heap_locations); @@ -1295,7 +1194,7 @@ static void TupleDataStructWithinCollectionGather(const TupleDataLayout &layout, auto &target_validity = FlatVector::Validity(target); // List parent - const auto list_entries = FlatVector::GetData(list_vector); + const auto list_entries = FlatVector::GetData(*list_vector); uint64_t target_offset = list_size_before; for (idx_t i = 0; i < scan_count; i++) { @@ -1330,78 +1229,23 @@ static void TupleDataStructWithinCollectionGather(const TupleDataLayout &layout, } } -template -struct CollectionVector { - static inline void Setup(Vector &collection) = delete; - static inline idx_t GetSize(Vector &collection) = delete; - static inline idx_t GetSizeBefore(Vector &collection) = delete; - static inline Vector &GetEntry(Vector &collection) = delete; - static inline void Reserve(Vector &collection, const idx_t new_capacity) = delete; - static inline void SetSize(Vector &collection, const idx_t new_size) = delete; -}; - -template <> -struct CollectionVector { - static inline void Setup(Vector &collection) { - ArrayVector::AllocateDummyListEntries(collection); - } - static inline idx_t GetSize(Vector &collection) { - return ArrayVector::GetTotalSize(collection); - } - static inline idx_t GetSizeBefore(Vector &) { - return 0; - } - static inline Vector &GetEntry(Vector &collection) { - return ArrayVector::GetEntry(collection); - } - static inline void Reserve(Vector &, const idx_t) { - } - static inline void SetSize(Vector &, const idx_t) { - } -}; - -template <> -struct CollectionVector { - static inline void Setup(Vector &collection) { - // do nothing - } - static inline idx_t GetSize(Vector &collection) { - return ListVector::GetListSize(collection); - } - static inline idx_t GetSizeBefore(Vector &collection) { - return ListVector::GetListSize(collection); - } - static inline Vector &GetEntry(Vector &collection) { - return ListVector::GetEntry(collection); - } - static inline void Reserve(Vector &collection, const idx_t new_capacity) { - ListVector::Reserve(collection, new_capacity); - } - static inline void SetSize(Vector &collection, const idx_t new_size) { - ListVector::SetListSize(collection, new_size); - } -}; - -template static void TupleDataCollectionWithinCollectionGather(const TupleDataLayout &layout, Vector &heap_locations, const idx_t list_size_before, const SelectionVector &scan_sel, const idx_t scan_count, Vector &target, - const SelectionVector &target_sel, Vector &list_vector, + const SelectionVector &target_sel, + optional_ptr list_vector, const vector &child_functions) { // Source auto source_heap_locations = FlatVector::GetData(heap_locations); auto &source_heap_validity = FlatVector::Validity(heap_locations); - // Setup - CollectionVector::Setup(target); - // Target auto target_list_entries = FlatVector::GetData(target); auto &target_validity = FlatVector::Validity(target); - const auto child_list_size_before = CollectionVector::GetSizeBefore(target); + const auto child_list_size_before = ListVector::GetListSize(target); // List parent - const auto list_entries = FlatVector::GetData(list_vector); + const auto list_entries = FlatVector::GetData(*list_vector); // We need to create a vector that has the combined list sizes (hugeint_t has same size as list_entry_t) Vector combined_list_vector(LogicalType::HUGEINT); @@ -1448,36 +1292,69 @@ static void TupleDataCollectionWithinCollectionGather(const TupleDataLayout &lay target_offset += list_length; } - CollectionVector::Reserve(target, target_child_offset); - CollectionVector::SetSize(target, target_child_offset); + ListVector::Reserve(target, target_child_offset); + ListVector::SetListSize(target, target_child_offset); // Recurse D_ASSERT(child_functions.size() == 1); const auto &child_function = child_functions[0]; child_function.function(layout, heap_locations, child_list_size_before, scan_sel, scan_count, - COLLECTION::GetEntry(target), target_sel, combined_list_vector, + ListVector::GetEntry(target), target_sel, &combined_list_vector, child_function.child_functions); } +//------------------------------------------------------------------------------ +// Special cases for arrays +//------------------------------------------------------------------------------ +// A gather function that wraps another gather function and casts the result to the target array type +static void TupleDataCastToArrayListGather(const TupleDataLayout &layout, Vector &row_locations, const idx_t col_idx, + const SelectionVector &scan_sel, const idx_t scan_count, Vector &target, + const SelectionVector &target_sel, optional_ptr cached_cast_vector, + const vector &child_functions) { + + if (cached_cast_vector) { + // Reuse the cached cast vector + TupleDataListGather(layout, row_locations, col_idx, scan_sel, scan_count, *cached_cast_vector, target_sel, + cached_cast_vector, child_functions); + VectorOperations::DefaultCast(*cached_cast_vector, target, scan_count); + } else { + // Otherwise, create a new temporary cast vector + Vector cast_vector(ArrayType::ConvertToList(target.GetType())); + TupleDataListGather(layout, row_locations, col_idx, scan_sel, scan_count, cast_vector, target_sel, &cast_vector, + child_functions); + VectorOperations::DefaultCast(cast_vector, target, scan_count); + } +} + +static void TupleDataCastToArrayStructGather(const TupleDataLayout &layout, Vector &row_locations, const idx_t col_idx, + const SelectionVector &scan_sel, const idx_t scan_count, Vector &target, + const SelectionVector &target_sel, optional_ptr cached_cast_vector, + const vector &child_functions) { + + if (cached_cast_vector) { + // Reuse the cached cast vector + TupleDataStructGather(layout, row_locations, col_idx, scan_sel, scan_count, *cached_cast_vector, target_sel, + cached_cast_vector, child_functions); + VectorOperations::DefaultCast(*cached_cast_vector, target, scan_count); + } else { + // Otherwise, create a new temporary cast vector + Vector cast_vector(ArrayType::ConvertToList(target.GetType())); + TupleDataStructGather(layout, row_locations, col_idx, scan_sel, scan_count, cast_vector, target_sel, + &cast_vector, child_functions); + VectorOperations::DefaultCast(cast_vector, target, scan_count); + } +} + //------------------------------------------------------------------------------ // Get Gather Function //------------------------------------------------------------------------------ template -tuple_data_gather_function_t TupleDataGetGatherFunction(WithinCollection within_collection) { - switch (within_collection) { - case WithinCollection::NO: - return TupleDataTemplatedGather; - case WithinCollection::LIST: - case WithinCollection::ARRAY: - return TupleDataTemplatedWithinCollectionGather; - default: - throw NotImplementedException("Unimplemented collection type"); - } +tuple_data_gather_function_t TupleDataGetGatherFunction(bool within_collection) { + return within_collection ? TupleDataTemplatedWithinCollectionGather : TupleDataTemplatedGather; } -TupleDataGatherFunction TupleDataCollection::GetGatherFunction(const LogicalType &type, - WithinCollection within_collection) { +static TupleDataGatherFunction TupleDataGetGatherFunctionInternal(const LogicalType &type, bool within_collection) { TupleDataGatherFunction result; switch (type.InternalType()) { case PhysicalType::BOOL: @@ -1526,49 +1403,19 @@ TupleDataGatherFunction TupleDataCollection::GetGatherFunction(const LogicalType result.function = TupleDataGetGatherFunction(within_collection); break; case PhysicalType::STRUCT: { - switch (within_collection) { - case WithinCollection::NO: - result.function = TupleDataStructGather; - break; - case WithinCollection::LIST: - case WithinCollection::ARRAY: - result.function = TupleDataStructWithinCollectionGather; - break; - default: - throw NotImplementedException("Unimplemented collection type"); - } + result.function = within_collection ? TupleDataStructWithinCollectionGather : TupleDataStructGather; for (const auto &child_type : StructType::GetChildTypes(type)) { - result.child_functions.push_back(GetGatherFunction(child_type.second, within_collection)); + result.child_functions.push_back(TupleDataGetGatherFunctionInternal(child_type.second, within_collection)); } break; } case PhysicalType::LIST: - switch (within_collection) { - case WithinCollection::NO: - result.function = TupleDataListGather; - break; - case WithinCollection::LIST: - case WithinCollection::ARRAY: - result.function = TupleDataCollectionWithinCollectionGather; - break; - default: - throw NotImplementedException("Unimplemented collection type"); - } - result.child_functions.push_back(GetGatherFunction(ListType::GetChildType(type), WithinCollection::LIST)); + result.function = within_collection ? TupleDataCollectionWithinCollectionGather : TupleDataListGather; + result.child_functions.push_back(TupleDataGetGatherFunctionInternal(ListType::GetChildType(type), true)); break; case PhysicalType::ARRAY: - switch (within_collection) { - case WithinCollection::NO: - result.function = TupleDataArrayGather; - break; - case WithinCollection::LIST: - case WithinCollection::ARRAY: - result.function = TupleDataCollectionWithinCollectionGather; - break; - default: - throw NotImplementedException("Unimplemented collection type"); - } - result.child_functions.push_back(GetGatherFunction(ArrayType::GetChildType(type), WithinCollection::ARRAY)); + result.function = within_collection ? TupleDataCollectionWithinCollectionGather : TupleDataListGather; + result.child_functions.push_back(TupleDataGetGatherFunctionInternal(ArrayType::GetChildType(type), true)); break; default: throw InternalException("Unsupported type for TupleDataCollection::GetGatherFunction"); @@ -1576,4 +1423,32 @@ TupleDataGatherFunction TupleDataCollection::GetGatherFunction(const LogicalType return result; } +TupleDataGatherFunction TupleDataCollection::GetGatherFunction(const LogicalType &type) { + if (!type.IsNested()) { + return TupleDataGetGatherFunctionInternal(type, false); + } + + if (type.Contains(LogicalTypeId::ARRAY)) { + // Special case: we cant handle arrays yet, so we need to replace them with lists when gathering + auto new_type = ArrayType::ConvertToList(type); + TupleDataGatherFunction result; + // Theres only two cases: Either the array is within a struct, or it is within a list (or has now become a list) + if (new_type.InternalType() == PhysicalType::LIST) { + result.function = TupleDataCastToArrayListGather; + result.child_functions.push_back( + TupleDataGetGatherFunctionInternal(ListType::GetChildType(new_type), true)); + return result; + } else if (new_type.InternalType() == PhysicalType::STRUCT) { + result.function = TupleDataCastToArrayStructGather; + for (const auto &child_type : StructType::GetChildTypes(new_type)) { + result.child_functions.push_back(TupleDataGetGatherFunctionInternal(child_type.second, false)); + } + return result; + } else { + throw InternalException("Unsupported type for TupleDataCollection::GetGatherFunction"); + } + } + return TupleDataGetGatherFunctionInternal(type, false); +} + } // namespace duckdb diff --git a/src/duckdb/src/common/types/time.cpp b/src/duckdb/src/common/types/time.cpp index 8ef18c2f2..7e37ab2a5 100644 --- a/src/duckdb/src/common/types/time.cpp +++ b/src/duckdb/src/common/types/time.cpp @@ -54,6 +54,10 @@ bool Time::TryConvertInternal(const char *buf, idx_t len, idx_t &pos, dtime_t &r } } + if (pos >= len) { + return false; + } + // fetch the separator sep = buf[pos++]; if (sep != ':') { diff --git a/src/duckdb/src/common/types/vector.cpp b/src/duckdb/src/common/types/vector.cpp index 2884ea911..ec3139c69 100644 --- a/src/duckdb/src/common/types/vector.cpp +++ b/src/duckdb/src/common/types/vector.cpp @@ -28,7 +28,7 @@ namespace duckdb { Vector::Vector(LogicalType type_p, bool create_data, bool zero_data, idx_t capacity) - : vector_type(VectorType::FLAT_VECTOR), type(std::move(type_p)), data(nullptr) { + : vector_type(VectorType::FLAT_VECTOR), type(std::move(type_p)), data(nullptr), validity(capacity) { if (create_data) { Initialize(zero_data, capacity); } @@ -909,6 +909,8 @@ void Vector::Flatten(idx_t count) { // | 2 | // ... + child.Flatten(count * array_size); + // Create a selection vector SelectionVector sel(count * array_size); for (idx_t array_idx = 0; array_idx < count; array_idx++) { @@ -2347,12 +2349,4 @@ idx_t ArrayVector::GetTotalSize(const Vector &vector) { return vector.auxiliary->Cast().GetChildSize(); } -void ArrayVector::AllocateDummyListEntries(Vector &vector) { - D_ASSERT(vector.GetType().InternalType() == PhysicalType::ARRAY); - auto array_size = ArrayType::GetSize(vector.GetType()); - auto array_count = ArrayVector::GetTotalSize(vector) / array_size; - vector.buffer = VectorBuffer::CreateStandardVector(LogicalType::HUGEINT, array_count); - vector.data = vector.buffer->GetData(); -} - } // namespace duckdb diff --git a/src/duckdb/src/common/types/vector_cache.cpp b/src/duckdb/src/common/types/vector_cache.cpp index d7f3121a6..c0ea6fa7c 100644 --- a/src/duckdb/src/common/types/vector_cache.cpp +++ b/src/duckdb/src/common/types/vector_cache.cpp @@ -49,7 +49,7 @@ class VectorCacheBuffer : public VectorBuffer { auto internal_type = type.InternalType(); result.vector_type = VectorType::FLAT_VECTOR; AssignSharedPointer(result.buffer, buffer); - result.validity.Reset(); + result.validity.Reset(capacity); switch (internal_type) { case PhysicalType::LIST: { result.data = owned_data.get(); @@ -77,10 +77,6 @@ class VectorCacheBuffer : public VectorBuffer { auto &child_cache = child_caches[0]->Cast(); auto &array_child = result.auxiliary->Cast().GetChild(); child_cache.ResetFromCache(array_child, child_caches[0]); - - // Ensure the child validity is (will be) large enough, even if its not initialized. - auto validity_target_size = array_child.validity.TargetCount(); - array_child.validity.Resize(validity_target_size, std::max(validity_target_size, child_cache.capacity)); break; } case PhysicalType::STRUCT: { diff --git a/src/duckdb/src/execution/aggregate_hashtable.cpp b/src/duckdb/src/execution/aggregate_hashtable.cpp index 8a15d15e3..bf7b7f998 100644 --- a/src/duckdb/src/execution/aggregate_hashtable.cpp +++ b/src/duckdb/src/execution/aggregate_hashtable.cpp @@ -482,7 +482,7 @@ struct FlushMoveState { bool Scan() { if (collection.Scan(scan_state, groups)) { collection.Gather(scan_state.chunk_state.row_locations, *FlatVector::IncrementalSelectionVector(), - groups.size(), hash_col_idx, hashes, *FlatVector::IncrementalSelectionVector()); + groups.size(), hash_col_idx, hashes, *FlatVector::IncrementalSelectionVector(), nullptr); return true; } diff --git a/src/duckdb/src/execution/index/fixed_size_buffer.cpp b/src/duckdb/src/execution/index/fixed_size_buffer.cpp index 16ed4711c..0dd86bd49 100644 --- a/src/duckdb/src/execution/index/fixed_size_buffer.cpp +++ b/src/duckdb/src/execution/index/fixed_size_buffer.cpp @@ -40,7 +40,7 @@ FixedSizeBuffer::FixedSizeBuffer(BlockManager &block_manager) block_handle(nullptr) { auto &buffer_manager = block_manager.buffer_manager; - buffer_handle = buffer_manager.Allocate(Storage::BLOCK_SIZE, false, &block_handle); + buffer_handle = buffer_manager.Allocate(MemoryTag::ART_INDEX, Storage::BLOCK_SIZE, false, &block_handle); } FixedSizeBuffer::FixedSizeBuffer(BlockManager &block_manager, const idx_t segment_count, const idx_t allocation_size, @@ -123,7 +123,6 @@ void FixedSizeBuffer::Serialize(PartialBlockManager &partial_block_manager, cons } void FixedSizeBuffer::Pin() { - auto &buffer_manager = block_manager.buffer_manager; D_ASSERT(block_pointer.IsValid()); D_ASSERT(block_handle && block_handle->BlockId() < MAXIMUM_BLOCK); @@ -133,7 +132,8 @@ void FixedSizeBuffer::Pin() { // we need to copy the (partial) data into a new (not yet disk-backed) buffer handle shared_ptr new_block_handle; - auto new_buffer_handle = buffer_manager.Allocate(Storage::BLOCK_SIZE, false, &new_block_handle); + auto new_buffer_handle = + buffer_manager.Allocate(MemoryTag::ART_INDEX, Storage::BLOCK_SIZE, false, &new_block_handle); memcpy(new_buffer_handle.Ptr(), buffer_handle.Ptr() + block_pointer.offset, allocation_size); diff --git a/src/duckdb/src/execution/join_hashtable.cpp b/src/duckdb/src/execution/join_hashtable.cpp index 038393bdc..2b74b5bd7 100644 --- a/src/duckdb/src/execution/join_hashtable.cpp +++ b/src/duckdb/src/execution/join_hashtable.cpp @@ -290,12 +290,9 @@ void JoinHashTable::InitializePointerTable() { if (hash_map.get()) { // There is already a hash map auto current_capacity = hash_map.GetSize() / sizeof(data_ptr_t); - if (capacity > current_capacity) { - // Need more space + if (capacity != current_capacity) { + // Different size, re-allocate hash_map = buffer_manager.GetBufferAllocator().Allocate(capacity * sizeof(data_ptr_t)); - } else { - // Just use the current hash map - capacity = current_capacity; } } else { // Allocate a hash map @@ -485,7 +482,7 @@ void ScanStructure::AdvancePointers() { void ScanStructure::GatherResult(Vector &result, const SelectionVector &result_vector, const SelectionVector &sel_vector, const idx_t count, const idx_t col_no) { - ht.data_collection->Gather(pointers, sel_vector, count, col_no, result, result_vector); + ht.data_collection->Gather(pointers, sel_vector, count, col_no, result, result_vector, nullptr); } void ScanStructure::GatherResult(Vector &result, const SelectionVector &sel_vector, const idx_t count, @@ -852,7 +849,7 @@ void JoinHashTable::ScanFullOuter(JoinHTScanState &state, Vector &addresses, Dat auto &vector = result.data[left_column_count + i]; const auto output_col_idx = output_columns[i]; D_ASSERT(vector.GetType() == layout.GetTypes()[output_col_idx]); - data_collection->Gather(addresses, sel_vector, found_entries, output_col_idx, vector, sel_vector); + data_collection->Gather(addresses, sel_vector, found_entries, output_col_idx, vector, sel_vector, nullptr); } } diff --git a/src/duckdb/src/execution/operator/aggregate/physical_hash_aggregate.cpp b/src/duckdb/src/execution/operator/aggregate/physical_hash_aggregate.cpp index e6bd35875..f1cc27a01 100644 --- a/src/duckdb/src/execution/operator/aggregate/physical_hash_aggregate.cpp +++ b/src/duckdb/src/execution/operator/aggregate/physical_hash_aggregate.cpp @@ -2,6 +2,7 @@ #include "duckdb/catalog/catalog_entry/aggregate_function_catalog_entry.hpp" #include "duckdb/common/atomic.hpp" +#include "duckdb/common/optional_idx.hpp" #include "duckdb/common/vector_operations/vector_operations.hpp" #include "duckdb/execution/aggregate_hashtable.hpp" #include "duckdb/execution/operator/aggregate/distinct_aggregate_data.hpp" @@ -14,7 +15,6 @@ #include "duckdb/planner/expression/bound_aggregate_expression.hpp" #include "duckdb/planner/expression/bound_constant_expression.hpp" #include "duckdb/planner/expression/bound_reference_expression.hpp" -#include "duckdb/common/optional_idx.hpp" namespace duckdb { @@ -565,9 +565,10 @@ class HashAggregateDistinctFinalizeTask : public ExecutorTask { }; void HashAggregateDistinctFinalizeEvent::Schedule() { - const auto n_threads = CreateGlobalSources(); + auto n_tasks = CreateGlobalSources(); + n_tasks = MinValue(n_tasks, TaskScheduler::GetScheduler(context).NumberOfThreads()); vector> tasks; - for (idx_t i = 0; i < n_threads; i++) { + for (idx_t i = 0; i < n_tasks; i++) { tasks.push_back(make_uniq(*pipeline, shared_from_this(), op, gstate)); } SetTasks(std::move(tasks)); @@ -577,7 +578,7 @@ idx_t HashAggregateDistinctFinalizeEvent::CreateGlobalSources() { auto &aggregates = op.grouped_aggregate_data.aggregates; global_source_states.reserve(op.groupings.size()); - idx_t n_threads = 0; + idx_t n_tasks = 0; for (idx_t grouping_idx = 0; grouping_idx < op.groupings.size(); grouping_idx++) { auto &grouping = op.groupings[grouping_idx]; auto &distinct_state = *gstate.grouping_states[grouping_idx].distinct_state; @@ -597,13 +598,13 @@ idx_t HashAggregateDistinctFinalizeEvent::CreateGlobalSources() { auto table_idx = distinct_data.info.table_map.at(agg_idx); auto &radix_table_p = distinct_data.radix_tables[table_idx]; - n_threads += radix_table_p->MaxThreads(*distinct_state.radix_states[table_idx]); + n_tasks += radix_table_p->MaxThreads(*distinct_state.radix_states[table_idx]); aggregate_sources.push_back(radix_table_p->GetGlobalSourceState(context)); } global_source_states.push_back(std::move(aggregate_sources)); } - return MaxValue(n_threads, 1); + return MaxValue(n_tasks, 1); } void HashAggregateDistinctFinalizeEvent::FinishEvent() { diff --git a/src/duckdb/src/execution/operator/aggregate/physical_ungrouped_aggregate.cpp b/src/duckdb/src/execution/operator/aggregate/physical_ungrouped_aggregate.cpp index 693d17a03..2401e99c8 100644 --- a/src/duckdb/src/execution/operator/aggregate/physical_ungrouped_aggregate.cpp +++ b/src/duckdb/src/execution/operator/aggregate/physical_ungrouped_aggregate.cpp @@ -431,7 +431,7 @@ void UngroupedDistinctAggregateFinalizeEvent::Schedule() { auto &aggregates = op.aggregates; auto &distinct_data = *op.distinct_data; - idx_t n_threads = 0; + idx_t n_tasks = 0; idx_t payload_idx = 0; idx_t next_payload_idx = 0; for (idx_t agg_idx = 0; agg_idx < aggregates.size(); agg_idx++) { @@ -451,13 +451,14 @@ void UngroupedDistinctAggregateFinalizeEvent::Schedule() { // Create global state for scanning auto table_idx = distinct_data.info.table_map.at(agg_idx); auto &radix_table_p = *distinct_data.radix_tables[table_idx]; - n_threads += radix_table_p.MaxThreads(*gstate.distinct_state->radix_states[table_idx]); + n_tasks += radix_table_p.MaxThreads(*gstate.distinct_state->radix_states[table_idx]); global_source_states.push_back(radix_table_p.GetGlobalSourceState(context)); } - n_threads = MaxValue(n_threads, 1); + n_tasks = MaxValue(n_tasks, 1); + n_tasks = MinValue(n_tasks, TaskScheduler::GetScheduler(context).NumberOfThreads()); vector> tasks; - for (idx_t i = 0; i < n_threads; i++) { + for (idx_t i = 0; i < n_tasks; i++) { tasks.push_back( make_uniq(pipeline->executor, shared_from_this(), op, gstate)); tasks_scheduled++; diff --git a/src/duckdb/src/execution/operator/csv_scanner/buffer_manager/csv_buffer.cpp b/src/duckdb/src/execution/operator/csv_scanner/buffer_manager/csv_buffer.cpp index 5c4f2e9ce..8c29ae79f 100644 --- a/src/duckdb/src/execution/operator/csv_scanner/buffer_manager/csv_buffer.cpp +++ b/src/duckdb/src/execution/operator/csv_scanner/buffer_manager/csv_buffer.cpp @@ -31,7 +31,14 @@ CSVBuffer::CSVBuffer(CSVFileHandle &file_handle, ClientContext &context, idx_t b last_buffer = file_handle.FinishedReading(); } -shared_ptr CSVBuffer::Next(CSVFileHandle &file_handle, idx_t buffer_size, idx_t file_number_p) { +shared_ptr CSVBuffer::Next(CSVFileHandle &file_handle, idx_t buffer_size, idx_t file_number_p, + bool &has_seaked) { + if (has_seaked) { + // This means that at some point a reload was done, and we are currently on the incorrect position in our file + // handle + file_handle.Seek(global_csv_start + actual_buffer_size); + has_seaked = false; + } auto next_csv_buffer = make_shared(file_handle, context, buffer_size, global_csv_start + actual_buffer_size, file_number_p, buffer_idx + 1); if (next_csv_buffer->GetBufferSize() == 0) { @@ -44,7 +51,8 @@ shared_ptr CSVBuffer::Next(CSVFileHandle &file_handle, idx_t buffer_s void CSVBuffer::AllocateBuffer(idx_t buffer_size) { auto &buffer_manager = BufferManager::GetBufferManager(context); bool can_destroy = can_seek; - handle = buffer_manager.Allocate(MaxValue(Storage::BLOCK_SIZE, buffer_size), can_destroy, &block); + handle = buffer_manager.Allocate(MemoryTag::CSV_READER, MaxValue(Storage::BLOCK_SIZE, buffer_size), + can_destroy, &block); } idx_t CSVBuffer::GetBufferSize() { @@ -57,15 +65,16 @@ void CSVBuffer::Reload(CSVFileHandle &file_handle) { file_handle.Read(handle.Ptr(), actual_buffer_size); } -unique_ptr CSVBuffer::Pin(CSVFileHandle &file_handle) { +shared_ptr CSVBuffer::Pin(CSVFileHandle &file_handle, bool &has_seeked) { auto &buffer_manager = BufferManager::GetBufferManager(context); if (can_seek && block->IsUnloaded()) { // We have to reload it from disk block = nullptr; Reload(file_handle); + has_seeked = true; } - return make_uniq(buffer_manager.Pin(block), actual_buffer_size, last_buffer, file_number, - buffer_idx); + return make_shared(buffer_manager.Pin(block), actual_buffer_size, last_buffer, file_number, + buffer_idx); } void CSVBuffer::Unpin() { diff --git a/src/duckdb/src/execution/operator/csv_scanner/buffer_manager/csv_buffer_manager.cpp b/src/duckdb/src/execution/operator/csv_scanner/buffer_manager/csv_buffer_manager.cpp index 1f5b0b4db..a5ae371e7 100644 --- a/src/duckdb/src/execution/operator/csv_scanner/buffer_manager/csv_buffer_manager.cpp +++ b/src/duckdb/src/execution/operator/csv_scanner/buffer_manager/csv_buffer_manager.cpp @@ -47,7 +47,7 @@ bool CSVBufferManager::ReadNextAndCacheIt() { last_buffer->last_buffer = true; return false; } - auto maybe_last_buffer = last_buffer->Next(*file_handle, cur_buffer_size, file_idx); + auto maybe_last_buffer = last_buffer->Next(*file_handle, cur_buffer_size, file_idx, has_seeked); if (!maybe_last_buffer) { last_buffer->last_buffer = true; return false; @@ -61,7 +61,7 @@ bool CSVBufferManager::ReadNextAndCacheIt() { return false; } -unique_ptr CSVBufferManager::GetBuffer(const idx_t pos) { +shared_ptr CSVBufferManager::GetBuffer(const idx_t pos) { lock_guard parallel_lock(main_mutex); while (pos >= cached_buffers.size()) { if (done) { @@ -72,9 +72,37 @@ unique_ptr CSVBufferManager::GetBuffer(const idx_t pos) { } } if (pos != 0) { - cached_buffers[pos - 1]->Unpin(); + if (cached_buffers[pos - 1]) { + cached_buffers[pos - 1]->Unpin(); + } + } + return cached_buffers[pos]->Pin(*file_handle, has_seeked); +} + +void CSVBufferManager::ResetBuffer(const idx_t buffer_idx) { + D_ASSERT(buffer_idx < cached_buffers.size() && cached_buffers[buffer_idx]); + if (buffer_idx == 0 && cached_buffers.size() > 1) { + cached_buffers[buffer_idx].reset(); + idx_t cur_buffer = buffer_idx + 1; + while (reset_when_possible.find(cur_buffer) != reset_when_possible.end()) { + cached_buffers[cur_buffer].reset(); + reset_when_possible.erase(cur_buffer); + cur_buffer++; + } + return; + } + // We only reset if previous one was also already reset + if (buffer_idx > 0 && !cached_buffers[buffer_idx - 1]) { + cached_buffers[buffer_idx].reset(); + idx_t cur_buffer = buffer_idx + 1; + while (reset_when_possible.find(cur_buffer) != reset_when_possible.end()) { + cached_buffers[cur_buffer].reset(); + reset_when_possible.erase(cur_buffer); + cur_buffer++; + } + } else { + reset_when_possible.insert(buffer_idx); } - return cached_buffers[pos]->Pin(*file_handle); } idx_t CSVBufferManager::GetBufferSize() { diff --git a/src/duckdb/src/execution/operator/csv_scanner/scanner/string_value_scanner.cpp b/src/duckdb/src/execution/operator/csv_scanner/scanner/string_value_scanner.cpp index 50fca884a..ab7c6ea98 100644 --- a/src/duckdb/src/execution/operator/csv_scanner/scanner/string_value_scanner.cpp +++ b/src/duckdb/src/execution/operator/csv_scanner/scanner/string_value_scanner.cpp @@ -9,9 +9,10 @@ namespace duckdb { -StringValueResult::StringValueResult(CSVStates &states, CSVStateMachine &state_machine, CSVBufferHandle &buffer_handle, - Allocator &buffer_allocator, idx_t result_size_p, idx_t buffer_position, - CSVErrorHandler &error_hander_p, CSVIterator &iterator_p, bool store_line_size_p, +StringValueResult::StringValueResult(CSVStates &states, CSVStateMachine &state_machine, + const shared_ptr &buffer_handle, Allocator &buffer_allocator, + idx_t result_size_p, idx_t buffer_position, CSVErrorHandler &error_hander_p, + CSVIterator &iterator_p, bool store_line_size_p, shared_ptr csv_file_scan_p, idx_t &lines_read_p) : ScannerResult(states, state_machine), number_of_columns(state_machine.dialect_options.num_cols), null_padding(state_machine.options.null_padding), ignore_errors(state_machine.options.ignore_errors), @@ -20,14 +21,14 @@ StringValueResult::StringValueResult(CSVStates &states, CSVStateMachine &state_m store_line_size(store_line_size_p), csv_file_scan(std::move(csv_file_scan_p)), lines_read(lines_read_p) { // Vector information D_ASSERT(number_of_columns > 0); - + buffer_handles.push_back(buffer_handle); // Buffer Information - buffer_ptr = buffer_handle.Ptr(); - buffer_size = buffer_handle.actual_size; + buffer_ptr = buffer_handle->Ptr(); + buffer_size = buffer_handle->actual_size; last_position = buffer_position; // Current Result information - previous_line_start = {iterator.pos.buffer_idx, iterator.pos.buffer_pos, buffer_handle.actual_size}; + previous_line_start = {iterator.pos.buffer_idx, iterator.pos.buffer_pos, buffer_handle->actual_size}; pre_previous_line_start = previous_line_start; // Fill out Parse Types vector logical_types; @@ -86,7 +87,29 @@ StringValueResult::StringValueResult(CSVStates &states, CSVStateMachine &state_m } } +inline bool IsValueNull(const char *null_str_ptr, const char *value_ptr, const idx_t size) { + for (idx_t i = 0; i < size; i++) { + if (null_str_ptr[i] != value_ptr[i]) { + return false; + } + } + return true; +} + void StringValueResult::AddValueToVector(const char *value_ptr, const idx_t size, bool allocate) { + if (cur_col_id >= number_of_columns) { + bool error = true; + if (cur_col_id == number_of_columns && ((quoted && state_machine.options.allow_quoted_nulls) || !quoted)) { + // we make an exception if the first over-value is null + error = !IsValueNull(null_str_ptr, value_ptr, size); + } + if (error) { + HandleOverLimitRows(); + } + } + if (ignore_current_row) { + return; + } if (projecting_columns) { if (!projected_columns[cur_col_id]) { cur_col_id++; @@ -95,22 +118,12 @@ void StringValueResult::AddValueToVector(const char *value_ptr, const idx_t size } if (size == null_str_size) { if (((quoted && state_machine.options.allow_quoted_nulls) || !quoted)) { - bool is_null = true; - for (idx_t i = 0; i < size; i++) { - if (null_str_ptr[i] != value_ptr[i]) { - is_null = false; - break; - } - } - if (is_null) { + if (IsValueNull(null_str_ptr, value_ptr, size)) { bool empty = false; if (chunk_col_id < state_machine.options.force_not_null.size()) { empty = state_machine.options.force_not_null[chunk_col_id]; } if (empty) { - if (chunk_col_id >= number_of_columns) { - HandleOverLimitRows(); - } if (parse_types[chunk_col_id] != LogicalTypeId::VARCHAR) { // If it is not a varchar, empty values are not accepted, we must error. cast_errors[chunk_col_id] = std::string(""); @@ -129,15 +142,6 @@ void StringValueResult::AddValueToVector(const char *value_ptr, const idx_t size } } } - if (chunk_col_id >= number_of_columns) { - HandleOverLimitRows(); - if (projecting_columns) { - if (!projected_columns[cur_col_id]) { - cur_col_id++; - return; - } - } - } bool success = true; switch (parse_types[chunk_col_id]) { case LogicalTypeId::TINYINT: @@ -227,18 +231,34 @@ DataChunk &StringValueResult::ToChunk() { return parse_chunk; } +void StringValueResult::Reset() { + if (number_of_rows == 0) { + return; + } + number_of_rows = 0; + cur_col_id = 0; + chunk_col_id = 0; + for (auto &v : validity_mask) { + v->SetAllValid(result_size); + } + buffer_handles.clear(); +} + void StringValueResult::AddQuotedValue(StringValueResult &result, const idx_t buffer_pos) { if (result.escaped) { if (result.projecting_columns) { if (!result.projected_columns[result.cur_col_id]) { result.cur_col_id++; + result.quoted = false; + result.escaped = false; return; } } // If it's an escaped value we have to remove all the escapes, this is not really great auto value = StringValueScanner::RemoveEscape( result.buffer_ptr + result.quoted_position + 1, buffer_pos - result.quoted_position - 2, - result.state_machine.options.GetEscape()[0], result.parse_chunk.data[result.chunk_col_id]); + result.state_machine.dialect_options.state_machine_options.escape.GetValue(), + result.parse_chunk.data[result.chunk_col_id]); result.AddValueToVector(value.GetData(), value.GetSize()); } else { if (buffer_pos < result.last_position + 2) { @@ -274,6 +294,7 @@ void StringValueResult::HandleOverLimitRows() { // If we get here we need to remove the last line cur_col_id = 0; chunk_col_id = 0; + ignore_current_row = true; } void StringValueResult::QuotedNewLine(StringValueResult &result) { @@ -292,6 +313,11 @@ void StringValueResult::NullPaddingQuotedNewlineCheck() { } bool StringValueResult::AddRowInternal() { + if (ignore_current_row) { + // An error occurred on this row, we are ignoring it and resetting our control flag + ignore_current_row = false; + return false; + } if (!cast_errors.empty()) { // A wild casting error appears // Recreate row for rejects-table @@ -336,6 +362,12 @@ bool StringValueResult::AddRowInternal() { if (cur_col_id < state_machine.options.force_not_null.size()) { empty = state_machine.options.force_not_null[cur_col_id]; } + if (projecting_columns) { + if (!projected_columns[cur_col_id]) { + cur_col_id++; + continue; + } + } if (empty) { static_cast(vector_ptr[chunk_col_id])[number_of_rows] = string_t(); } else { @@ -345,7 +377,7 @@ bool StringValueResult::AddRowInternal() { chunk_col_id++; } } else { - // If we are not nullpadding this is an error + // If we are not null-padding this is an error auto csv_error = CSVError::IncorrectColumnAmountError(state_machine.options, nullptr, number_of_columns, cur_col_id); LinesPerBoundary lines_per_batch(iterator.GetBoundaryIdx(), number_of_rows + 1); @@ -374,8 +406,8 @@ bool StringValueResult::AddRow(StringValueResult &result, const idx_t buffer_pos } if (current_line_size > result.state_machine.options.maximum_line_size) { auto csv_error = CSVError::LineSizeError(result.state_machine.options, current_line_size); - LinesPerBoundary lines_per_batch(result.iterator.GetBoundaryIdx(), result.number_of_rows + 1); - result.error_handler.Error(lines_per_batch, csv_error); + LinesPerBoundary lines_per_batch(result.iterator.GetBoundaryIdx(), result.number_of_rows); + result.error_handler.Error(lines_per_batch, csv_error, true); } result.pre_previous_line_start = result.previous_line_start; result.previous_line_start = current_line_start; @@ -444,7 +476,7 @@ StringValueScanner::StringValueScanner(idx_t scanner_idx_p, const shared_ptr &csv_file_scan, CSVIterator boundary, idx_t result_size) : BaseScanner(buffer_manager, state_machine, error_handler, csv_file_scan, boundary), scanner_idx(scanner_idx_p), - result(states, *state_machine, *cur_buffer_handle, BufferAllocator::Get(buffer_manager->context), result_size, + result(states, *state_machine, cur_buffer_handle, BufferAllocator::Get(buffer_manager->context), result_size, iterator.pos.buffer_pos, *error_handler, iterator, buffer_manager->context.client_data->debug_set_max_line_length, csv_file_scan, lines_read) { } @@ -453,7 +485,7 @@ StringValueScanner::StringValueScanner(const shared_ptr &buffe const shared_ptr &state_machine, const shared_ptr &error_handler) : BaseScanner(buffer_manager, state_machine, error_handler, nullptr, {}), scanner_idx(0), - result(states, *state_machine, *cur_buffer_handle, Allocator::DefaultAllocator(), STANDARD_VECTOR_SIZE, + result(states, *state_machine, cur_buffer_handle, Allocator::DefaultAllocator(), STANDARD_VECTOR_SIZE, iterator.pos.buffer_pos, *error_handler, iterator, buffer_manager->context.client_data->debug_set_max_line_length, csv_file_scan, lines_read) { } @@ -476,12 +508,7 @@ bool StringValueScanner::FinishedIterator() { } StringValueResult &StringValueScanner::ParseChunk() { - result.number_of_rows = 0; - result.cur_col_id = 0; - result.chunk_col_id = 0; - for (auto &v : result.validity_mask) { - v->SetAllValid(result.result_size); - } + result.Reset(); ParseChunkInternal(result); return result; } @@ -831,8 +858,9 @@ void StringValueScanner::ProcessOverbufferValue() { bool StringValueScanner::MoveToNextBuffer() { if (iterator.pos.buffer_pos >= cur_buffer_handle->actual_size) { - previous_buffer_handle = std::move(cur_buffer_handle); + previous_buffer_handle = cur_buffer_handle; cur_buffer_handle = buffer_manager->GetBuffer(++iterator.pos.buffer_idx); + result.buffer_handles.push_back(cur_buffer_handle); if (!cur_buffer_handle) { iterator.pos.buffer_idx--; buffer_handle_ptr = nullptr; @@ -1051,8 +1079,8 @@ void StringValueScanner::FinalizeChunkProcess() { } } iterator.done = FinishedFile(); - if (result.null_padding) { - while (result.cur_col_id < result.number_of_columns) { + if (result.null_padding && result.number_of_rows < STANDARD_VECTOR_SIZE) { + while (result.chunk_col_id < result.parse_chunk.ColumnCount()) { result.validity_mask[result.chunk_col_id++]->SetInvalid(result.number_of_rows); result.cur_col_id++; } diff --git a/src/duckdb/src/execution/operator/csv_scanner/table_function/csv_file_scanner.cpp b/src/duckdb/src/execution/operator/csv_scanner/table_function/csv_file_scanner.cpp index cca2c5230..27293e073 100644 --- a/src/duckdb/src/execution/operator/csv_scanner/table_function/csv_file_scanner.cpp +++ b/src/duckdb/src/execution/operator/csv_scanner/table_function/csv_file_scanner.cpp @@ -114,8 +114,8 @@ CSVFileScan::CSVFileScan(ClientContext &context, const string &file_path_p, cons options.dialect_options.state_machine_options.new_line = CSVSniffer::DetectNewLineDelimiter(*buffer_manager); } - names = bind_data.return_names; - types = bind_data.return_types; + names = bind_data.csv_names; + types = bind_data.csv_types; state_machine = make_shared(state_machine_cache.Get(options.dialect_options.state_machine_options), options); @@ -152,11 +152,6 @@ CSVFileScan::CSVFileScan(ClientContext &context, const string &file_name, CSVRea } void CSVFileScan::InitializeFileNamesTypes() { - if (options.null_padding) { - // If we are null padding we do not yet support projection pushdown - file_types = types; - return; - } if (reader_data.empty_columns && reader_data.column_ids.empty()) { // This means that the columns from this file are irrelevant. // just read the first column diff --git a/src/duckdb/src/execution/operator/csv_scanner/table_function/global_csv_state.cpp b/src/duckdb/src/execution/operator/csv_scanner/table_function/global_csv_state.cpp index b262f592e..dad859ed1 100644 --- a/src/duckdb/src/execution/operator/csv_scanner/table_function/global_csv_state.cpp +++ b/src/duckdb/src/execution/operator/csv_scanner/table_function/global_csv_state.cpp @@ -36,6 +36,7 @@ CSVGlobalState::CSVGlobalState(ClientContext &context_p, const shared_ptrbuffer_manager->GetBuffer(0)->actual_size; current_boundary = CSVIterator(0, 0, 0, 0, buffer_size); } + current_buffer_in_use = make_shared(*file_scans.back()->buffer_manager, 0); } double CSVGlobalState::GetProgress(const ReadCSVData &bind_data_p) const { @@ -77,18 +78,23 @@ unique_ptr CSVGlobalState::Next() { if (finished) { return nullptr; } - + if (current_buffer_in_use->buffer_idx != current_boundary.GetBufferIdx()) { + current_buffer_in_use = + make_shared(*file_scans.back()->buffer_manager, current_boundary.GetBufferIdx()); + } // We first create the scanner for the current boundary auto ¤t_file = *file_scans.back(); auto csv_scanner = make_uniq(scanner_idx++, current_file.buffer_manager, current_file.state_machine, current_file.error_handler, file_scans.back(), current_boundary); + + csv_scanner->buffer_tracker = current_buffer_in_use; + // We then produce the next boundary if (!current_boundary.Next(*current_file.buffer_manager)) { // This means we are done scanning the current file auto current_file_idx = current_file.file_idx + 1; if (current_file_idx < bind_data.files.size()) { - file_scans.back()->buffer_manager.reset(); // If we have a next file we have to construct the file scan for that file_scans.emplace_back(make_shared(context, bind_data.files[current_file_idx], bind_data.options, current_file_idx, bind_data, column_ids, @@ -96,6 +102,7 @@ unique_ptr CSVGlobalState::Next() { // And re-start the boundary-iterator auto buffer_size = file_scans.back()->buffer_manager->GetBuffer(0)->actual_size; current_boundary = CSVIterator(current_file_idx, 0, 0, 0, buffer_size); + current_buffer_in_use = make_shared(*file_scans.back()->buffer_manager, 0); } else { // If not we are done with this CSV Scanning finished = true; diff --git a/src/duckdb/src/execution/operator/csv_scanner/util/csv_error.cpp b/src/duckdb/src/execution/operator/csv_scanner/util/csv_error.cpp index b2c737c99..612c8f37e 100644 --- a/src/duckdb/src/execution/operator/csv_scanner/util/csv_error.cpp +++ b/src/duckdb/src/execution/operator/csv_scanner/util/csv_error.cpp @@ -170,6 +170,7 @@ idx_t CSVErrorHandler::GetLine(LinesPerBoundary &error_info) { // if it's the first boundary, we just return break; } + lock_guard parallel_lock(main_mutex); if (lines_per_batch_map.find(boundary_idx) != lines_per_batch_map.end()) { batch_done = true; current_line += lines_per_batch_map[boundary_idx].lines_in_batch; diff --git a/src/duckdb/src/execution/operator/join/perfect_hash_join_executor.cpp b/src/duckdb/src/execution/operator/join/perfect_hash_join_executor.cpp index ff3078bcc..bfac54029 100644 --- a/src/duckdb/src/execution/operator/join/perfect_hash_join_executor.cpp +++ b/src/duckdb/src/execution/operator/join/perfect_hash_join_executor.cpp @@ -77,7 +77,7 @@ bool PerfectHashJoinExecutor::FullScanHashTable(LogicalType &key_type) { auto &col_mask = FlatVector::Validity(vector); col_mask.Initialize(build_size); } - data_collection.Gather(tuples_addresses, sel_tuples, key_count, output_col_idx, vector, sel_build); + data_collection.Gather(tuples_addresses, sel_tuples, key_count, output_col_idx, vector, sel_build, nullptr); } return true; diff --git a/src/duckdb/src/execution/operator/join/physical_hash_join.cpp b/src/duckdb/src/execution/operator/join/physical_hash_join.cpp index 6cb658f97..8a4959035 100644 --- a/src/duckdb/src/execution/operator/join/physical_hash_join.cpp +++ b/src/duckdb/src/execution/operator/join/physical_hash_join.cpp @@ -7,13 +7,13 @@ #include "duckdb/main/client_context.hpp" #include "duckdb/main/query_profiler.hpp" #include "duckdb/parallel/base_pipeline_event.hpp" +#include "duckdb/parallel/interrupt.hpp" #include "duckdb/parallel/pipeline.hpp" #include "duckdb/parallel/thread_context.hpp" #include "duckdb/planner/expression/bound_aggregate_expression.hpp" #include "duckdb/planner/expression/bound_reference_expression.hpp" #include "duckdb/storage/buffer_manager.hpp" #include "duckdb/storage/storage_manager.hpp" -#include "duckdb/parallel/interrupt.hpp" #include "duckdb/storage/temporary_memory_manager.hpp" namespace duckdb { @@ -87,8 +87,10 @@ PhysicalHashJoin::PhysicalHashJoin(LogicalOperator &op, unique_ptr temporary_memory_update_count; //! Temporary memory state for managing this operator's memory usage unique_ptr temporary_memory_state; + //! Global HT used by the join unique_ptr hash_table; //! The perfect hash join executor (if any) @@ -133,7 +139,8 @@ class HashJoinGlobalSinkState : public GlobalSinkState { class HashJoinLocalSinkState : public LocalSinkState { public: - HashJoinLocalSinkState(const PhysicalHashJoin &op, ClientContext &context) : join_key_executor(context) { + HashJoinLocalSinkState(const PhysicalHashJoin &op, ClientContext &context) + : join_key_executor(context), chunk_count(0) { auto &allocator = BufferAllocator::Get(context); for (auto &cond : op.conditions) { @@ -159,6 +166,10 @@ class HashJoinLocalSinkState : public LocalSinkState { //! Thread-local HT unique_ptr hash_table; + + //! For updating the temporary memory state + idx_t chunk_count; + static constexpr const idx_t CHUNK_COUNT_UPDATE_INTERVAL = 60; }; unique_ptr PhysicalHashJoin::InitializeHashTable(ClientContext &context) const { @@ -242,6 +253,16 @@ SinkResultType PhysicalHashJoin::Sink(ExecutionContext &context, DataChunk &chun } ht.Build(lstate.append_state, lstate.join_keys, lstate.payload_chunk); } + + if (++lstate.chunk_count % HashJoinLocalSinkState::CHUNK_COUNT_UPDATE_INTERVAL == 0) { + auto &gstate = input.global_state.Cast(); + if (++gstate.temporary_memory_update_count % gstate.num_threads == 0) { + auto &sink_collection = lstate.hash_table->GetSinkCollection(); + auto ht_size = sink_collection.SizeInBytes() + JoinHashTable::PointerTableSize(sink_collection.Count()); + gstate.temporary_memory_state->SetRemainingSize(context.client, gstate.num_threads * ht_size); + } + } + return SinkResultType::NEED_MORE_INPUT; } @@ -431,7 +452,6 @@ class HashJoinRepartitionEvent : public BasePipelineEvent { sink.hash_table->GetTotalSize(partition_sizes, partition_counts, max_partition_size, max_partition_count); sink.temporary_memory_state->SetMinimumReservation(max_partition_size + JoinHashTable::PointerTableSize(max_partition_count)); - sink.hash_table->PrepareExternalFinalize(sink.temporary_memory_state->GetReservation()); sink.ScheduleFinalize(*pipeline, *this); } diff --git a/src/duckdb/src/execution/operator/persistent/physical_copy_to_file.cpp b/src/duckdb/src/execution/operator/persistent/physical_copy_to_file.cpp index 57bc9bbc0..76eb6d60d 100644 --- a/src/duckdb/src/execution/operator/persistent/physical_copy_to_file.cpp +++ b/src/duckdb/src/execution/operator/persistent/physical_copy_to_file.cpp @@ -112,7 +112,16 @@ unique_ptr PhysicalCopyToFile::GetGlobalSinkState(ClientContext //===--------------------------------------------------------------------===// void PhysicalCopyToFile::MoveTmpFile(ClientContext &context, const string &tmp_file_path) { auto &fs = FileSystem::GetFileSystem(context); - auto file_path = tmp_file_path.substr(0, tmp_file_path.length() - 4); + + auto path = StringUtil::GetFilePath(tmp_file_path); + auto base = StringUtil::GetFileName(tmp_file_path); + + auto prefix = base.find("tmp_"); + if (prefix == 0) { + base = base.substr(4); + } + + auto file_path = fs.JoinPath(path, base); if (fs.FileExists(file_path)) { fs.RemoveFile(file_path); } diff --git a/src/duckdb/src/execution/operator/scan/physical_column_data_scan.cpp b/src/duckdb/src/execution/operator/scan/physical_column_data_scan.cpp index d91e7a5c3..ca6899406 100644 --- a/src/duckdb/src/execution/operator/scan/physical_column_data_scan.cpp +++ b/src/duckdb/src/execution/operator/scan/physical_column_data_scan.cpp @@ -72,7 +72,18 @@ void PhysicalColumnDataScan::BuildPipelines(Pipeline ¤t, MetaPipeline &met return; } case PhysicalOperatorType::CTE_SCAN: { - break; + auto entry = state.cte_dependencies.find(*this); + D_ASSERT(entry != state.cte_dependencies.end()); + // this chunk scan introduces a dependency to the current pipeline + // namely a dependency on the CTE pipeline to finish + auto cte_dependency = entry->second.get().shared_from_this(); + auto cte_sink = state.GetPipelineSink(*cte_dependency); + (void)cte_sink; + D_ASSERT(cte_sink); + D_ASSERT(cte_sink->type == PhysicalOperatorType::CTE); + current.AddDependency(cte_dependency); + state.SetPipelineSource(current, *this); + return; } case PhysicalOperatorType::RECURSIVE_CTE_SCAN: if (!meta_pipeline.HasRecursiveCTE()) { diff --git a/src/duckdb/src/execution/operator/set/physical_cte.cpp b/src/duckdb/src/execution/operator/set/physical_cte.cpp index 67cfa021a..8804b4a2a 100644 --- a/src/duckdb/src/execution/operator/set/physical_cte.cpp +++ b/src/duckdb/src/execution/operator/set/physical_cte.cpp @@ -3,12 +3,9 @@ #include "duckdb/common/types/column/column_data_collection.hpp" #include "duckdb/common/vector_operations/vector_operations.hpp" #include "duckdb/execution/aggregate_hashtable.hpp" -#include "duckdb/execution/executor.hpp" #include "duckdb/parallel/event.hpp" #include "duckdb/parallel/meta_pipeline.hpp" #include "duckdb/parallel/pipeline.hpp" -#include "duckdb/parallel/task_scheduler.hpp" -#include "duckdb/storage/buffer_manager.hpp" namespace duckdb { @@ -26,96 +23,59 @@ PhysicalCTE::~PhysicalCTE() { //===--------------------------------------------------------------------===// // Sink //===--------------------------------------------------------------------===// -class CTEState : public GlobalSinkState { +class CTEGlobalState : public GlobalSinkState { public: - explicit CTEState(ClientContext &context, const PhysicalCTE &op) - : intermediate_table(context, op.children[1]->GetTypes()) { + explicit CTEGlobalState(ClientContext &context, const PhysicalCTE &op) : working_table_ref(op.working_table.get()) { } - ColumnDataCollection intermediate_table; - ColumnDataScanState scan_state; - bool initialized = false; - bool finished_scan = false; -}; + optional_ptr working_table_ref; -unique_ptr PhysicalCTE::GetGlobalSinkState(ClientContext &context) const { - working_table->Reset(); - return make_uniq(context, *this); -} + mutex lhs_lock; -SinkResultType PhysicalCTE::Sink(ExecutionContext &context, DataChunk &chunk, OperatorSinkInput &input) const { - auto &gstate = input.global_state.Cast(); - if (!gstate.finished_scan) { - working_table->Append(chunk); - } else { - gstate.intermediate_table.Append(chunk); + void MergeIT(ColumnDataCollection &input) { + lock_guard guard(lhs_lock); + working_table_ref->Combine(input); } - return SinkResultType::NEED_MORE_INPUT; -} +}; -//===--------------------------------------------------------------------===// -// Source -//===--------------------------------------------------------------------===// -SourceResultType PhysicalCTE::GetData(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input) const { - auto &gstate = sink_state->Cast(); - if (!gstate.initialized) { - gstate.intermediate_table.InitializeScan(gstate.scan_state); - gstate.finished_scan = false; - gstate.initialized = true; +class CTELocalState : public LocalSinkState { +public: + explicit CTELocalState(ClientContext &context, const PhysicalCTE &op) + : lhs_data(context, op.working_table->Types()) { + lhs_data.InitializeAppend(append_state); } - if (!gstate.finished_scan) { - gstate.finished_scan = true; - ExecuteRecursivePipelines(context); + + unique_ptr distinct_state; + ColumnDataCollection lhs_data; + ColumnDataAppendState append_state; + + void Append(DataChunk &input) { + lhs_data.Append(input); } +}; - gstate.intermediate_table.Scan(gstate.scan_state, chunk); +unique_ptr PhysicalCTE::GetGlobalSinkState(ClientContext &context) const { + working_table->Reset(); + return make_uniq(context, *this); +} - return chunk.size() == 0 ? SourceResultType::FINISHED : SourceResultType::HAVE_MORE_OUTPUT; +unique_ptr PhysicalCTE::GetLocalSinkState(ExecutionContext &context) const { + auto state = make_uniq(context.client, *this); + return std::move(state); } -void PhysicalCTE::ExecuteRecursivePipelines(ExecutionContext &context) const { - if (!recursive_meta_pipeline) { - throw InternalException("Missing meta pipeline for recursive CTE"); - } +SinkResultType PhysicalCTE::Sink(ExecutionContext &context, DataChunk &chunk, OperatorSinkInput &input) const { + auto &lstate = input.local_state.Cast(); + lstate.lhs_data.Append(lstate.append_state, chunk); - // get and reset pipelines - vector> pipelines; - recursive_meta_pipeline->GetPipelines(pipelines, true); - for (auto &pipeline : pipelines) { - auto sink = pipeline->GetSink(); - if (sink.get() != this) { - sink->sink_state.reset(); - } - for (auto &op_ref : pipeline->GetOperators()) { - auto &op = op_ref.get(); - op.op_state.reset(); - } - pipeline->ClearSource(); - } + return SinkResultType::NEED_MORE_INPUT; +} - // get the MetaPipelines in the recursive_meta_pipeline and reschedule them - vector> meta_pipelines; - recursive_meta_pipeline->GetMetaPipelines(meta_pipelines, true, false); - auto &executor = recursive_meta_pipeline->GetExecutor(); - vector> events; - executor.ReschedulePipelines(meta_pipelines, events); - - while (true) { - executor.WorkOnTasks(); - if (executor.HasError()) { - executor.ThrowException(); - } - bool finished = true; - for (auto &event : events) { - if (!event->IsFinished()) { - finished = false; - break; - } - } - if (finished) { - // all pipelines finished: done! - break; - } - } +SinkCombineResultType PhysicalCTE::Combine(ExecutionContext &context, OperatorSinkCombineInput &input) const { + auto &lstate = input.local_state.Cast(); + auto &gstate = input.global_state.Cast(); + gstate.MergeIT(lstate.lhs_data); + + return SinkCombineResultType::FINISHED; } //===--------------------------------------------------------------------===// @@ -125,27 +85,21 @@ void PhysicalCTE::BuildPipelines(Pipeline ¤t, MetaPipeline &meta_pipeline) D_ASSERT(children.size() == 2); op_state.reset(); sink_state.reset(); - recursive_meta_pipeline.reset(); auto &state = meta_pipeline.GetState(); - state.SetPipelineSource(current, *this); - - auto &executor = meta_pipeline.GetExecutor(); - executor.AddMaterializedCTE(*this); auto &child_meta_pipeline = meta_pipeline.CreateChildMetaPipeline(current, *this); child_meta_pipeline.Build(*children[0]); - // the RHS is the recursive pipeline - recursive_meta_pipeline = make_shared(executor, state, this); - if (meta_pipeline.HasRecursiveCTE()) { - recursive_meta_pipeline->SetRecursiveCTE(); + for (auto &cte_scan : cte_scans) { + state.cte_dependencies.insert(make_pair(cte_scan, reference(*child_meta_pipeline.GetBasePipeline()))); } - recursive_meta_pipeline->Build(*children[1]); + + children[1]->BuildPipelines(current, meta_pipeline); } vector> PhysicalCTE::GetSources() const { - return {*this}; + return children[1]->GetSources(); } string PhysicalCTE::ParamsToString() const { diff --git a/src/duckdb/src/execution/operator/set/physical_recursive_cte.cpp b/src/duckdb/src/execution/operator/set/physical_recursive_cte.cpp index ea367ed47..fba7d664d 100644 --- a/src/duckdb/src/execution/operator/set/physical_recursive_cte.cpp +++ b/src/duckdb/src/execution/operator/set/physical_recursive_cte.cpp @@ -38,6 +38,7 @@ class RecursiveCTEState : public GlobalSinkState { unique_ptr ht; bool intermediate_empty = true; + mutex intermediate_table_lock; ColumnDataCollection intermediate_table; ColumnDataScanState scan_state; bool initialized = false; @@ -63,6 +64,8 @@ idx_t PhysicalRecursiveCTE::ProbeHT(DataChunk &chunk, RecursiveCTEState &state) SinkResultType PhysicalRecursiveCTE::Sink(ExecutionContext &context, DataChunk &chunk, OperatorSinkInput &input) const { auto &gstate = input.global_state.Cast(); + + lock_guard guard(gstate.intermediate_table_lock); if (!union_all) { idx_t match_count = ProbeHT(chunk, gstate); if (match_count > 0) { diff --git a/src/duckdb/src/execution/physical_plan/plan_copy_to_file.cpp b/src/duckdb/src/execution/physical_plan/plan_copy_to_file.cpp index 373fbdfcf..49e33275b 100644 --- a/src/duckdb/src/execution/physical_plan/plan_copy_to_file.cpp +++ b/src/duckdb/src/execution/physical_plan/plan_copy_to_file.cpp @@ -13,7 +13,9 @@ unique_ptr PhysicalPlanGenerator::CreatePlan(LogicalCopyToFile auto &fs = FileSystem::GetFileSystem(context); op.file_path = fs.ExpandPath(op.file_path); if (op.use_tmp_file) { - op.file_path += ".tmp"; + auto path = StringUtil::GetFilePath(op.file_path); + auto base = StringUtil::GetFileName(op.file_path); + op.file_path = fs.JoinPath(path, "tmp_" + base); } if (op.per_thread_output || op.file_size_bytes.IsValid() || op.partition_output || !op.partition_columns.empty() || op.overwrite_or_ignore) { diff --git a/src/duckdb/src/execution/physical_plan/plan_cte.cpp b/src/duckdb/src/execution/physical_plan/plan_cte.cpp index 0c0b04855..f323aed46 100644 --- a/src/duckdb/src/execution/physical_plan/plan_cte.cpp +++ b/src/duckdb/src/execution/physical_plan/plan_cte.cpp @@ -16,16 +16,18 @@ unique_ptr PhysicalPlanGenerator::CreatePlan(LogicalMaterializ // Add the ColumnDataCollection to the context of this PhysicalPlanGenerator recursive_cte_tables[op.table_index] = working_table; + materialized_ctes[op.table_index] = vector>(); // Create the plan for the left side. This is the materialization. auto left = CreatePlan(*op.children[0]); // Initialize an empty vector to collect the scan operators. - materialized_ctes.insert(op.table_index); auto right = CreatePlan(*op.children[1]); - auto cte = make_uniq(op.ctename, op.table_index, op.children[1]->types, std::move(left), - std::move(right), op.estimated_cardinality); + unique_ptr cte; + cte = make_uniq(op.ctename, op.table_index, op.children[1]->types, std::move(left), std::move(right), + op.estimated_cardinality); cte->working_table = working_table; + cte->cte_scans = materialized_ctes[op.table_index]; return std::move(cte); } diff --git a/src/duckdb/src/execution/physical_plan/plan_recursive_cte.cpp b/src/duckdb/src/execution/physical_plan/plan_recursive_cte.cpp index 8ded97a8c..82ddd9c21 100644 --- a/src/duckdb/src/execution/physical_plan/plan_recursive_cte.cpp +++ b/src/duckdb/src/execution/physical_plan/plan_recursive_cte.cpp @@ -37,7 +37,7 @@ unique_ptr PhysicalPlanGenerator::CreatePlan(LogicalCTERef &op // If this check fails, this is a reference to a materialized recursive CTE. if (materialized_cte != materialized_ctes.end()) { - auto chunk_scan = make_uniq(op.types, PhysicalOperatorType::CTE_SCAN, + auto chunk_scan = make_uniq(op.chunk_types, PhysicalOperatorType::CTE_SCAN, op.estimated_cardinality, op.cte_index); auto cte = recursive_cte_tables.find(op.cte_index); @@ -45,6 +45,7 @@ unique_ptr PhysicalPlanGenerator::CreatePlan(LogicalCTERef &op throw InvalidInputException("Referenced materialized CTE does not exist."); } chunk_scan->collection = cte->second.get(); + materialized_cte->second.push_back(*chunk_scan.get()); return std::move(chunk_scan); } @@ -60,6 +61,7 @@ unique_ptr PhysicalPlanGenerator::CreatePlan(LogicalCTERef &op cte->second.get()->Types(), PhysicalOperatorType::RECURSIVE_CTE_SCAN, op.estimated_cardinality, op.cte_index); chunk_scan->collection = cte->second.get(); + return std::move(chunk_scan); } diff --git a/src/duckdb/src/execution/radix_partitioned_hashtable.cpp b/src/duckdb/src/execution/radix_partitioned_hashtable.cpp index b3d0a7ea8..640f29687 100644 --- a/src/duckdb/src/execution/radix_partitioned_hashtable.cpp +++ b/src/duckdb/src/execution/radix_partitioned_hashtable.cpp @@ -168,6 +168,7 @@ class RadixHTGlobalSinkState : public GlobalSinkState { //! For synchronizing finalize tasks atomic finalize_idx; + atomic finalize_done; //! Pin properties when scanning TupleDataPinProperties scan_pin_properties; @@ -182,8 +183,9 @@ class RadixHTGlobalSinkState : public GlobalSinkState { RadixHTGlobalSinkState::RadixHTGlobalSinkState(ClientContext &context_p, const RadixPartitionedHashTable &radix_ht_p) : context(context_p), temporary_memory_state(TemporaryMemoryManager::Get(context).Register(context)), radix_ht(radix_ht_p), config(context, *this), finalized(false), external(false), active_threads(0), - any_combined(false), finalize_idx(0), scan_pin_properties(TupleDataPinProperties::DESTROY_AFTER_DONE), - count_before_combining(0), max_partition_size(0) { + any_combined(false), finalize_idx(0), finalize_done(0), + scan_pin_properties(TupleDataPinProperties::DESTROY_AFTER_DONE), count_before_combining(0), + max_partition_size(0) { auto tuples_per_block = Storage::BLOCK_ALLOC_SIZE / radix_ht.GetLayout().GetRowWidth(); idx_t ht_count = config.sink_capacity / GroupedAggregateHashTable::LOAD_FACTOR; @@ -353,7 +355,7 @@ bool MaybeRepartition(ClientContext &context, RadixHTGlobalSinkState &gstate, Ra // Check if we're approaching the memory limit auto &temporary_memory_state = *gstate.temporary_memory_state; - const auto total_size = ht.GetPartitionedData()->SizeInBytes() + ht.Capacity() * sizeof(aggr_ht_entry_t); + const auto total_size = partitioned_data->SizeInBytes() + ht.Capacity() * sizeof(aggr_ht_entry_t); idx_t thread_limit = temporary_memory_state.GetReservation() / gstate.active_threads; if (total_size > thread_limit) { // We're over the thread memory limit @@ -362,11 +364,10 @@ bool MaybeRepartition(ClientContext &context, RadixHTGlobalSinkState &gstate, Ra lock_guard guard(gstate.lock); thread_limit = temporary_memory_state.GetReservation() / gstate.active_threads; if (total_size > thread_limit) { - // Out-of-core would be triggered below, try to increase the reservation, but only up to a limit - auto new_remaining_size = - MinValue(2 * temporary_memory_state.GetRemainingSize(), - BufferManager::GetBufferManager(context).GetQueryMaxMemory() / 2); - temporary_memory_state.SetRemainingSize(context, new_remaining_size); + // Out-of-core would be triggered below, try to increase the reservation + auto remaining_size = + MaxValue(gstate.active_threads * total_size, temporary_memory_state.GetRemainingSize()); + temporary_memory_state.SetRemainingSize(context, 2 * remaining_size); thread_limit = temporary_memory_state.GetReservation() / gstate.active_threads; } } @@ -542,9 +543,12 @@ idx_t RadixPartitionedHashTable::MaxThreads(GlobalSinkState &sink_p) const { // This many partitions will fit given our reservation (at least 1)) auto partitions_fit = MaxValue(sink.temporary_memory_state->GetReservation() / sink.max_partition_size, 1); + // Maximum is either the number of partitions, or the number of threads + auto max_possible = + MinValue(sink.partitions.size(), TaskScheduler::GetScheduler(sink.context).NumberOfThreads()); - // Of course, limit it to the number of actual partitions - return MinValue(sink.partitions.size(), partitions_fit); + // Mininum of the two + return MinValue(partitions_fit, max_possible); } void RadixPartitionedHashTable::SetMultiScan(GlobalSinkState &sink_p) { @@ -640,20 +644,17 @@ bool RadixHTGlobalSourceState::AssignTask(RadixHTGlobalSinkState &sink, RadixHTL // We first try to assign a Scan task, then a Finalize task if that didn't work bool scan_assigned = false; - { - lock_guard gstate_guard(lock); - if (scan_idx < n_partitions && sink.partitions[scan_idx]->finalized) { - lstate.task_idx = scan_idx++; - scan_assigned = true; - if (scan_idx == n_partitions) { - // We will never be able to assign another task, unblock blocked tasks - lock_guard sink_guard(sink.lock); - if (!sink.blocked_tasks.empty()) { - for (auto &state : sink.blocked_tasks) { - state.Callback(); - } - sink.blocked_tasks.clear(); + if (scan_idx < n_partitions && sink.partitions[scan_idx]->finalized) { + lstate.task_idx = scan_idx++; + scan_assigned = true; + if (scan_idx == n_partitions) { + // We will never be able to assign another task, unblock blocked tasks + lock_guard sink_guard(sink.lock); + if (!sink.blocked_tasks.empty()) { + for (auto &state : sink.blocked_tasks) { + state.Callback(); } + sink.blocked_tasks.clear(); } } } @@ -668,6 +669,7 @@ bool RadixHTGlobalSourceState::AssignTask(RadixHTGlobalSinkState &sink, RadixHTL // We didn't assign a Scan task if (sink.finalize_idx >= n_partitions) { + lstate.ht.reset(); return false; // No finalize tasks left } @@ -680,7 +682,6 @@ bool RadixHTGlobalSourceState::AssignTask(RadixHTGlobalSinkState &sink, RadixHTL } // We didn't manage to assign a Finalize task because there are none left - sink.temporary_memory_state->SetRemainingSize(context, 0); return false; } @@ -750,21 +751,24 @@ void RadixHTLocalSourceState::Finalize(RadixHTGlobalSinkState &sink, RadixHTGlob partition.data->Combine(*ht->GetPartitionedData()->GetPartitions()[0]); // Mark partition as ready to scan + lock_guard glock(gstate.lock); partition.finalized = true; + if (++sink.finalize_done == sink.partitions.size()) { + // All finalizes are done, set remaining size to 0 + sink.temporary_memory_state->SetRemainingSize(sink.context, 0); + } + // Unblock blocked tasks so they can scan this partition - { - lock_guard sink_guard(sink.lock); - if (!sink.blocked_tasks.empty()) { - for (auto &state : sink.blocked_tasks) { - state.Callback(); - } - sink.blocked_tasks.clear(); + lock_guard sink_guard(sink.lock); + if (!sink.blocked_tasks.empty()) { + for (auto &state : sink.blocked_tasks) { + state.Callback(); } + sink.blocked_tasks.clear(); } // Make sure this thread's aggregate allocator does not get lost - lock_guard guard(sink.lock); sink.stored_allocators.emplace_back(ht->GetAggregateAllocator()); } @@ -896,18 +900,19 @@ SourceResultType RadixPartitionedHashTable::GetData(ExecutionContext &context, D } while (!gstate.finished && chunk.size() == 0) { - if (!lstate.TaskFinished() || gstate.AssignTask(sink, lstate)) { - lstate.ExecuteTask(sink, gstate, chunk); - } else { + if (lstate.TaskFinished()) { lock_guard gstate_guard(gstate.lock); - if (gstate.scan_idx < sink.partitions.size()) { - lock_guard sink_guard(sink.lock); - sink.blocked_tasks.push_back(input.interrupt_state); - return SourceResultType::BLOCKED; - } else { - return SourceResultType::FINISHED; + if (!gstate.AssignTask(sink, lstate)) { + if (gstate.scan_idx < sink.partitions.size()) { + lock_guard sink_guard(sink.lock); + sink.blocked_tasks.push_back(input.interrupt_state); + return SourceResultType::BLOCKED; + } else { + return SourceResultType::FINISHED; + } } } + lstate.ExecuteTask(sink, gstate, chunk); } if (chunk.size() != 0) { diff --git a/src/duckdb/src/function/aggregate/sorted_aggregate_function.cpp b/src/duckdb/src/function/aggregate/sorted_aggregate_function.cpp index 054251f33..1ba8ae453 100644 --- a/src/duckdb/src/function/aggregate/sorted_aggregate_function.cpp +++ b/src/duckdb/src/function/aggregate/sorted_aggregate_function.cpp @@ -102,8 +102,8 @@ struct SortedAggregateState { using LinkedChunkFunctions = vector; //! Capacities of the various levels of buffering - static const idx_t LIST_CAPACITY = 16; static const idx_t CHUNK_CAPACITY = STANDARD_VECTOR_SIZE; + static const idx_t LIST_CAPACITY = MinValue(16, CHUNK_CAPACITY); SortedAggregateState() : count(0), nsel(0), offset(0) { } diff --git a/src/duckdb/src/function/cast/list_casts.cpp b/src/duckdb/src/function/cast/list_casts.cpp index 13513e346..9b2e56272 100644 --- a/src/duckdb/src/function/cast/list_casts.cpp +++ b/src/duckdb/src/function/cast/list_casts.cpp @@ -172,72 +172,90 @@ static bool ListToArrayCast(Vector &source, Vector &result, idx_t count, CastPar auto &result_cc = ArrayVector::GetEntry(result); auto ldata = FlatVector::GetData(source); - bool all_lengths_match = true; - - // Now, here's where things get funky. - // We need to slice out the data from the child vector, since the list - // wont store child data for null entries, which is the case for arrays. - // So we need to preserve the "gaps" for null entries when we copy into - // the result vector. In short 'len(source_child) <= len(result_child)' - // depending on how many NULL's the source has. so we "unslice" the - // child data so that len(payload_vector) == len(result_child). - auto child_count = array_size * count; - Vector payload_vector(source_cc.GetType(), child_count); - SelectionVector sel(child_count); + SelectionVector child_sel(child_count); + + bool all_ok = true; for (idx_t i = 0; i < count; i++) { - // If the list is null, set the entire array to null if (FlatVector::IsNull(source, i)) { FlatVector::SetNull(result, i, true); for (idx_t array_elem = 0; array_elem < array_size; array_elem++) { - FlatVector::SetNull(payload_vector, i * array_size + array_elem, true); - // just select the first value, it won't be used anyway - sel.set_index(i * array_size + array_elem, 0); + FlatVector::SetNull(result_cc, i * array_size + array_elem, true); + child_sel.set_index(i * array_size + array_elem, 0); } } else if (ldata[i].length != array_size) { - if (all_lengths_match) { - // Cant cast to array, list size mismatch - all_lengths_match = false; + if (all_ok) { + all_ok = false; auto msg = StringUtil::Format("Cannot cast list with length %llu to array with length %u", ldata[i].length, array_size); HandleCastError::AssignError(msg, parameters.error_message); } FlatVector::SetNull(result, i, true); for (idx_t array_elem = 0; array_elem < array_size; array_elem++) { - FlatVector::SetNull(payload_vector, i * array_size + array_elem, true); - // just select the first value, it won't be used anyway - sel.set_index(i * array_size + array_elem, 0); + FlatVector::SetNull(result_cc, i * array_size + array_elem, true); + child_sel.set_index(i * array_size + array_elem, 0); } } else { - // Set the selection vector to point to the correct offsets for (idx_t array_elem = 0; array_elem < array_size; array_elem++) { - sel.set_index(i * array_size + array_elem, ldata[i].offset + array_elem); + child_sel.set_index(i * array_size + array_elem, ldata[i].offset + array_elem); } } } - // Perform the actual copy - VectorOperations::Copy(source_cc, payload_vector, sel, child_count, 0, 0); + CastParameters child_parameters(parameters, cast_data.child_cast_info.cast_data, parameters.local_state); - // Take a last pass and null out the child elems - for (idx_t i = 0; i < count; i++) { - if (FlatVector::IsNull(source, i) || FlatVector::IsNull(result, i)) { - for (idx_t array_elem = 0; array_elem < array_size; array_elem++) { - FlatVector::SetNull(payload_vector, i * array_size + array_elem, true); - } + // Fast path: No lists are null + // We can just cast the child vector directly + // Note: Its worth doing a CheckAllValid here, the slow path is significantly more expensive + if (FlatVector::Validity(source).CheckAllValid(count)) { + Vector payload_vector(result_cc.GetType(), child_count); + + bool ok = cast_data.child_cast_info.function(source_cc, payload_vector, child_count, child_parameters); + if (all_ok && !ok) { + all_ok = false; + HandleCastError::AssignError(*child_parameters.error_message, parameters.error_message); } + // Now do the actual copy onto the result vector, making sure to slice properly in case the lists are out of + // order + VectorOperations::Copy(payload_vector, result_cc, child_sel, child_count, 0, 0); + return all_ok; } - CastParameters child_parameters(parameters, cast_data.child_cast_info.cast_data, parameters.local_state); - bool child_succeeded = - cast_data.child_cast_info.function(payload_vector, result_cc, child_count, child_parameters); + // Slow path: Some lists are null, so we need to copy the data list by list to the right place + auto list_data = FlatVector::GetData(source); + DataChunk cast_chunk; + cast_chunk.Initialize(Allocator::DefaultAllocator(), {source_cc.GetType(), result_cc.GetType()}, array_size); + + for (idx_t i = 0; i < count; i++) { + if (FlatVector::IsNull(source, i)) { + FlatVector::SetNull(result, i, true); + // Also null the array children + for (idx_t array_elem = 0; array_elem < array_size; array_elem++) { + FlatVector::SetNull(result_cc, i * array_size + array_elem, true); + } + } else { + auto &list_cast_input = cast_chunk.data[0]; + auto &list_cast_output = cast_chunk.data[1]; + auto list_entry = list_data[i]; + + VectorOperations::Copy(source_cc, list_cast_input, list_entry.offset + array_size, list_entry.offset, + 0); + + bool ok = + cast_data.child_cast_info.function(list_cast_input, list_cast_output, array_size, child_parameters); + if (all_ok && !ok) { + all_ok = false; + HandleCastError::AssignError(*child_parameters.error_message, parameters.error_message); + } + VectorOperations::Copy(list_cast_output, result_cc, array_size, 0, i * array_size); - if (!child_succeeded) { - HandleCastError::AssignError(*child_parameters.error_message, parameters.error_message); + // Reset the cast_chunk + cast_chunk.Reset(); + } } - return all_lengths_match && child_succeeded; + return all_ok; } } @@ -259,4 +277,8 @@ BoundCastInfo DefaultCasts::ListCastSwitch(BindCastInput &input, const LogicalTy } } +/* + + */ + } // namespace duckdb diff --git a/src/duckdb/src/function/cast/struct_cast.cpp b/src/duckdb/src/function/cast/struct_cast.cpp index 3fbb46b53..4aadeee39 100644 --- a/src/duckdb/src/function/cast/struct_cast.cpp +++ b/src/duckdb/src/function/cast/struct_cast.cpp @@ -16,15 +16,42 @@ unique_ptr StructBoundCastData::BindStructToStructCast(BindCastIn if (source_child_types.size() != result_child_types.size()) { throw TypeMismatchException(source, target, "Cannot cast STRUCTs of different size"); } - for (idx_t i = 0; i < source_child_types.size(); i++) { - if (!target_is_unnamed && !source_is_unnamed && - !StringUtil::CIEquals(source_child_types[i].first, result_child_types[i].first)) { - throw TypeMismatchException(source, target, "Cannot cast STRUCTs with different names"); + bool named_struct_cast = !source_is_unnamed && !target_is_unnamed; + case_insensitive_map_t target_members; + if (named_struct_cast) { + for (idx_t i = 0; i < result_child_types.size(); i++) { + auto &target_name = result_child_types[i].first; + if (target_members.find(target_name) != target_members.end()) { + throw NotImplementedException("Error while casting - duplicate name \"%s\" in struct", target_name); + } + target_members[target_name] = i; + } + } + vector child_member_map; + child_member_map.reserve(source_child_types.size()); + for (idx_t source_idx = 0; source_idx < source_child_types.size(); source_idx++) { + auto &source_child = source_child_types[source_idx]; + idx_t target_idx; + if (named_struct_cast) { + // named struct cast - find corresponding member in target + auto entry = target_members.find(source_child.first); + if (entry == target_members.end()) { + throw TypeMismatchException(source, target, + "Cannot cast STRUCTs - element \"" + source_child.first + + "\" in source struct was not found in target struct"); + } + target_idx = entry->second; + target_members.erase(entry); + } else { + // unnamed struct cast - positionally cast elements + target_idx = source_idx; } - auto child_cast = input.GetCastFunction(source_child_types[i].second, result_child_types[i].second); + child_member_map.push_back(target_idx); + auto child_cast = input.GetCastFunction(source_child.second, result_child_types[target_idx].second); child_cast_info.push_back(std::move(child_cast)); } - return make_uniq(std::move(child_cast_info), target); + D_ASSERT(child_member_map.size() == source_child_types.size()); + return make_uniq(std::move(child_cast_info), target, std::move(child_member_map)); } unique_ptr StructBoundCastData::InitStructCastLocalState(CastLocalStateParameters ¶meters) { @@ -52,8 +79,10 @@ static bool StructToStructCast(Vector &source, Vector &result, idx_t count, Cast auto &result_children = StructVector::GetEntries(result); bool all_converted = true; for (idx_t c_idx = 0; c_idx < source_child_types.size(); c_idx++) { - auto &result_child_vector = *result_children[c_idx]; - auto &source_child_vector = *source_children[c_idx]; + auto source_idx = c_idx; + auto target_idx = cast_data.child_member_map[source_idx]; + auto &source_child_vector = *source_children[source_idx]; + auto &result_child_vector = *result_children[target_idx]; CastParameters child_parameters(parameters, cast_data.child_cast_info[c_idx].cast_data, lstate.local_states[c_idx]); if (!cast_data.child_cast_info[c_idx].function(source_child_vector, result_child_vector, count, diff --git a/src/duckdb/src/function/scalar/list/list_extract.cpp b/src/duckdb/src/function/scalar/list/list_extract.cpp index 99784f975..822b9079c 100644 --- a/src/duckdb/src/function/scalar/list/list_extract.cpp +++ b/src/duckdb/src/function/scalar/list/list_extract.cpp @@ -248,7 +248,8 @@ void ListExtractFun::RegisterFunction(BuiltinFunctions &set) { ScalarFunctionSet array_extract("array_extract"); array_extract.AddFunction(lfun); array_extract.AddFunction(sfun); - array_extract.AddFunction(StructExtractFun::GetFunction()); + array_extract.AddFunction(StructExtractFun::KeyExtractFunction()); + array_extract.AddFunction(StructExtractFun::IndexExtractFunction()); set.AddFunction(array_extract); } diff --git a/src/duckdb/src/function/scalar/list/list_zip.cpp b/src/duckdb/src/function/scalar/list/list_zip.cpp index ab5548321..3c0d40feb 100644 --- a/src/duckdb/src/function/scalar/list/list_zip.cpp +++ b/src/duckdb/src/function/scalar/list/list_zip.cpp @@ -126,21 +126,22 @@ static unique_ptr ListZipBind(ClientContext &context, ScalarFuncti // The last argument could be a flag to be set if we want a minimal list or a maximal list idx_t size = arguments.size(); + if (size == 0) { + throw BinderException("Provide at least one argument to " + bound_function.name); + } if (arguments[size - 1]->return_type.id() == LogicalTypeId::BOOLEAN) { size--; } + case_insensitive_set_t struct_names; for (idx_t i = 0; i < size; i++) { auto &child = arguments[i]; - if (child->alias.empty()) { - child->alias = "list_" + to_string(i + 1); - } switch (child->return_type.id()) { case LogicalTypeId::LIST: - struct_children.push_back(make_pair(child->alias, ListType::GetChildType(child->return_type))); + struct_children.push_back(make_pair(string(), ListType::GetChildType(child->return_type))); break; case LogicalTypeId::SQLNULL: - struct_children.push_back(make_pair(child->alias, LogicalTypeId::SQLNULL)); + struct_children.push_back(make_pair(string(), LogicalTypeId::SQLNULL)); break; default: throw ParameterNotResolvedException(); diff --git a/src/duckdb/src/function/scalar/struct/struct_extract.cpp b/src/duckdb/src/function/scalar/struct/struct_extract.cpp index f94982754..50e033b77 100644 --- a/src/duckdb/src/function/scalar/struct/struct_extract.cpp +++ b/src/duckdb/src/function/scalar/struct/struct_extract.cpp @@ -8,21 +8,18 @@ namespace duckdb { struct StructExtractBindData : public FunctionData { - StructExtractBindData(string key, idx_t index, LogicalType type) - : key(std::move(key)), index(index), type(std::move(type)) { + explicit StructExtractBindData(idx_t index) : index(index) { } - string key; idx_t index; - LogicalType type; public: unique_ptr Copy() const override { - return make_uniq(key, index, type); + return make_uniq(index); } bool Equals(const FunctionData &other_p) const override { auto &other = other_p.Cast(); - return key == other.key && index == other.index && type == other.type; + return index == other.index; } }; @@ -44,15 +41,20 @@ static void StructExtractFunction(DataChunk &args, ExpressionState &state, Vecto static unique_ptr StructExtractBind(ClientContext &context, ScalarFunction &bound_function, vector> &arguments) { D_ASSERT(bound_function.arguments.size() == 2); - if (arguments[0]->return_type.id() == LogicalTypeId::UNKNOWN) { + auto &child_type = arguments[0]->return_type; + if (child_type.id() == LogicalTypeId::UNKNOWN) { throw ParameterNotResolvedException(); } - D_ASSERT(LogicalTypeId::STRUCT == arguments[0]->return_type.id()); - auto &struct_children = StructType::GetChildTypes(arguments[0]->return_type); + D_ASSERT(LogicalTypeId::STRUCT == child_type.id()); + auto &struct_children = StructType::GetChildTypes(child_type); if (struct_children.empty()) { throw InternalException("Can't extract something from an empty struct"); } - bound_function.arguments[0] = arguments[0]->return_type; + if (StructType::IsUnnamed(child_type)) { + throw BinderException( + "struct_extract with a string key cannot be used on an unnamed struct, use a numeric index instead"); + } + bound_function.arguments[0] = child_type; auto &key_child = arguments[1]; if (key_child->HasParameter()) { @@ -95,8 +97,44 @@ static unique_ptr StructExtractBind(ClientContext &context, Scalar throw BinderException("Could not find key \"%s\" in struct\n%s", key, message); } - bound_function.return_type = return_type; - return make_uniq(std::move(key), key_index, std::move(return_type)); + bound_function.return_type = std::move(return_type); + return make_uniq(key_index); +} + +static unique_ptr StructExtractBindIndex(ClientContext &context, ScalarFunction &bound_function, + vector> &arguments) { + D_ASSERT(bound_function.arguments.size() == 2); + auto &child_type = arguments[0]->return_type; + if (child_type.id() == LogicalTypeId::UNKNOWN) { + throw ParameterNotResolvedException(); + } + D_ASSERT(LogicalTypeId::STRUCT == child_type.id()); + auto &struct_children = StructType::GetChildTypes(child_type); + if (struct_children.empty()) { + throw InternalException("Can't extract something from an empty struct"); + } + if (!StructType::IsUnnamed(child_type)) { + throw BinderException( + "struct_extract with an integer key can only be used on unnamed structs, use a string key instead"); + } + bound_function.arguments[0] = child_type; + + auto &key_child = arguments[1]; + if (key_child->HasParameter()) { + throw ParameterNotResolvedException(); + } + + if (!key_child->IsFoldable()) { + throw BinderException("Key index for struct_extract needs to be a constant value"); + } + Value key_val = ExpressionExecutor::EvaluateScalar(context, *key_child); + auto index = key_val.GetValue(); + if (index <= 0 || idx_t(index) > struct_children.size()) { + throw BinderException("Key index %lld for struct_extract out of range - expected an index between 1 and %llu", + index, struct_children.size()); + } + bound_function.return_type = struct_children[index - 1].second; + return make_uniq(index - 1); } static unique_ptr PropagateStructExtractStats(ClientContext &context, FunctionStatisticsInput &input) { @@ -108,15 +146,26 @@ static unique_ptr PropagateStructExtractStats(ClientContext &con return struct_child_stats[info.index].ToUnique(); } -ScalarFunction StructExtractFun::GetFunction() { +ScalarFunction StructExtractFun::KeyExtractFunction() { return ScalarFunction("struct_extract", {LogicalTypeId::STRUCT, LogicalType::VARCHAR}, LogicalType::ANY, StructExtractFunction, StructExtractBind, nullptr, PropagateStructExtractStats); } +ScalarFunction StructExtractFun::IndexExtractFunction() { + return ScalarFunction("struct_extract", {LogicalTypeId::STRUCT, LogicalType::BIGINT}, LogicalType::ANY, + StructExtractFunction, StructExtractBindIndex); +} + +ScalarFunctionSet StructExtractFun::GetFunctions() { + ScalarFunctionSet functions("struct_extract"); + functions.AddFunction(KeyExtractFunction()); + functions.AddFunction(IndexExtractFunction()); + return functions; +} + void StructExtractFun::RegisterFunction(BuiltinFunctions &set) { // the arguments and return types are actually set in the binder function - auto fun = GetFunction(); - set.AddFunction(fun); + set.AddFunction(GetFunctions()); } } // namespace duckdb diff --git a/src/duckdb/src/function/table/arrow.cpp b/src/duckdb/src/function/table/arrow.cpp index d3d2aa00f..8f59c89e5 100644 --- a/src/duckdb/src/function/table/arrow.cpp +++ b/src/duckdb/src/function/table/arrow.cpp @@ -210,30 +210,6 @@ unique_ptr ArrowTableFunction::GetArrowLogicalType(ArrowSchema &schem return arrow_type; } -void ArrowTableFunction::RenameArrowColumns(vector &names) { - unordered_map name_map; - for (auto &column_name : names) { - // put it all lower_case - auto low_column_name = StringUtil::Lower(column_name); - if (name_map.find(low_column_name) == name_map.end()) { - // Name does not exist yet - name_map[low_column_name]++; - } else { - // Name already exists, we add _x where x is the repetition number - string new_column_name = column_name + "_" + std::to_string(name_map[low_column_name]); - auto new_column_name_low = StringUtil::Lower(new_column_name); - while (name_map.find(new_column_name_low) != name_map.end()) { - // This name is already here due to a previous definition - name_map[low_column_name]++; - new_column_name = column_name + "_" + std::to_string(name_map[low_column_name]); - new_column_name_low = StringUtil::Lower(new_column_name); - } - column_name = new_column_name; - name_map[new_column_name_low]++; - } - } -} - void ArrowTableFunction::PopulateArrowTableType(ArrowTableType &arrow_table, ArrowSchemaWrapper &schema_p, vector &names, vector &return_types) { for (idx_t col_idx = 0; col_idx < (idx_t)schema_p.arrow_schema.n_children; col_idx++) { @@ -268,7 +244,7 @@ unique_ptr ArrowTableFunction::ArrowScanBind(ClientContext &contex auto &data = *res; stream_factory_get_schema(reinterpret_cast(stream_factory_ptr), data.schema_root.arrow_schema); PopulateArrowTableType(res->arrow_table, data.schema_root, names, return_types); - RenameArrowColumns(names); + QueryResult::DeduplicateColumns(names); res->all_types = return_types; return std::move(res); } diff --git a/src/duckdb/src/function/table/arrow_conversion.cpp b/src/duckdb/src/function/table/arrow_conversion.cpp index 17ed63b0e..40915d5dd 100644 --- a/src/duckdb/src/function/table/arrow_conversion.cpp +++ b/src/duckdb/src/function/table/arrow_conversion.cpp @@ -372,30 +372,31 @@ static void IntervalConversionMonthDayNanos(Vector &vector, ArrowArray &array, c } } +// Find the index of the first run-end that is strictly greater than the offset. +// count is returned if no such run-end is found. template static idx_t FindRunIndex(const RUN_END_TYPE *run_ends, idx_t count, idx_t offset) { + // Binary-search within the [0, count) range. For example: + // [0, 0, 0, 1, 1, 2] encoded as + // run_ends: [3, 5, 6]: + // 0, 1, 2 -> 0 + // 3, 4 -> 1 + // 5 -> 2 + // 6, 7 .. -> 3 (3 == count [not found]) idx_t begin = 0; - idx_t end = count - 1; + idx_t end = count; while (begin < end) { - idx_t middle = static_cast(std::floor((begin + end) / 2)); + idx_t middle = (begin + end) / 2; + // begin < end implies middle < end if (offset >= static_cast(run_ends[middle])) { - // Our offset starts after this run has ended + // keep searching in [middle + 1, end) begin = middle + 1; } else { - // This offset might fall into this run_end - if (middle == 0) { - return middle; - } - if (offset >= static_cast(run_ends[middle - 1])) { - // For example [0, 0, 0, 1, 1, 2] - // encoded as run_ends: [3, 5, 6] - // 3 (run_ends[0]) >= offset < 5 (run_ends[1]) - return middle; - } - end = middle - 1; + // offset < run_ends[middle], so keep searching in [begin, middle) + end = middle; } } - return end; + return begin; } template diff --git a/src/duckdb/src/function/table/pragma_detailed_profiling_output.cpp b/src/duckdb/src/function/table/pragma_detailed_profiling_output.cpp deleted file mode 100644 index e3cae0b91..000000000 --- a/src/duckdb/src/function/table/pragma_detailed_profiling_output.cpp +++ /dev/null @@ -1,173 +0,0 @@ -#include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp" -#include "duckdb/catalog/catalog_entry/view_catalog_entry.hpp" -#include "duckdb/common/limits.hpp" -#include "duckdb/common/types/column/column_data_collection.hpp" -#include "duckdb/function/table/system_functions.hpp" -#include "duckdb/main/client_context.hpp" -#include "duckdb/main/client_data.hpp" -#include "duckdb/main/query_profiler.hpp" -#include "duckdb/planner/constraints/bound_not_null_constraint.hpp" - -namespace duckdb { - -struct PragmaDetailedProfilingOutputOperatorData : public GlobalTableFunctionState { - explicit PragmaDetailedProfilingOutputOperatorData() : initialized(false) { - } - - ColumnDataScanState scan_state; - bool initialized; -}; - -struct PragmaDetailedProfilingOutputData : public TableFunctionData { - explicit PragmaDetailedProfilingOutputData(vector &types) : types(types) { - } - unique_ptr collection; - vector types; -}; - -static unique_ptr PragmaDetailedProfilingOutputBind(ClientContext &context, TableFunctionBindInput &input, - vector &return_types, - vector &names) { - names.emplace_back("OPERATOR_ID"); - return_types.emplace_back(LogicalType::INTEGER); - - names.emplace_back("ANNOTATION"); - return_types.emplace_back(LogicalType::VARCHAR); - - names.emplace_back("ID"); - return_types.emplace_back(LogicalType::INTEGER); - - names.emplace_back("NAME"); - return_types.emplace_back(LogicalType::VARCHAR); - - names.emplace_back("TIME"); - return_types.emplace_back(LogicalType::DOUBLE); - - names.emplace_back("CYCLES_PER_TUPLE"); - return_types.emplace_back(LogicalType::DOUBLE); - - names.emplace_back("SAMPLE_SIZE"); - return_types.emplace_back(LogicalType::INTEGER); - - names.emplace_back("INPUT_SIZE"); - return_types.emplace_back(LogicalType::INTEGER); - - names.emplace_back("EXTRA_INFO"); - return_types.emplace_back(LogicalType::VARCHAR); - - return make_uniq(return_types); -} - -unique_ptr PragmaDetailedProfilingOutputInit(ClientContext &context, - TableFunctionInitInput &input) { - return make_uniq(); -} - -// Insert a row into the given datachunk -static void SetValue(DataChunk &output, int index, int op_id, string annotation, int id, string name, double time, - int sample_counter, int tuple_counter, string extra_info) { - output.SetValue(0, index, op_id); - output.SetValue(1, index, std::move(annotation)); - output.SetValue(2, index, id); - output.SetValue(3, index, std::move(name)); -#if defined(RDTSC) - output.SetValue(4, index, Value(nullptr)); - output.SetValue(5, index, time); -#else - output.SetValue(4, index, time); - output.SetValue(5, index, Value(nullptr)); - -#endif - output.SetValue(6, index, sample_counter); - output.SetValue(7, index, tuple_counter); - output.SetValue(8, index, std::move(extra_info)); -} - -static void ExtractFunctions(ColumnDataCollection &collection, ExpressionInfo &info, DataChunk &chunk, int op_id, - int &fun_id) { - if (info.hasfunction) { - D_ASSERT(info.sample_tuples_count != 0); - SetValue(chunk, chunk.size(), op_id, "Function", fun_id++, info.function_name, - int(info.function_time) / double(info.sample_tuples_count), info.sample_tuples_count, - info.tuples_count, ""); - - chunk.SetCardinality(chunk.size() + 1); - if (chunk.size() == STANDARD_VECTOR_SIZE) { - collection.Append(chunk); - chunk.Reset(); - } - } - if (info.children.empty()) { - return; - } - // extract the children of this node - for (auto &child : info.children) { - ExtractFunctions(collection, *child, chunk, op_id, fun_id); - } -} - -static void PragmaDetailedProfilingOutputFunction(ClientContext &context, TableFunctionInput &data_p, - DataChunk &output) { - auto &state = data_p.global_state->Cast(); - auto &data = data_p.bind_data->CastNoConst(); - - if (!state.initialized) { - // create a ColumnDataCollection - auto collection = make_uniq(context, data.types); - - // create a chunk - DataChunk chunk; - chunk.Initialize(context, data.types); - - // Initialize ids - int operator_counter = 1; - int function_counter = 1; - int expression_counter = 1; - auto &client_data = ClientData::Get(context); - if (client_data.query_profiler_history->GetPrevProfilers().empty()) { - return; - } - // For each Operator - auto &tree_map = client_data.query_profiler_history->GetPrevProfilers().back().second->GetTreeMap(); - for (auto op : tree_map) { - // For each Expression Executor - for (auto &expr_executor : op.second.get().info.executors_info) { - // For each Expression tree - if (!expr_executor) { - continue; - } - for (auto &expr_timer : expr_executor->roots) { - D_ASSERT(expr_timer->sample_tuples_count != 0); - SetValue(chunk, chunk.size(), operator_counter, "ExpressionRoot", expression_counter++, - // Sometimes, cycle counter is not accurate, too big or too small. return 0 for - // those cases - expr_timer->name, int(expr_timer->time) / double(expr_timer->sample_tuples_count), - expr_timer->sample_tuples_count, expr_timer->tuples_count, expr_timer->extra_info); - // Increment cardinality - chunk.SetCardinality(chunk.size() + 1); - // Check whether data chunk is full or not - if (chunk.size() == STANDARD_VECTOR_SIZE) { - collection->Append(chunk); - chunk.Reset(); - } - // Extract all functions inside the tree - ExtractFunctions(*collection, *expr_timer->root, chunk, operator_counter, function_counter); - } - } - operator_counter++; - } - collection->Append(chunk); - data.collection = std::move(collection); - data.collection->InitializeScan(state.scan_state); - state.initialized = true; - } - - data.collection->Scan(state.scan_state, output); -} - -void PragmaDetailedProfilingOutput::RegisterFunction(BuiltinFunctions &set) { - set.AddFunction(TableFunction("pragma_detailed_profiling_output", {}, PragmaDetailedProfilingOutputFunction, - PragmaDetailedProfilingOutputBind, PragmaDetailedProfilingOutputInit)); -} - -} // namespace duckdb diff --git a/src/duckdb/src/function/table/pragma_last_profiling_output.cpp b/src/duckdb/src/function/table/pragma_last_profiling_output.cpp deleted file mode 100644 index c822f3ddb..000000000 --- a/src/duckdb/src/function/table/pragma_last_profiling_output.cpp +++ /dev/null @@ -1,101 +0,0 @@ -#include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp" -#include "duckdb/catalog/catalog_entry/view_catalog_entry.hpp" -#include "duckdb/common/limits.hpp" -#include "duckdb/common/types/column/column_data_collection.hpp" -#include "duckdb/function/table/system_functions.hpp" -#include "duckdb/main/client_context.hpp" -#include "duckdb/main/client_data.hpp" -#include "duckdb/main/query_profiler.hpp" -#include "duckdb/planner/constraints/bound_not_null_constraint.hpp" - -namespace duckdb { - -struct PragmaLastProfilingOutputOperatorData : public GlobalTableFunctionState { - PragmaLastProfilingOutputOperatorData() : initialized(false) { - } - - ColumnDataScanState scan_state; - bool initialized; -}; - -struct PragmaLastProfilingOutputData : public TableFunctionData { - explicit PragmaLastProfilingOutputData(vector &types) : types(types) { - } - unique_ptr collection; - vector types; -}; - -static unique_ptr PragmaLastProfilingOutputBind(ClientContext &context, TableFunctionBindInput &input, - vector &return_types, - vector &names) { - names.emplace_back("OPERATOR_ID"); - return_types.emplace_back(LogicalType::INTEGER); - - names.emplace_back("NAME"); - return_types.emplace_back(LogicalType::VARCHAR); - - names.emplace_back("TIME"); - return_types.emplace_back(LogicalType::DOUBLE); - - names.emplace_back("CARDINALITY"); - return_types.emplace_back(LogicalType::BIGINT); - - names.emplace_back("DESCRIPTION"); - return_types.emplace_back(LogicalType::VARCHAR); - - return make_uniq(return_types); -} - -static void SetValue(DataChunk &output, int index, int op_id, string name, double time, int64_t car, - string description) { - output.SetValue(0, index, op_id); - output.SetValue(1, index, std::move(name)); - output.SetValue(2, index, time); - output.SetValue(3, index, car); - output.SetValue(4, index, std::move(description)); -} - -unique_ptr PragmaLastProfilingOutputInit(ClientContext &context, - TableFunctionInitInput &input) { - return make_uniq(); -} - -static void PragmaLastProfilingOutputFunction(ClientContext &context, TableFunctionInput &data_p, DataChunk &output) { - auto &state = data_p.global_state->Cast(); - auto &data = data_p.bind_data->CastNoConst(); - if (!state.initialized) { - // create a ColumnDataCollection - auto collection = make_uniq(context, data.types); - - DataChunk chunk; - chunk.Initialize(context, data.types); - int operator_counter = 1; - auto &client_data = ClientData::Get(context); - if (!client_data.query_profiler_history->GetPrevProfilers().empty()) { - auto &tree_map = client_data.query_profiler_history->GetPrevProfilers().back().second->GetTreeMap(); - for (auto op : tree_map) { - auto &tree_info = op.second.get(); - SetValue(chunk, chunk.size(), operator_counter++, tree_info.name, tree_info.info.time, - tree_info.info.elements, " "); - chunk.SetCardinality(chunk.size() + 1); - if (chunk.size() == STANDARD_VECTOR_SIZE) { - collection->Append(chunk); - chunk.Reset(); - } - } - } - collection->Append(chunk); - data.collection = std::move(collection); - data.collection->InitializeScan(state.scan_state); - state.initialized = true; - } - - data.collection->Scan(state.scan_state, output); -} - -void PragmaLastProfilingOutput::RegisterFunction(BuiltinFunctions &set) { - set.AddFunction(TableFunction("pragma_last_profiling_output", {}, PragmaLastProfilingOutputFunction, - PragmaLastProfilingOutputBind, PragmaLastProfilingOutputInit)); -} - -} // namespace duckdb diff --git a/src/duckdb/src/function/table/range.cpp b/src/duckdb/src/function/table/range.cpp index 3e47bf111..ecb65eb99 100644 --- a/src/duckdb/src/function/table/range.cpp +++ b/src/duckdb/src/function/table/range.cpp @@ -221,9 +221,6 @@ static void RangeDateTimeFunction(ClientContext &context, TableFunctionInput &da idx_t size = 0; auto data = FlatVector::GetData(output.data[0]); while (true) { - data[size++] = state.current_state; - state.current_state = - AddOperator::Operation(state.current_state, bind_data.increment); if (bind_data.Finished(state.current_state)) { state.finished = true; break; @@ -231,6 +228,9 @@ static void RangeDateTimeFunction(ClientContext &context, TableFunctionInput &da if (size >= STANDARD_VECTOR_SIZE) { break; } + data[size++] = state.current_state; + state.current_state = + AddOperator::Operation(state.current_state, bind_data.increment); } output.SetCardinality(size); } diff --git a/src/duckdb/src/function/table/read_csv.cpp b/src/duckdb/src/function/table/read_csv.cpp index 47dff231b..98cfa18a0 100644 --- a/src/duckdb/src/function/table/read_csv.cpp +++ b/src/duckdb/src/function/table/read_csv.cpp @@ -106,6 +106,8 @@ static unique_ptr ReadCSVBind(ClientContext &context, TableFunctio names = sniffer_result.names; return_types = sniffer_result.return_types; } + result->csv_types = return_types; + result->csv_names = names; } D_ASSERT(return_types.size() == names.size()); @@ -131,13 +133,16 @@ static unique_ptr ReadCSVBind(ClientContext &context, TableFunctio } } } + result->csv_types = return_types; + result->csv_names = names; } else { + result->csv_types = return_types; + result->csv_names = names; result->reader_bind = MultiFileReader::BindOptions(options.file_options, result->files, return_types, names); } result->return_types = return_types; result->return_names = names; - result->csv_types = return_types; - result->csv_names = names; + result->FinalizeRead(context); return std::move(result); } diff --git a/src/duckdb/src/function/table/system/duckdb_columns.cpp b/src/duckdb/src/function/table/system/duckdb_columns.cpp index 922d6d32e..cee5e1f49 100644 --- a/src/duckdb/src/function/table/system/duckdb_columns.cpp +++ b/src/duckdb/src/function/table/system/duckdb_columns.cpp @@ -166,7 +166,7 @@ class ViewColumnHelper : public ColumnHelper { return entry.types.size(); } const string &ColumnName(idx_t col) override { - return entry.aliases[col]; + return col < entry.aliases.size() ? entry.aliases[col] : entry.names[col]; } const LogicalType &ColumnType(idx_t col) override { return entry.types[col]; diff --git a/src/duckdb/src/function/table/system/duckdb_memory.cpp b/src/duckdb/src/function/table/system/duckdb_memory.cpp new file mode 100644 index 000000000..9117c91a9 --- /dev/null +++ b/src/duckdb/src/function/table/system/duckdb_memory.cpp @@ -0,0 +1,63 @@ +#include "duckdb/function/table/system_functions.hpp" +#include "duckdb/storage/buffer_manager.hpp" + +namespace duckdb { + +struct DuckDBMemoryData : public GlobalTableFunctionState { + DuckDBMemoryData() : offset(0) { + } + + vector entries; + idx_t offset; +}; + +static unique_ptr DuckDBMemoryBind(ClientContext &context, TableFunctionBindInput &input, + vector &return_types, vector &names) { + names.emplace_back("tag"); + return_types.emplace_back(LogicalType::VARCHAR); + + names.emplace_back("memory_usage_bytes"); + return_types.emplace_back(LogicalType::BIGINT); + + names.emplace_back("temporary_storage_bytes"); + return_types.emplace_back(LogicalType::BIGINT); + + return nullptr; +} + +unique_ptr DuckDBMemoryInit(ClientContext &context, TableFunctionInitInput &input) { + auto result = make_uniq(); + + result->entries = BufferManager::GetBufferManager(context).GetMemoryUsageInfo(); + return std::move(result); +} + +void DuckDBMemoryFunction(ClientContext &context, TableFunctionInput &data_p, DataChunk &output) { + auto &data = data_p.global_state->Cast(); + if (data.offset >= data.entries.size()) { + // finished returning values + return; + } + // start returning values + // either fill up the chunk or return all the remaining columns + idx_t count = 0; + while (data.offset < data.entries.size() && count < STANDARD_VECTOR_SIZE) { + auto &entry = data.entries[data.offset++]; + // return values: + idx_t col = 0; + // tag, VARCHAR + output.SetValue(col++, count, EnumUtil::ToString(entry.tag)); + // memory_usage_bytes, BIGINT + output.SetValue(col++, count, Value::BIGINT(entry.size)); + // temporary_storage_bytes, BIGINT + output.SetValue(col++, count, Value::BIGINT(entry.evicted_data)); + count++; + } + output.SetCardinality(count); +} + +void DuckDBMemoryFun::RegisterFunction(BuiltinFunctions &set) { + set.AddFunction(TableFunction("duckdb_memory", {}, DuckDBMemoryFunction, DuckDBMemoryBind, DuckDBMemoryInit)); +} + +} // namespace duckdb diff --git a/src/duckdb/src/function/table/system/duckdb_secrets.cpp b/src/duckdb/src/function/table/system/duckdb_secrets.cpp index 7cdf0ecb1..e97a5d11e 100644 --- a/src/duckdb/src/function/table/system/duckdb_secrets.cpp +++ b/src/duckdb/src/function/table/system/duckdb_secrets.cpp @@ -97,17 +97,17 @@ void DuckDBSecretsFunction(ClientContext &context, TableFunctionInput &data_p, D auto &secret_entry = secrets[data.offset]; vector scope_value; - for (const auto &scope_entry : secret_entry.get().secret->GetScope()) { + for (const auto &scope_entry : secret_entry.secret->GetScope()) { scope_value.push_back(scope_entry); } - const auto &secret = *secret_entry.get().secret; + const auto &secret = *secret_entry.secret; output.SetValue(0, count, secret.GetName()); output.SetValue(1, count, Value(secret.GetType())); output.SetValue(2, count, Value(secret.GetProvider())); - output.SetValue(3, count, Value(secret_entry.get().persist_type == SecretPersistType::PERSISTENT)); - output.SetValue(4, count, Value(secret_entry.get().storage_mode)); + output.SetValue(3, count, Value(secret_entry.persist_type == SecretPersistType::PERSISTENT)); + output.SetValue(4, count, Value(secret_entry.storage_mode)); output.SetValue(5, count, Value::LIST(LogicalType::VARCHAR, scope_value)); output.SetValue(6, count, secret.ToString(bind_data.redact)); diff --git a/src/duckdb/src/function/table/system/pragma_table_info.cpp b/src/duckdb/src/function/table/system/pragma_table_info.cpp index a2d441269..64ff4694f 100644 --- a/src/duckdb/src/function/table/system/pragma_table_info.cpp +++ b/src/duckdb/src/function/table/system/pragma_table_info.cpp @@ -251,7 +251,7 @@ static void PragmaTableInfoView(PragmaTableOperatorData &data, ViewCatalogEntry for (idx_t i = data.offset; i < next; i++) { auto index = i - data.offset; auto type = view.types[i]; - auto &name = view.aliases[i]; + auto &name = i < view.aliases.size() ? view.aliases[i] : view.names[i]; if (is_table_info) { PragmaTableInfoHelper::GetViewColumns(i, name, type, output, index); diff --git a/src/duckdb/src/function/table/system/test_all_types.cpp b/src/duckdb/src/function/table/system/test_all_types.cpp index da43d20ec..53e41672a 100644 --- a/src/duckdb/src/function/table/system/test_all_types.cpp +++ b/src/duckdb/src/function/table/system/test_all_types.cpp @@ -196,7 +196,7 @@ vector TestAllTypesFun::GetTestTypes(bool use_large_enum) { map_struct1.push_back(make_pair("value", Value("🦆🦆🦆🦆🦆🦆"))); child_list_t map_struct2; map_struct2.push_back(make_pair("key", Value("key2"))); - map_struct2.push_back(make_pair("key", Value("goose"))); + map_struct2.push_back(make_pair("value", Value("goose"))); vector map_values; map_values.push_back(Value::STRUCT(map_struct1)); diff --git a/src/duckdb/src/function/table/system_functions.cpp b/src/duckdb/src/function/table/system_functions.cpp index ee4ca0821..a9c191543 100644 --- a/src/duckdb/src/function/table/system_functions.cpp +++ b/src/duckdb/src/function/table/system_functions.cpp @@ -16,8 +16,6 @@ void BuiltinFunctions::RegisterSQLiteFunctions() { PragmaStorageInfo::RegisterFunction(*this); PragmaMetadataInfo::RegisterFunction(*this); PragmaDatabaseSize::RegisterFunction(*this); - PragmaLastProfilingOutput::RegisterFunction(*this); - PragmaDetailedProfilingOutput::RegisterFunction(*this); PragmaUserAgent::RegisterFunction(*this); DuckDBColumnsFun::RegisterFunction(*this); @@ -29,6 +27,7 @@ void BuiltinFunctions::RegisterSQLiteFunctions() { DuckDBSchemasFun::RegisterFunction(*this); DuckDBDependenciesFun::RegisterFunction(*this); DuckDBExtensionsFun::RegisterFunction(*this); + DuckDBMemoryFun::RegisterFunction(*this); DuckDBOptimizersFun::RegisterFunction(*this); DuckDBSecretsFun::RegisterFunction(*this); DuckDBSequencesFun::RegisterFunction(*this); diff --git a/src/duckdb/src/function/table/version/pragma_version.cpp b/src/duckdb/src/function/table/version/pragma_version.cpp index 8d37c6b57..654dbaa4c 100644 --- a/src/duckdb/src/function/table/version/pragma_version.cpp +++ b/src/duckdb/src/function/table/version/pragma_version.cpp @@ -1,8 +1,8 @@ #ifndef DUCKDB_VERSION -#define DUCKDB_VERSION "v0.9.3-dev3861" +#define DUCKDB_VERSION "v0.10.1-dev2" #endif #ifndef DUCKDB_SOURCE_ID -#define DUCKDB_SOURCE_ID "a20a91c5a4" +#define DUCKDB_SOURCE_ID "583508495b" #endif #include "duckdb/function/table/system_functions.hpp" #include "duckdb/main/database.hpp" diff --git a/src/duckdb/src/include/duckdb/catalog/catalog_entry/view_catalog_entry.hpp b/src/duckdb/src/include/duckdb/catalog/catalog_entry/view_catalog_entry.hpp index 459ed527e..554f13fcc 100644 --- a/src/duckdb/src/include/duckdb/catalog/catalog_entry/view_catalog_entry.hpp +++ b/src/duckdb/src/include/duckdb/catalog/catalog_entry/view_catalog_entry.hpp @@ -36,6 +36,8 @@ class ViewCatalogEntry : public StandardEntry { vector aliases; //! The returned types of the view vector types; + //! The returned names of the view + vector names; public: unique_ptr GetInfo() const override; diff --git a/src/duckdb/src/include/duckdb/common/enum_util.hpp b/src/duckdb/src/include/duckdb/common/enum_util.hpp index 9587715c2..66a34ad8b 100644 --- a/src/duckdb/src/include/duckdb/common/enum_util.hpp +++ b/src/duckdb/src/include/duckdb/common/enum_util.hpp @@ -166,6 +166,8 @@ enum class MacroType : uint8_t; enum class MapInvalidReason : uint8_t; +enum class MemoryTag : uint8_t; + enum class NType : uint8_t; enum class NewLineIdentifier : uint8_t; @@ -306,8 +308,6 @@ enum class WindowBoundary : uint8_t; enum class WindowExcludeMode : uint8_t; -enum class WithinCollection : uint8_t; - template<> const char* EnumUtil::ToChars(AccessMode value); @@ -510,6 +510,9 @@ const char* EnumUtil::ToChars(MacroType value); template<> const char* EnumUtil::ToChars(MapInvalidReason value); +template<> +const char* EnumUtil::ToChars(MemoryTag value); + template<> const char* EnumUtil::ToChars(NType value); @@ -720,9 +723,6 @@ const char* EnumUtil::ToChars(WindowBoundary value); template<> const char* EnumUtil::ToChars(WindowExcludeMode value); -template<> -const char* EnumUtil::ToChars(WithinCollection value); - template<> AccessMode EnumUtil::FromString(const char *value); @@ -925,6 +925,9 @@ MacroType EnumUtil::FromString(const char *value); template<> MapInvalidReason EnumUtil::FromString(const char *value); +template<> +MemoryTag EnumUtil::FromString(const char *value); + template<> NType EnumUtil::FromString(const char *value); @@ -1135,8 +1138,5 @@ WindowBoundary EnumUtil::FromString(const char *value); template<> WindowExcludeMode EnumUtil::FromString(const char *value); -template<> -WithinCollection EnumUtil::FromString(const char *value); - } diff --git a/src/duckdb/src/include/duckdb/common/enums/memory_tag.hpp b/src/duckdb/src/include/duckdb/common/enums/memory_tag.hpp new file mode 100644 index 000000000..109332b17 --- /dev/null +++ b/src/duckdb/src/include/duckdb/common/enums/memory_tag.hpp @@ -0,0 +1,32 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/common/enums/memory_tag.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/constants.hpp" + +namespace duckdb { + +enum class MemoryTag : uint8_t { + BASE_TABLE = 0, + HASH_TABLE = 1, + PARQUET_READER = 2, + CSV_READER = 3, + ORDER_BY = 4, + ART_INDEX = 5, + COLUMN_DATA = 6, + METADATA = 7, + OVERFLOW_STRINGS = 8, + IN_MEMORY_TABLE = 9, + ALLOCATOR = 10, + EXTENSION = 11 +}; + +static constexpr const idx_t MEMORY_TAG_COUNT = 12; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/exception.hpp b/src/duckdb/src/include/duckdb/common/exception.hpp index bd5f1857a..b23fb9709 100644 --- a/src/duckdb/src/include/duckdb/common/exception.hpp +++ b/src/duckdb/src/include/duckdb/common/exception.hpp @@ -222,12 +222,18 @@ class DependencyException : public Exception { class IOException : public Exception { public: DUCKDB_API explicit IOException(const string &msg); + DUCKDB_API explicit IOException(const string &msg, const unordered_map &extra_info); explicit IOException(ExceptionType exception_type, const string &msg) : Exception(exception_type, msg) { } template explicit IOException(const string &msg, Args... params) : IOException(ConstructMessage(msg, params...)) { } + + template + explicit IOException(const string &msg, const unordered_map &extra_info, Args... params) + : IOException(ConstructMessage(msg, params...), extra_info) { + } }; class MissingExtensionException : public Exception { diff --git a/src/duckdb/src/include/duckdb/common/helper.hpp b/src/duckdb/src/include/duckdb/common/helper.hpp index bfc994e5c..e1f86c3d3 100644 --- a/src/duckdb/src/include/duckdb/common/helper.hpp +++ b/src/duckdb/src/include/duckdb/common/helper.hpp @@ -146,7 +146,7 @@ static duckdb::unique_ptr make_unique(_Args&&... __args) { } template -T MaxValue(T a, T b) { +constexpr T MaxValue(T a, T b) { return a > b ? a : b; } diff --git a/src/duckdb/src/include/duckdb/common/http_state.hpp b/src/duckdb/src/include/duckdb/common/http_state.hpp index 287d5a377..721628692 100644 --- a/src/duckdb/src/include/duckdb/common/http_state.hpp +++ b/src/duckdb/src/include/duckdb/common/http_state.hpp @@ -13,6 +13,7 @@ #include "duckdb/main/client_data.hpp" #include "duckdb/common/atomic.hpp" #include "duckdb/common/optional_ptr.hpp" +#include "duckdb/main/client_context_state.hpp" namespace duckdb { @@ -98,7 +99,7 @@ class HTTPState : public ClientContextState { atomic total_bytes_sent {0}; //! Called by the ClientContext when the current query ends - void QueryEnd() override { + void QueryEnd(ClientContext &context) override { Reset(); } diff --git a/src/duckdb/src/include/duckdb/common/operator/integer_cast_operator.hpp b/src/duckdb/src/include/duckdb/common/operator/integer_cast_operator.hpp index 10953fe20..28a5125f3 100644 --- a/src/duckdb/src/include/duckdb/common/operator/integer_cast_operator.hpp +++ b/src/duckdb/src/include/duckdb/common/operator/integer_cast_operator.hpp @@ -267,6 +267,9 @@ static bool IntegerCastLoop(const char *buf, idx_t len, T &result, bool strict) } if (ALLOW_EXPONENT) { if (buf[pos] == 'e' || buf[pos] == 'E') { + if (strict) { + return false; + } if (pos == start_pos) { return false; } diff --git a/src/duckdb/src/include/duckdb/common/row_operations/row_operations.hpp b/src/duckdb/src/include/duckdb/common/row_operations/row_operations.hpp index 095c8b989..9347a9f6e 100644 --- a/src/duckdb/src/include/duckdb/common/row_operations/row_operations.hpp +++ b/src/duckdb/src/include/duckdb/common/row_operations/row_operations.hpp @@ -25,6 +25,20 @@ class StringHeap; class Vector; struct UnifiedVectorFormat; +// The NestedValidity class help to set/get the validity from inside nested vectors +class NestedValidity { + data_ptr_t list_validity_location; + data_ptr_t *struct_validity_locations; + idx_t entry_idx; + idx_t idx_in_entry; + +public: + explicit NestedValidity(data_ptr_t validitymask_location); + NestedValidity(data_ptr_t *validitymask_locations, idx_t child_vector_index); + void SetInvalid(idx_t idx); + bool IsValid(idx_t idx); +}; + struct RowOperationsState { explicit RowOperationsState(ArenaAllocator &allocator) : allocator(allocator) { } @@ -91,15 +105,15 @@ struct RowOperations { static void ComputeEntrySizes(Vector &v, UnifiedVectorFormat &vdata, idx_t entry_sizes[], idx_t vcount, idx_t ser_count, const SelectionVector &sel, idx_t offset = 0); //! Scatter vector with variable size type to the heap. - static void HeapScatter(Vector &v, idx_t vcount, const SelectionVector &sel, idx_t ser_count, idx_t col_idx, - data_ptr_t *key_locations, data_ptr_t *validitymask_locations, idx_t offset = 0); + static void HeapScatter(Vector &v, idx_t vcount, const SelectionVector &sel, idx_t ser_count, + data_ptr_t *key_locations, optional_ptr parent_validity, idx_t offset = 0); //! Scatter vector data with variable size type to the heap. static void HeapScatterVData(UnifiedVectorFormat &vdata, PhysicalType type, const SelectionVector &sel, - idx_t ser_count, idx_t col_idx, data_ptr_t *key_locations, - data_ptr_t *validitymask_locations, idx_t offset = 0); + idx_t ser_count, data_ptr_t *key_locations, + optional_ptr parent_validity, idx_t offset = 0); //! Gather a single column with variable size type from the heap. - static void HeapGather(Vector &v, const idx_t &vcount, const SelectionVector &sel, const idx_t &col_idx, - data_ptr_t key_locations[], data_ptr_t validitymask_locations[]); + static void HeapGather(Vector &v, const idx_t &vcount, const SelectionVector &sel, data_ptr_t key_locations[], + optional_ptr parent_validity); //===--------------------------------------------------------------------===// // Sorting Operators diff --git a/src/duckdb/src/include/duckdb/common/string_util.hpp b/src/duckdb/src/include/duckdb/common/string_util.hpp index bef968e02..e5eb9cd62 100644 --- a/src/duckdb/src/include/duckdb/common/string_util.hpp +++ b/src/duckdb/src/include/duckdb/common/string_util.hpp @@ -257,6 +257,11 @@ class StringUtil { //! NOTE: this method is not efficient DUCKDB_API static string ToJSONMap(ExceptionType type, const string &message, const unordered_map &map); + + DUCKDB_API static string GetFileName(const string &file_path); + DUCKDB_API static string GetFileExtension(const string &file_name); + DUCKDB_API static string GetFileStem(const string &file_name); + DUCKDB_API static string GetFilePath(const string &file_path); }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/types.hpp b/src/duckdb/src/include/duckdb/common/types.hpp index 8c12ff83c..04c0e96fa 100644 --- a/src/duckdb/src/include/duckdb/common/types.hpp +++ b/src/duckdb/src/include/duckdb/common/types.hpp @@ -332,6 +332,10 @@ struct LogicalType { DUCKDB_API bool IsValid() const; + template + bool Contains(F &&predicate) const; + bool Contains(LogicalTypeId type_id) const; + private: LogicalTypeId id_; PhysicalType physical_type_; @@ -470,6 +474,8 @@ struct ArrayType { DUCKDB_API static idx_t GetSize(const LogicalType &type); DUCKDB_API static bool IsAnySize(const LogicalType &type); DUCKDB_API static constexpr idx_t MAX_ARRAY_SIZE = 100000; // 100k for now + //! Recursively replace all ARRAY types to LIST types within the given type + DUCKDB_API static LogicalType ConvertToList(const LogicalType &type); }; struct AggregateStateType { @@ -524,4 +530,37 @@ struct aggregate_state_t { vector bound_argument_types; }; +template +bool LogicalType::Contains(F &&predicate) const { + if(predicate(*this)) { + return true; + } + switch(id()) { + case LogicalTypeId::STRUCT: { + for(const auto &child : StructType::GetChildTypes(*this)) { + if(child.second.Contains(predicate)) { + return true; + } + } + } + break; + case LogicalTypeId::LIST: + return ListType::GetChildType(*this).Contains(predicate); + case LogicalTypeId::MAP: + return MapType::KeyType(*this).Contains(predicate) || MapType::ValueType(*this).Contains(predicate); + case LogicalTypeId::UNION: + for(const auto &child : UnionType::CopyMemberTypes(*this)) { + if(child.second.Contains(predicate)) { + return true; + } + } + break; + case LogicalTypeId::ARRAY: + return ArrayType::GetChildType(*this).Contains(predicate); + default: + return false; + } + return false; +} + } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/common/types/row/row_data_collection.hpp b/src/duckdb/src/include/duckdb/common/types/row/row_data_collection.hpp index acdd6de3b..490e81d92 100644 --- a/src/duckdb/src/include/duckdb/common/types/row/row_data_collection.hpp +++ b/src/duckdb/src/include/duckdb/common/types/row/row_data_collection.hpp @@ -17,10 +17,10 @@ namespace duckdb { struct RowDataBlock { public: - RowDataBlock(BufferManager &buffer_manager, idx_t capacity, idx_t entry_size) + RowDataBlock(MemoryTag tag, BufferManager &buffer_manager, idx_t capacity, idx_t entry_size) : capacity(capacity), entry_size(entry_size), count(0), byte_offset(0) { idx_t size = MaxValue(Storage::BLOCK_SIZE, capacity * entry_size); - buffer_manager.Allocate(size, false, &block); + buffer_manager.Allocate(tag, size, false, &block); D_ASSERT(BufferManager::GetAllocSize(size) == block->GetMemoryUsage()); } explicit RowDataBlock(idx_t entry_size) : entry_size(entry_size) { diff --git a/src/duckdb/src/include/duckdb/common/types/row/tuple_data_collection.hpp b/src/duckdb/src/include/duckdb/common/types/row/tuple_data_collection.hpp index 5c5aae636..01fc20ec3 100644 --- a/src/duckdb/src/include/duckdb/common/types/row/tuple_data_collection.hpp +++ b/src/duckdb/src/include/duckdb/common/types/row/tuple_data_collection.hpp @@ -33,7 +33,7 @@ struct TupleDataScatterFunction { typedef void (*tuple_data_gather_function_t)(const TupleDataLayout &layout, Vector &row_locations, const idx_t col_idx, const SelectionVector &scan_sel, const idx_t scan_count, Vector &target, - const SelectionVector &target_sel, Vector &list_vector, + const SelectionVector &target_sel, optional_ptr list_vector, const vector &child_functions); struct TupleDataGatherFunction { @@ -41,12 +41,6 @@ struct TupleDataGatherFunction { vector child_functions; }; -enum class WithinCollection : uint8_t { - NO, - LIST, - ARRAY, -}; - //! TupleDataCollection represents a set of buffer-managed data stored in row format //! FIXME: rename to RowDataCollection after we phase it out class TupleDataCollection { @@ -74,11 +68,9 @@ class TupleDataCollection { void Unpin(); //! Gets the scatter function for the given type - static TupleDataScatterFunction GetScatterFunction(const LogicalType &type, - WithinCollection within = WithinCollection::NO); + static TupleDataScatterFunction GetScatterFunction(const LogicalType &type, bool within_collection = false); //! Gets the gather function for the given type - static TupleDataGatherFunction GetGatherFunction(const LogicalType &type, - WithinCollection within = WithinCollection::NO); + static TupleDataGatherFunction GetGatherFunction(const LogicalType &type); //! Initializes an Append state - useful for optimizing many appends made to the same tuple data collection void InitializeAppend(TupleDataAppendState &append_state, @@ -176,15 +168,16 @@ class TupleDataCollection { //! Gathers a DataChunk from the TupleDataCollection, given the specific row locations (requires full pin) void Gather(Vector &row_locations, const SelectionVector &scan_sel, const idx_t scan_count, DataChunk &result, - const SelectionVector &target_sel) const; + const SelectionVector &target_sel, vector> &cached_cast_vectors) const; //! Gathers a DataChunk (only the columns given by column_ids) from the TupleDataCollection, //! given the specific row locations (requires full pin) void Gather(Vector &row_locations, const SelectionVector &scan_sel, const idx_t scan_count, - const vector &column_ids, DataChunk &result, const SelectionVector &target_sel) const; + const vector &column_ids, DataChunk &result, const SelectionVector &target_sel, + vector> &cached_cast_vectors) const; //! Gathers a Vector (from the given column id) from the TupleDataCollection //! given the specific row locations (requires full pin) void Gather(Vector &row_locations, const SelectionVector &sel, const idx_t scan_count, const column_t column_id, - Vector &result, const SelectionVector &target_sel) const; + Vector &result, const SelectionVector &target_sel, optional_ptr cached_cast_vector) const; //! Converts this TupleDataCollection to a string representation string ToString(); diff --git a/src/duckdb/src/include/duckdb/common/types/row/tuple_data_states.hpp b/src/duckdb/src/include/duckdb/common/types/row/tuple_data_states.hpp index f2852d3b4..c3923f085 100644 --- a/src/duckdb/src/include/duckdb/common/types/row/tuple_data_states.hpp +++ b/src/duckdb/src/include/duckdb/common/types/row/tuple_data_states.hpp @@ -11,6 +11,7 @@ #include "duckdb/common/mutex.hpp" #include "duckdb/common/perfect_map_set.hpp" #include "duckdb/common/types.hpp" +#include "duckdb/common/types/vector_cache.hpp" namespace duckdb { @@ -48,7 +49,6 @@ struct TupleDataVectorFormat { // Optional: only used for ArrayVector to fake being a list vector unique_array array_list_entries; - idx_t parent_array_size; }; struct TupleDataChunkState { @@ -58,6 +58,9 @@ struct TupleDataChunkState { Vector row_locations = Vector(LogicalType::POINTER); Vector heap_locations = Vector(LogicalType::POINTER); Vector heap_sizes = Vector(LogicalType::UBIGINT); + + vector> cached_cast_vectors; + vector> cached_cast_vector_cache; }; struct TupleDataAppendState { diff --git a/src/duckdb/src/include/duckdb/common/types/validity_mask.hpp b/src/duckdb/src/include/duckdb/common/types/validity_mask.hpp index 776ed535a..b7dd548cf 100644 --- a/src/duckdb/src/include/duckdb/common/types/validity_mask.hpp +++ b/src/duckdb/src/include/duckdb/common/types/validity_mask.hpp @@ -135,10 +135,10 @@ struct TemplatedValidityMask { inline V *GetData() const { return validity_mask; } - inline void Reset() { + inline void Reset(idx_t target_count_p = STANDARD_VECTOR_SIZE) { validity_mask = nullptr; validity_data.reset(); - target_count = STANDARD_VECTOR_SIZE; + target_count = target_count_p; } static inline idx_t EntryCount(idx_t count) { diff --git a/src/duckdb/src/include/duckdb/common/types/vector.hpp b/src/duckdb/src/include/duckdb/common/types/vector.hpp index dd0152583..516445157 100644 --- a/src/duckdb/src/include/duckdb/common/types/vector.hpp +++ b/src/duckdb/src/include/duckdb/common/types/vector.hpp @@ -461,11 +461,6 @@ struct ArrayVector { DUCKDB_API static Vector &GetEntry(Vector &vector); //! Gets the total size of the underlying child-vector of an array DUCKDB_API static idx_t GetTotalSize(const Vector &vector); - //! Allocate dummy list entries for a vector - //! Note that there is nothing ensuring that the allocated data - //! remains valid (e.g. if this vector is resized) - //! This is only used during row serialization - DUCKDB_API static void AllocateDummyListEntries(Vector &vector); }; enum class UnionInvalidReason : uint8_t { diff --git a/src/duckdb/src/include/duckdb/execution/executor.hpp b/src/duckdb/src/include/duckdb/execution/executor.hpp index 3c248c68d..03cf4843d 100644 --- a/src/duckdb/src/include/duckdb/execution/executor.hpp +++ b/src/duckdb/src/include/duckdb/execution/executor.hpp @@ -89,7 +89,6 @@ class Executor { void AddEvent(shared_ptr event); void AddRecursiveCTE(PhysicalOperator &rec_cte); - void AddMaterializedCTE(PhysicalOperator &mat_cte); void ReschedulePipelines(const vector> &pipelines, vector> &events); //! Whether or not the root of the pipeline is a result collector object @@ -131,8 +130,6 @@ class Executor { vector> root_pipelines; //! The recursive CTE's in this query plan vector> recursive_ctes; - //! The materialized CTE's in this query plan - vector> materialized_ctes; //! The pipeline executor for the root pipeline unique_ptr root_executor; //! The current root pipeline index diff --git a/src/duckdb/src/include/duckdb/execution/index/art/art.hpp b/src/duckdb/src/include/duckdb/execution/index/art/art.hpp index 7848746b4..8a4daf659 100644 --- a/src/duckdb/src/include/duckdb/execution/index/art/art.hpp +++ b/src/duckdb/src/include/duckdb/execution/index/art/art.hpp @@ -64,13 +64,9 @@ class ART : public Index { public: //! Create a index instance of this type - static unique_ptr Create(const string &name, const IndexConstraintType constraint_type, - const vector &column_ids, - const vector> &unbound_expressions, - TableIOManager &table_io_manager, AttachedDatabase &db, - const IndexStorageInfo &storage_info) { - auto art = make_uniq(name, constraint_type, column_ids, table_io_manager, unbound_expressions, db, nullptr, - storage_info); + static unique_ptr Create(CreateIndexInput &input) { + auto art = make_uniq(input.name, input.constraint_type, input.column_ids, input.table_io_manager, + input.unbound_expressions, input.db, nullptr, input.storage_info); return std::move(art); } diff --git a/src/duckdb/src/include/duckdb/execution/index/index_type.hpp b/src/duckdb/src/include/duckdb/execution/index/index_type.hpp index 2cd6a54fa..a2eb16d23 100644 --- a/src/duckdb/src/include/duckdb/execution/index/index_type.hpp +++ b/src/duckdb/src/include/duckdb/execution/index/index_type.hpp @@ -12,6 +12,8 @@ #include "duckdb/common/vector.hpp" #include "duckdb/common/unique_ptr.hpp" #include "duckdb/common/string.hpp" +#include "duckdb/common/types/value.hpp" +#include "duckdb/common/case_insensitive_map.hpp" namespace duckdb { @@ -22,12 +24,26 @@ class TableIOManager; class AttachedDatabase; struct IndexStorageInfo; -typedef unique_ptr (*index_create_function_t)(const string &name, - const IndexConstraintType index_constraint_type, - const vector &column_ids, - const vector> &unbound_expressions, - TableIOManager &table_io_manager, AttachedDatabase &db, - const IndexStorageInfo &storage_info); +struct CreateIndexInput { + TableIOManager &table_io_manager; + AttachedDatabase &db; + IndexConstraintType constraint_type; + const string &name; + const vector &column_ids; + const vector> &unbound_expressions; + const IndexStorageInfo &storage_info; + const case_insensitive_map_t &options; + + CreateIndexInput(TableIOManager &table_io_manager, AttachedDatabase &db, IndexConstraintType constraint_type, + const string &name, const vector &column_ids, + const vector> &unbound_expressions, const IndexStorageInfo &storage_info, + const case_insensitive_map_t &options) + : table_io_manager(table_io_manager), db(db), constraint_type(constraint_type), name(name), + column_ids(column_ids), unbound_expressions(unbound_expressions), storage_info(storage_info), + options(options) {}; +}; + +typedef unique_ptr (*index_create_function_t)(CreateIndexInput &input); //! A index "type" class IndexType { public: diff --git a/src/duckdb/src/include/duckdb/execution/join_hashtable.hpp b/src/duckdb/src/include/duckdb/execution/join_hashtable.hpp index b96589add..b4910a554 100644 --- a/src/duckdb/src/include/duckdb/execution/join_hashtable.hpp +++ b/src/duckdb/src/include/duckdb/execution/join_hashtable.hpp @@ -242,7 +242,7 @@ class JoinHashTable { //===--------------------------------------------------------------------===// // External Join //===--------------------------------------------------------------------===// - static constexpr const idx_t INITIAL_RADIX_BITS = 3; + static constexpr const idx_t INITIAL_RADIX_BITS = 4; struct ProbeSpillLocalAppendState { //! Local partition and append state (if partitioned) diff --git a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/base_scanner.hpp b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/base_scanner.hpp index f4c8bde83..722bab13f 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/base_scanner.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/base_scanner.hpp @@ -95,7 +95,7 @@ class BaseScanner { //! Unique pointer to the buffer_handle, this is unique per scanner, since it also contains the necessary counters //! To offload buffers to disk if necessary - unique_ptr cur_buffer_handle; + shared_ptr cur_buffer_handle; //! Hold the current buffer ptr char *buffer_handle_ptr = nullptr; diff --git a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_buffer.hpp b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_buffer.hpp index 565ccdce2..72665ae2d 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_buffer.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_buffer.hpp @@ -51,7 +51,7 @@ class CSVBuffer { idx_t file_number_p, idx_t buffer_idx); //! Creates a new buffer with the next part of the CSV File - shared_ptr Next(CSVFileHandle &file_handle, idx_t buffer_size, idx_t file_number); + shared_ptr Next(CSVFileHandle &file_handle, idx_t buffer_size, idx_t file_number, bool &has_seaked); //! Gets the buffer actual size idx_t GetBufferSize(); @@ -65,7 +65,7 @@ class CSVBuffer { void Reload(CSVFileHandle &file_handle); //! Wrapper for the Pin Function, if it can seek, it means that the buffer might have been destroyed, hence we must //! Scan it from the disk file again. - unique_ptr Pin(CSVFileHandle &file_handle); + shared_ptr Pin(CSVFileHandle &file_handle, bool &has_seeked); //! Wrapper for the unpin void Unpin(); char *Ptr() { diff --git a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_buffer_manager.hpp b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_buffer_manager.hpp index 78c046027..b9b4bb92d 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_buffer_manager.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_buffer_manager.hpp @@ -25,7 +25,9 @@ class CSVBufferManager { const idx_t file_idx); //! Returns a buffer from a buffer id (starting from 0). If it's in the auto-detection then we cache new buffers //! Otherwise we remove them from the cache if they are already there, or just return them bypassing the cache. - unique_ptr GetBuffer(const idx_t buffer_idx); + shared_ptr GetBuffer(const idx_t buffer_idx); + + void ResetBuffer(const idx_t buffer_idx); //! unique_ptr to the file handle, gets stolen after sniffing unique_ptr file_handle; //! Initializes the buffer manager, during it's construction/reset @@ -64,6 +66,9 @@ class CSVBufferManager { idx_t bytes_read = 0; //! Because the buffer manager can be accessed in Parallel we need a mutex. mutex main_mutex; + //! If the file_handle used seek + bool has_seeked = false; + unordered_set reset_when_possible; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_file_scanner.hpp b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_file_scanner.hpp index 74af23fef..ce9fc08ce 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_file_scanner.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/csv_file_scanner.hpp @@ -60,7 +60,7 @@ class CSVFileScan { vector file_types; - // Variables to handle projection pushdown + //! Variables to handle projection pushdown set projected_columns; std::vector> projection_ids; diff --git a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/global_csv_state.hpp b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/global_csv_state.hpp index c227c76e1..4be2aee92 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/global_csv_state.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/global_csv_state.hpp @@ -74,6 +74,7 @@ struct CSVGlobalState : public GlobalTableFunctionState { atomic scanner_idx; atomic last_file_idx; + shared_ptr current_buffer_in_use; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/string_value_scanner.hpp b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/string_value_scanner.hpp index 0a8ba39c6..2f0e09405 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/string_value_scanner.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/csv_scanner/string_value_scanner.hpp @@ -15,6 +15,18 @@ namespace duckdb { +struct CSVBufferUsage { + CSVBufferUsage(CSVBufferManager &buffer_manager_p, idx_t buffer_idx_p) + : buffer_manager(buffer_manager_p), buffer_idx(buffer_idx_p) { + + }; + ~CSVBufferUsage() { + buffer_manager.ResetBuffer(buffer_idx); + } + CSVBufferManager &buffer_manager; + idx_t buffer_idx; +}; + //! Class that keeps track of line starts, used for line size verification class LinePosition { public: @@ -37,9 +49,9 @@ class LinePosition { class StringValueResult : public ScannerResult { public: - StringValueResult(CSVStates &states, CSVStateMachine &state_machine, CSVBufferHandle &buffer_handle, - Allocator &buffer_allocator, idx_t result_size, idx_t buffer_position, - CSVErrorHandler &error_hander, CSVIterator &iterator, bool store_line_size, + StringValueResult(CSVStates &states, CSVStateMachine &state_machine, + const shared_ptr &buffer_handle, Allocator &buffer_allocator, idx_t result_size, + idx_t buffer_position, CSVErrorHandler &error_hander, CSVIterator &iterator, bool store_line_size, shared_ptr csv_file_scan, idx_t &lines_read); //! Information on the vector @@ -83,6 +95,12 @@ class StringValueResult : public ScannerResult { unsafe_unique_array projected_columns; bool projecting_columns = false; idx_t chunk_col_id = 0; + + //! We must ensure that we keep the buffers alive until processing the query result + vector> buffer_handles; + + //! If the current row has an error, we have to skip it + bool ignore_current_row = false; //! Specialized code for quoted values, makes sure to remove quotes and escapes static inline void AddQuotedValue(StringValueResult &result, const idx_t buffer_pos); //! Adds a Value to the result @@ -105,6 +123,9 @@ class StringValueResult : public ScannerResult { Value GetValue(idx_t row_idx, idx_t col_idx); DataChunk &ToChunk(); + + //! Resets the state of the result + void Reset(); }; //! Our dialect scanner basically goes over the CSV and actually parses the values to a DuckDB vector of string_t @@ -141,6 +162,9 @@ class StringValueScanner : public BaseScanner { const idx_t scanner_idx; + //! Variable that manages buffer tracking + shared_ptr buffer_tracker; + private: void Initialize() override; @@ -167,7 +191,7 @@ class StringValueScanner : public BaseScanner { vector types; //! Pointer to the previous buffer handle, necessary for overbuffer values - unique_ptr previous_buffer_handle; + shared_ptr previous_buffer_handle; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/execution/operator/set/physical_cte.hpp b/src/duckdb/src/include/duckdb/execution/operator/set/physical_cte.hpp index 6babb8dd8..3ff5fb8cf 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/set/physical_cte.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/set/physical_cte.hpp @@ -24,39 +24,44 @@ class PhysicalCTE : public PhysicalOperator { unique_ptr bottom, idx_t estimated_cardinality); ~PhysicalCTE() override; + vector> cte_scans; + std::shared_ptr working_table; - shared_ptr recursive_meta_pipeline; idx_t table_index; string ctename; -public: - // Source interface - SourceResultType GetData(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input) const override; - - bool IsSource() const override { - return true; - } - public: // Sink interface SinkResultType Sink(ExecutionContext &context, DataChunk &chunk, OperatorSinkInput &input) const override; unique_ptr GetGlobalSinkState(ClientContext &context) const override; + unique_ptr GetLocalSinkState(ExecutionContext &context) const override; + + SinkCombineResultType Combine(ExecutionContext &context, OperatorSinkCombineInput &input) const override; bool IsSink() const override { return true; } + bool ParallelSink() const override { + return true; + } + + bool SinkOrderDependent() const override { + return false; + } + + bool RequiresBatchIndex() const override { + return false; + } + string ParamsToString() const override; public: void BuildPipelines(Pipeline ¤t, MetaPipeline &meta_pipeline) override; vector> GetSources() const override; - -private: - void ExecuteRecursivePipelines(ExecutionContext &context) const; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/execution/operator/set/physical_recursive_cte.hpp b/src/duckdb/src/include/duckdb/execution/operator/set/physical_recursive_cte.hpp index b40e20194..88071368e 100644 --- a/src/duckdb/src/include/duckdb/execution/operator/set/physical_recursive_cte.hpp +++ b/src/duckdb/src/include/duckdb/execution/operator/set/physical_recursive_cte.hpp @@ -50,6 +50,10 @@ class PhysicalRecursiveCTE : public PhysicalOperator { return true; } + bool ParallelSink() const override { + return true; + } + string ParamsToString() const override; public: diff --git a/src/duckdb/src/include/duckdb/execution/physical_plan_generator.hpp b/src/duckdb/src/include/duckdb/execution/physical_plan_generator.hpp index 6d377a62b..ab6198104 100644 --- a/src/duckdb/src/include/duckdb/execution/physical_plan_generator.hpp +++ b/src/duckdb/src/include/duckdb/execution/physical_plan_generator.hpp @@ -34,7 +34,7 @@ class PhysicalPlanGenerator { //! This data structure is used to establish it. unordered_map> recursive_cte_tables; //! Materialized CTE ids must be collected. - unordered_set materialized_ctes; + unordered_map>> materialized_ctes; public: //! Creates a plan from the logical operator. This involves resolving column bindings and generating physical diff --git a/src/duckdb/src/include/duckdb/function/cast/bound_cast_data.hpp b/src/duckdb/src/include/duckdb/function/cast/bound_cast_data.hpp index 8645c24dd..50b4c70ab 100644 --- a/src/duckdb/src/include/duckdb/function/cast/bound_cast_data.hpp +++ b/src/duckdb/src/include/duckdb/function/cast/bound_cast_data.hpp @@ -48,12 +48,21 @@ struct ListCast { }; struct StructBoundCastData : public BoundCastData { + StructBoundCastData(vector child_casts, LogicalType target_p, vector child_member_map_p) + : child_cast_info(std::move(child_casts)), target(std::move(target_p)), + child_member_map(std::move(child_member_map_p)) { + D_ASSERT(child_cast_info.size() == child_member_map.size()); + } StructBoundCastData(vector child_casts, LogicalType target_p) : child_cast_info(std::move(child_casts)), target(std::move(target_p)) { + for (idx_t i = 0; i < child_cast_info.size(); i++) { + child_member_map.push_back(i); + } } vector child_cast_info; LogicalType target; + vector child_member_map; static unique_ptr BindStructToStructCast(BindCastInput &input, const LogicalType &source, const LogicalType &target); @@ -65,7 +74,7 @@ struct StructBoundCastData : public BoundCastData { for (auto &info : child_cast_info) { copy_info.push_back(info.Copy()); } - return make_uniq(std::move(copy_info), target); + return make_uniq(std::move(copy_info), target, child_member_map); } }; diff --git a/src/duckdb/src/include/duckdb/function/scalar/nested_functions.hpp b/src/duckdb/src/include/duckdb/function/scalar/nested_functions.hpp index 86fa9a19b..5eec127b0 100644 --- a/src/duckdb/src/include/duckdb/function/scalar/nested_functions.hpp +++ b/src/duckdb/src/include/duckdb/function/scalar/nested_functions.hpp @@ -103,7 +103,6 @@ struct ListPositionFun { }; struct ListResizeFun { - static ScalarFunction GetFunction(); static void RegisterFunction(BuiltinFunctions &set); }; @@ -123,7 +122,9 @@ struct ListWhereFun { }; struct StructExtractFun { - static ScalarFunction GetFunction(); + static ScalarFunction KeyExtractFunction(); + static ScalarFunction IndexExtractFunction(); + static ScalarFunctionSet GetFunctions(); static void RegisterFunction(BuiltinFunctions &set); }; diff --git a/src/duckdb/src/include/duckdb/function/table/arrow.hpp b/src/duckdb/src/include/duckdb/function/table/arrow.hpp index 3750a6c7e..ce923906e 100644 --- a/src/duckdb/src/include/duckdb/function/table/arrow.hpp +++ b/src/duckdb/src/include/duckdb/function/table/arrow.hpp @@ -213,8 +213,6 @@ struct ArrowTableFunction { //! Gets the progress on the table scan, used for Progress Bars static double ArrowProgress(ClientContext &context, const FunctionData *bind_data, const GlobalTableFunctionState *global_state); - //! Renames repeated columns and case sensitive columns - static void RenameArrowColumns(vector &names); public: //! Helper function to get the DuckDB logical type diff --git a/src/duckdb/src/include/duckdb/function/table/system_functions.hpp b/src/duckdb/src/include/duckdb/function/table/system_functions.hpp index 1a2928308..1ab8f8769 100644 --- a/src/duckdb/src/include/duckdb/function/table/system_functions.hpp +++ b/src/duckdb/src/include/duckdb/function/table/system_functions.hpp @@ -29,14 +29,6 @@ struct PragmaMetadataInfo { static void RegisterFunction(BuiltinFunctions &set); }; -struct PragmaLastProfilingOutput { - static void RegisterFunction(BuiltinFunctions &set); -}; - -struct PragmaDetailedProfilingOutput { - static void RegisterFunction(BuiltinFunctions &set); -}; - struct PragmaVersion { static void RegisterFunction(BuiltinFunctions &set); }; @@ -89,6 +81,10 @@ struct DuckDBIndexesFun { static void RegisterFunction(BuiltinFunctions &set); }; +struct DuckDBMemoryFun { + static void RegisterFunction(BuiltinFunctions &set); +}; + struct DuckDBOptimizersFun { static void RegisterFunction(BuiltinFunctions &set); }; diff --git a/src/duckdb/src/include/duckdb/main/attached_database.hpp b/src/duckdb/src/include/duckdb/main/attached_database.hpp index 7ecbc14f8..a7d824180 100644 --- a/src/duckdb/src/include/duckdb/main/attached_database.hpp +++ b/src/duckdb/src/include/duckdb/main/attached_database.hpp @@ -39,8 +39,8 @@ class AttachedDatabase : public CatalogEntry { //! Create an attached database instance with the specified name and storage AttachedDatabase(DatabaseInstance &db, Catalog &catalog, string name, string file_path, AccessMode access_mode); //! Create an attached database instance with the specified storage extension - AttachedDatabase(DatabaseInstance &db, Catalog &catalog, StorageExtension &ext, string name, const AttachInfo &info, - AccessMode access_mode); + AttachedDatabase(DatabaseInstance &db, Catalog &catalog, StorageExtension &ext, ClientContext &context, string name, + const AttachInfo &info, AccessMode access_mode); ~AttachedDatabase() override; void Initialize(); diff --git a/src/duckdb/src/include/duckdb/main/client_context.hpp b/src/duckdb/src/include/duckdb/main/client_context.hpp index b62dc04fd..c26a0e61b 100644 --- a/src/duckdb/src/include/duckdb/main/client_context.hpp +++ b/src/duckdb/src/include/duckdb/main/client_context.hpp @@ -25,6 +25,7 @@ #include "duckdb/main/external_dependencies.hpp" #include "duckdb/common/error_data.hpp" #include "duckdb/main/client_properties.hpp" +#include "duckdb/main/client_context_state.hpp" namespace duckdb { class Appender; @@ -45,6 +46,7 @@ struct ActiveQueryContext; struct ParserOptions; class SimpleBufferedData; struct ClientData; +class ClientContextState; struct PendingQueryParameters { //! Prepared statement parameters (if any) @@ -53,14 +55,6 @@ struct PendingQueryParameters { bool allow_stream_result = false; }; -//! ClientContextState is virtual base class for ClientContext-local (or Query-Local, using QueryEnd callback) state -//! e.g. caches that need to live as long as a ClientContext or Query. -class ClientContextState { -public: - virtual ~ClientContextState() {}; - virtual void QueryEnd() = 0; -}; - //! The ClientContext holds information relevant to the current client session //! during execution class ClientContext : public std::enable_shared_from_this { @@ -241,7 +235,6 @@ class ClientContext : public std::enable_shared_from_this { unique_ptr LockContext(); - void BeginTransactionInternal(ClientContextLock &lock, bool requires_valid_transaction); void BeginQueryInternal(ClientContextLock &lock, const string &query); ErrorData EndQueryInternal(ClientContextLock &lock, bool success, bool invalidate_transaction); diff --git a/src/duckdb/src/include/duckdb/main/client_context_state.hpp b/src/duckdb/src/include/duckdb/main/client_context_state.hpp new file mode 100644 index 000000000..c07c9011a --- /dev/null +++ b/src/duckdb/src/include/duckdb/main/client_context_state.hpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/main/client_context_state.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/common/common.hpp" + +namespace duckdb { +class ClientContext; +class MetaTransaction; + +//! ClientContextState is virtual base class for ClientContext-local (or Query-Local, using QueryEnd callback) state +//! e.g. caches that need to live as long as a ClientContext or Query. +class ClientContextState { +public: + virtual ~ClientContextState() = default; + virtual void QueryBegin(ClientContext &context) { + } + virtual void QueryEnd() { + } + virtual void QueryEnd(ClientContext &context) { + QueryEnd(); + } + virtual void TransactionBegin(MetaTransaction &transaction, ClientContext &context) { + } + virtual void TransactionCommit(MetaTransaction &transaction, ClientContext &context) { + } + virtual void TransactionRollback(MetaTransaction &transaction, ClientContext &context) { + } +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/client_data.hpp b/src/duckdb/src/include/duckdb/main/client_data.hpp index bc94d757a..7c281806b 100644 --- a/src/duckdb/src/include/duckdb/main/client_data.hpp +++ b/src/duckdb/src/include/duckdb/main/client_data.hpp @@ -24,7 +24,6 @@ class FileOpener; class FileSystem; class HTTPState; class QueryProfiler; -class QueryProfilerHistory; class PreparedStatementData; class SchemaCatalogEntry; struct RandomEngine; @@ -35,8 +34,6 @@ struct ClientData { //! Query profiler shared_ptr profiler; - //! QueryProfiler History - unique_ptr query_profiler_history; //! The set of temporary objects that belong to this client shared_ptr temporary_objects; diff --git a/src/duckdb/src/include/duckdb/main/config.hpp b/src/duckdb/src/include/duckdb/main/config.hpp index 8f0bc7470..468ec5bff 100644 --- a/src/duckdb/src/include/duckdb/main/config.hpp +++ b/src/duckdb/src/include/duckdb/main/config.hpp @@ -120,8 +120,9 @@ struct DBConfigOptions { idx_t maximum_memory = (idx_t)-1; //! The maximum amount of CPU threads used by the database system. Default: all available. idx_t maximum_threads = (idx_t)-1; - //! The number of external threads that work on DuckDB tasks. Default: none. - idx_t external_threads = 0; + //! The number of external threads that work on DuckDB tasks. Default: 1. + //! Must be smaller or equal to maximum_threads. + idx_t external_threads = 1; //! Whether or not to create and use a temporary directory to store intermediates that do not fit in memory bool use_temporary_directory = true; //! Directory to store temporary structures that do not fit in memory @@ -274,7 +275,6 @@ struct DBConfig { DUCKDB_API CastFunctionSet &GetCastFunctions(); DUCKDB_API IndexTypeSet &GetIndexTypes(); static idx_t GetSystemMaxThreads(FileSystem &fs); - void SetDefaultMaxThreads(); void SetDefaultMaxMemory(); OrderType ResolveOrder(OrderType order_type) const; diff --git a/src/duckdb/src/include/duckdb/main/database.hpp b/src/duckdb/src/include/duckdb/main/database.hpp index 35aaf6d85..a5798c805 100644 --- a/src/duckdb/src/include/duckdb/main/database.hpp +++ b/src/duckdb/src/include/duckdb/main/database.hpp @@ -55,8 +55,8 @@ class DatabaseInstance : public std::enable_shared_from_this { DUCKDB_API bool TryGetCurrentSetting(const std::string &key, Value &result); - unique_ptr CreateAttachedDatabase(const AttachInfo &info, const string &type, - AccessMode access_mode); + unique_ptr CreateAttachedDatabase(ClientContext &context, const AttachInfo &info, + const string &type, AccessMode access_mode); private: void Initialize(const char *path, DBConfig *config); diff --git a/src/duckdb/src/include/duckdb/main/query_profiler.hpp b/src/duckdb/src/include/duckdb/main/query_profiler.hpp index 2ea69dc66..aa5bc42e8 100644 --- a/src/duckdb/src/include/duckdb/main/query_profiler.hpp +++ b/src/duckdb/src/include/duckdb/main/query_profiler.hpp @@ -230,36 +230,4 @@ class QueryProfiler { bool OperatorRequiresProfiling(PhysicalOperatorType op_type); }; -//! The QueryProfilerHistory can be used to access the profiler of previous queries -class QueryProfilerHistory { -private: - static constexpr uint64_t DEFAULT_SIZE = 20; - - //! Previous Query profilers - deque>> prev_profilers; - //! Previous Query profilers size - uint64_t prev_profilers_size = DEFAULT_SIZE; - -public: - deque>> &GetPrevProfilers() { - return prev_profilers; - } - QueryProfilerHistory() { - } - - void SetPrevProfilersSize(uint64_t prevProfilersSize) { - prev_profilers_size = prevProfilersSize; - } - uint64_t GetPrevProfilersSize() const { - return prev_profilers_size; - } - -public: - void SetProfilerHistorySize(uint64_t size) { - this->prev_profilers_size = size; - } - void ResetProfilerHistorySize() { - this->prev_profilers_size = DEFAULT_SIZE; - } -}; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/main/query_result.hpp b/src/duckdb/src/include/duckdb/main/query_result.hpp index 95516053b..c42c3ac2b 100644 --- a/src/duckdb/src/include/duckdb/main/query_result.hpp +++ b/src/duckdb/src/include/duckdb/main/query_result.hpp @@ -89,6 +89,10 @@ class QueryResult : public BaseQueryResult { return reinterpret_cast(*this); } +public: + //! Deduplicate column names for interop with external libraries + static void DeduplicateColumns(vector &names); + public: //! Returns the name of the column for the given index DUCKDB_API const string &ColumnName(idx_t index) const; diff --git a/src/duckdb/src/include/duckdb/main/secret/secret.hpp b/src/duckdb/src/include/duckdb/main/secret/secret.hpp index de5753d2d..27960e0e1 100644 --- a/src/duckdb/src/include/duckdb/main/secret/secret.hpp +++ b/src/duckdb/src/include/duckdb/main/secret/secret.hpp @@ -100,6 +100,11 @@ class BaseSecret { //! Serialize this secret virtual void Serialize(Serializer &serializer) const; + virtual unique_ptr Clone() const { + D_ASSERT(typeid(BaseSecret) == typeid(*this)); + return make_uniq(*this); + } + //! Getters const vector &GetScope() const { return prefix_paths; @@ -188,6 +193,10 @@ class KeyValueSecret : public BaseSecret { return duckdb::unique_ptr_cast(std::move(result)); } + unique_ptr Clone() const override { + return make_uniq(*this); + } + //! the map of key -> values that make up the secret case_insensitive_tree_t secret_map; //! keys that are sensitive and should be redacted diff --git a/src/duckdb/src/include/duckdb/main/secret/secret_manager.hpp b/src/duckdb/src/include/duckdb/main/secret/secret_manager.hpp index aa7676bc1..2a5f72d3e 100644 --- a/src/duckdb/src/include/duckdb/main/secret/secret_manager.hpp +++ b/src/duckdb/src/include/duckdb/main/secret/secret_manager.hpp @@ -25,24 +25,42 @@ struct SecretMatch { public: SecretMatch() : secret_entry(nullptr), score(NumericLimits::Minimum()) { } - SecretMatch(SecretEntry &secret_entry, int64_t score) : secret_entry(&secret_entry), score(score) { + + SecretMatch(const SecretMatch &other) + : secret_entry((other.secret_entry != nullptr) ? make_uniq(*other.secret_entry) : nullptr), + score(other.score) { + } + + SecretMatch(SecretEntry &secret_entry, int64_t score) + : secret_entry(make_uniq(secret_entry)), score(score) { } + SecretMatch &operator=(const SecretMatch &other) { + this->secret_entry = (other.secret_entry != nullptr) ? make_uniq(*other.secret_entry) : nullptr; + this->score = other.score; + return *this; + }; + //! Get the secret - const BaseSecret &GetSecret(); + const BaseSecret &GetSecret() const; bool HasMatch() { - return secret_entry; + return secret_entry != nullptr; } - optional_ptr secret_entry; + unique_ptr secret_entry; int64_t score; }; //! A Secret Entry in the secret manager struct SecretEntry { public: - SecretEntry(unique_ptr secret) : secret(std::move(secret)) {}; + SecretEntry(unique_ptr secret) : secret(secret != nullptr ? secret->Clone() : nullptr) {}; + + SecretEntry(const SecretEntry &other) + : persist_type(other.persist_type), storage_mode(other.storage_mode), + secret((other.secret != nullptr) ? other.secret->Clone() : nullptr) { + } //! Whether the secret is persistent SecretPersistType persist_type; @@ -96,26 +114,25 @@ class SecretManager { //! Register a Secret Function i.e. a secret provider for a secret type DUCKDB_API void RegisterSecretFunction(CreateSecretFunction function, OnCreateConflict on_conflict); //! Register a secret by providing a secret manually - DUCKDB_API optional_ptr RegisterSecret(CatalogTransaction transaction, - unique_ptr secret, - OnCreateConflict on_conflict, SecretPersistType persist_type, - const string &storage = ""); + DUCKDB_API unique_ptr RegisterSecret(CatalogTransaction transaction, + unique_ptr secret, OnCreateConflict on_conflict, + SecretPersistType persist_type, const string &storage = ""); //! Create a secret from a CreateSecretInfo - DUCKDB_API optional_ptr CreateSecret(ClientContext &context, const CreateSecretInfo &info); + DUCKDB_API unique_ptr CreateSecret(ClientContext &context, const CreateSecretInfo &info); //! The Bind for create secret is done by the secret manager DUCKDB_API BoundStatement BindCreateSecret(CatalogTransaction transaction, CreateSecretInfo &info); //! Lookup the best matching secret by matching the secret scopes to the path DUCKDB_API SecretMatch LookupSecret(CatalogTransaction transaction, const string &path, const string &type); //! Get a secret by name, optionally from a specific storage - DUCKDB_API optional_ptr GetSecretByName(CatalogTransaction transaction, const string &name, - const string &storage = ""); + DUCKDB_API unique_ptr GetSecretByName(CatalogTransaction transaction, const string &name, + const string &storage = ""); //! Delete a secret by name, optionally by providing the storage to drop from DUCKDB_API void DropSecretByName(CatalogTransaction transaction, const string &name, OnEntryNotFound on_entry_not_found, SecretPersistType persist_type = SecretPersistType::DEFAULT, const string &storage = ""); //! List all secrets from all secret storages - DUCKDB_API vector> AllSecrets(CatalogTransaction transaction); + DUCKDB_API vector AllSecrets(CatalogTransaction transaction); //! Secret Manager settings DUCKDB_API virtual void SetEnablePersistentSecrets(bool enabled); @@ -141,9 +158,9 @@ class SecretManager { //! Lookup a CreateSecretFunction optional_ptr LookupFunctionInternal(const string &type, const string &provider); //! Register a new Secret - optional_ptr RegisterSecretInternal(CatalogTransaction transaction, - unique_ptr secret, OnCreateConflict on_conflict, - SecretPersistType persist_type, const string &storage = ""); + unique_ptr RegisterSecretInternal(CatalogTransaction transaction, unique_ptr secret, + OnCreateConflict on_conflict, SecretPersistType persist_type, + const string &storage = ""); //! Initialize the secret catalog_set and persistent secrets (lazily) void InitializeSecrets(CatalogTransaction transaction); //! Load a secret storage diff --git a/src/duckdb/src/include/duckdb/main/secret/secret_storage.hpp b/src/duckdb/src/include/duckdb/main/secret/secret_storage.hpp index 25dfdbf8d..7b40c0a4d 100644 --- a/src/duckdb/src/include/duckdb/main/secret/secret_storage.hpp +++ b/src/duckdb/src/include/duckdb/main/secret/secret_storage.hpp @@ -38,10 +38,10 @@ class SecretStorage { }; //! Store a secret - virtual optional_ptr StoreSecret(unique_ptr secret, OnCreateConflict on_conflict, - optional_ptr transaction = nullptr) = 0; + virtual unique_ptr StoreSecret(unique_ptr secret, OnCreateConflict on_conflict, + optional_ptr transaction = nullptr) = 0; //! Get all secrets - virtual vector> AllSecrets(optional_ptr transaction = nullptr) = 0; + virtual vector AllSecrets(optional_ptr transaction = nullptr) = 0; //! Drop secret by name virtual void DropSecretByName(const string &name, OnEntryNotFound on_entry_not_found, optional_ptr transaction = nullptr) = 0; @@ -49,8 +49,8 @@ class SecretStorage { virtual SecretMatch LookupSecret(const string &path, const string &type, optional_ptr transaction = nullptr) = 0; //! Get a secret by name - virtual optional_ptr GetSecretByName(const string &name, - optional_ptr transaction = nullptr) = 0; + virtual unique_ptr GetSecretByName(const string &name, + optional_ptr transaction = nullptr) = 0; //! Return the offset associated to this storage for tie-breaking secrets between storages virtual int64_t GetTieBreakOffset() = 0; @@ -103,15 +103,15 @@ class CatalogSetSecretStorage : public SecretStorage { return storage_name; }; - virtual optional_ptr StoreSecret(unique_ptr secret, OnCreateConflict on_conflict, - optional_ptr transaction = nullptr) override; - vector> AllSecrets(optional_ptr transaction = nullptr) override; + virtual unique_ptr StoreSecret(unique_ptr secret, OnCreateConflict on_conflict, + optional_ptr transaction = nullptr) override; + vector AllSecrets(optional_ptr transaction = nullptr) override; void DropSecretByName(const string &name, OnEntryNotFound on_entry_not_found, optional_ptr transaction = nullptr) override; SecretMatch LookupSecret(const string &path, const string &type, optional_ptr transaction = nullptr) override; - optional_ptr GetSecretByName(const string &name, - optional_ptr transaction = nullptr) override; + unique_ptr GetSecretByName(const string &name, + optional_ptr transaction = nullptr) override; protected: //! Callback called on Store to allow child classes to implement persistence. diff --git a/src/duckdb/src/include/duckdb/main/settings.hpp b/src/duckdb/src/include/duckdb/main/settings.hpp index 72433204b..31d8b1db8 100644 --- a/src/duckdb/src/include/duckdb/main/settings.hpp +++ b/src/duckdb/src/include/duckdb/main/settings.hpp @@ -496,15 +496,6 @@ struct ExportLargeBufferArrow { static Value GetSetting(ClientContext &context); }; -struct ProfilerHistorySize { - static constexpr const char *Name = "profiler_history_size"; - static constexpr const char *Description = "Sets the profiler history size"; - static constexpr const LogicalTypeId InputType = LogicalTypeId::BIGINT; - static void SetLocal(ClientContext &context, const Value ¶meter); - static void ResetLocal(ClientContext &context); - static Value GetSetting(ClientContext &context); -}; - struct ProfileOutputSetting { static constexpr const char *Name = "profile_output"; static constexpr const char *Description = diff --git a/src/duckdb/src/include/duckdb/parallel/task_scheduler.hpp b/src/duckdb/src/include/duckdb/parallel/task_scheduler.hpp index 1c04dbb88..2d955e0b5 100644 --- a/src/duckdb/src/include/duckdb/parallel/task_scheduler.hpp +++ b/src/duckdb/src/include/duckdb/parallel/task_scheduler.hpp @@ -58,9 +58,10 @@ class TaskScheduler { //! Run tasks until `max_tasks` have been completed, or until there are no more tasks available void ExecuteTasks(idx_t max_tasks); - //! Sets the amount of active threads executing tasks for the system; n-1 background threads will be launched. - //! The main thread will also be used for execution - void SetThreads(int32_t n); + //! Sets the amount of background threads to be used for execution, based on the number of total threads + //! and the number of external threads. External threads, e.g. the main thread, will also be used for execution. + //! Launches `total_threads - external_threads` background worker threads. + void SetThreads(idx_t total_threads, idx_t external_threads); void RelaunchThreads(); diff --git a/src/duckdb/src/include/duckdb/parser/parsed_data/create_view_info.hpp b/src/duckdb/src/include/duckdb/parser/parsed_data/create_view_info.hpp index fd13c2b47..a6a4c39b4 100644 --- a/src/duckdb/src/include/duckdb/parser/parsed_data/create_view_info.hpp +++ b/src/duckdb/src/include/duckdb/parser/parsed_data/create_view_info.hpp @@ -27,6 +27,8 @@ struct CreateViewInfo : public CreateInfo { vector aliases; //! Return types vector types; + //! Names of the query + vector names; //! The SelectStatement of the view unique_ptr query; diff --git a/src/duckdb/src/include/duckdb/planner/binder.hpp b/src/duckdb/src/include/duckdb/planner/binder.hpp index 1d40a63a9..8421c9017 100644 --- a/src/duckdb/src/include/duckdb/planner/binder.hpp +++ b/src/duckdb/src/include/duckdb/planner/binder.hpp @@ -106,6 +106,8 @@ class Binder : public std::enable_shared_from_this { //! The intermediate lambda bindings to bind nested lambdas (if any) optional_ptr> lambda_bindings; + unordered_map recursive_ctes; + public: DUCKDB_API BoundStatement Bind(SQLStatement &statement); DUCKDB_API BoundStatement Bind(QueryNode &node); diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_cteref.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_cteref.hpp index 5fb2debff..b0e00ec4d 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_cteref.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_cteref.hpp @@ -22,7 +22,7 @@ class LogicalCTERef : public LogicalOperator { LogicalCTERef(idx_t table_index, idx_t cte_index, vector types, vector colnames, CTEMaterialize materialized_cte) : LogicalOperator(LogicalOperatorType::LOGICAL_CTE_REF), table_index(table_index), cte_index(cte_index), - materialized_cte(materialized_cte) { + correlated_columns(0), materialized_cte(materialized_cte) { D_ASSERT(types.size() > 0); chunk_types = types; bound_columns = colnames; @@ -35,6 +35,8 @@ class LogicalCTERef : public LogicalOperator { idx_t cte_index; //! The types of the chunk vector chunk_types; + //! Number of correlated columns + idx_t correlated_columns; //! Does this operator read a materialized CTE? CTEMaterialize materialized_cte; diff --git a/src/duckdb/src/include/duckdb/planner/operator/logical_recursive_cte.hpp b/src/duckdb/src/include/duckdb/planner/operator/logical_recursive_cte.hpp index 0fa74bd6c..20a13d729 100644 --- a/src/duckdb/src/include/duckdb/planner/operator/logical_recursive_cte.hpp +++ b/src/duckdb/src/include/duckdb/planner/operator/logical_recursive_cte.hpp @@ -9,6 +9,7 @@ #pragma once #include "duckdb/planner/logical_operator.hpp" +#include "duckdb/planner/binder.hpp" namespace duckdb { @@ -32,6 +33,7 @@ class LogicalRecursiveCTE : public LogicalOperator { string ctename; idx_t table_index; idx_t column_count; + vector correlated_columns; public: vector GetColumnBindings() override { diff --git a/src/duckdb/src/include/duckdb/planner/subquery/flatten_dependent_join.hpp b/src/duckdb/src/include/duckdb/planner/subquery/flatten_dependent_join.hpp index ce1cdc376..efc41cf64 100644 --- a/src/duckdb/src/include/duckdb/planner/subquery/flatten_dependent_join.hpp +++ b/src/duckdb/src/include/duckdb/planner/subquery/flatten_dependent_join.hpp @@ -25,6 +25,9 @@ struct FlattenDependentJoins { //! has_correlated_expressions map. bool DetectCorrelatedExpressions(LogicalOperator *op, bool lateral = false, idx_t lateral_depth = 0); + //! Mark entire subtree of Logical Operators as correlated by adding them to the has_correlated_expressions map. + bool MarkSubtreeCorrelated(LogicalOperator &op); + //! Push the dependent join down a LogicalOperator unique_ptr PushDownDependentJoin(unique_ptr plan); diff --git a/src/duckdb/src/include/duckdb/planner/subquery/rewrite_cte_scan.hpp b/src/duckdb/src/include/duckdb/planner/subquery/rewrite_cte_scan.hpp new file mode 100644 index 000000000..e2c507e73 --- /dev/null +++ b/src/duckdb/src/include/duckdb/planner/subquery/rewrite_cte_scan.hpp @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// DuckDB +// +// duckdb/planner/subquery/rewrite_cte_scan.hpp +// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "duckdb/planner/binder.hpp" +#include "duckdb/planner/column_binding_map.hpp" +#include "duckdb/planner/logical_operator.hpp" + +namespace duckdb { + +//! Helper class to rewrite correlated cte scans within a single LogicalOperator +class RewriteCTEScan : public LogicalOperatorVisitor { +public: + RewriteCTEScan(idx_t table_index, const vector &correlated_columns); + + void VisitOperator(LogicalOperator &op) override; + +private: + idx_t table_index; + const vector &correlated_columns; +}; + +} // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/storage/buffer/block_handle.hpp b/src/duckdb/src/include/duckdb/storage/buffer/block_handle.hpp index e797003bb..196fb27c7 100644 --- a/src/duckdb/src/include/duckdb/storage/buffer/block_handle.hpp +++ b/src/duckdb/src/include/duckdb/storage/buffer/block_handle.hpp @@ -13,6 +13,7 @@ #include "duckdb/common/mutex.hpp" #include "duckdb/storage/storage_info.hpp" #include "duckdb/common/file_buffer.hpp" +#include "duckdb/common/enums/memory_tag.hpp" namespace duckdb { class BlockManager; @@ -23,10 +24,11 @@ class DatabaseInstance; enum class BlockState : uint8_t { BLOCK_UNLOADED = 0, BLOCK_LOADED = 1 }; struct BufferPoolReservation { + MemoryTag tag; idx_t size {0}; BufferPool &pool; - BufferPoolReservation(BufferPool &pool); + BufferPoolReservation(MemoryTag tag, BufferPool &pool); BufferPoolReservation(const BufferPoolReservation &) = delete; BufferPoolReservation &operator=(const BufferPoolReservation &) = delete; @@ -40,7 +42,7 @@ struct BufferPoolReservation { }; struct TempBufferPoolReservation : BufferPoolReservation { - TempBufferPoolReservation(BufferPool &pool, idx_t size) : BufferPoolReservation(pool) { + TempBufferPoolReservation(MemoryTag tag, BufferPool &pool, idx_t size) : BufferPoolReservation(tag, pool) { Resize(size); } TempBufferPoolReservation(TempBufferPoolReservation &&) = default; @@ -58,9 +60,9 @@ class BlockHandle { friend class BufferPool; public: - BlockHandle(BlockManager &block_manager, block_id_t block_id); - BlockHandle(BlockManager &block_manager, block_id_t block_id, unique_ptr buffer, bool can_destroy, - idx_t block_size, BufferPoolReservation &&reservation); + BlockHandle(BlockManager &block_manager, block_id_t block_id, MemoryTag tag); + BlockHandle(BlockManager &block_manager, block_id_t block_id, MemoryTag tag, unique_ptr buffer, + bool can_destroy, idx_t block_size, BufferPoolReservation &&reservation); ~BlockHandle(); BlockManager &block_manager; @@ -115,6 +117,8 @@ class BlockHandle { atomic readers; //! The block id of the block const block_id_t block_id; + //! Memory tag + MemoryTag tag; //! Pointer to loaded data (if any) unique_ptr buffer; //! Internal eviction timestamp diff --git a/src/duckdb/src/include/duckdb/storage/buffer/buffer_pool.hpp b/src/duckdb/src/include/duckdb/storage/buffer/buffer_pool.hpp index b2dfa6731..a75b1a2da 100644 --- a/src/duckdb/src/include/duckdb/storage/buffer/buffer_pool.hpp +++ b/src/duckdb/src/include/duckdb/storage/buffer/buffer_pool.hpp @@ -49,7 +49,7 @@ class BufferPool { //! blocks can be evicted void SetLimit(idx_t limit, const char *exception_postscript); - void IncreaseUsedMemory(idx_t size); + void IncreaseUsedMemory(MemoryTag tag, idx_t size); idx_t GetUsedMemory() const; @@ -70,7 +70,7 @@ class BufferPool { bool success; TempBufferPoolReservation reservation; }; - virtual EvictionResult EvictBlocks(idx_t extra_memory, idx_t memory_limit, + virtual EvictionResult EvictBlocks(MemoryTag tag, idx_t extra_memory, idx_t memory_limit, unique_ptr *buffer = nullptr); //! Garbage collect eviction queue @@ -90,6 +90,8 @@ class BufferPool { atomic queue_insertions; //! Memory manager for concurrently used temporary memory, e.g., for physical operators unique_ptr temporary_memory_manager; + //! Memory usage per tag + atomic memory_usage_per_tag[MEMORY_TAG_COUNT]; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/storage/buffer/temporary_file_information.hpp b/src/duckdb/src/include/duckdb/storage/buffer/temporary_file_information.hpp index 642078aa1..4da97844a 100644 --- a/src/duckdb/src/include/duckdb/storage/buffer/temporary_file_information.hpp +++ b/src/duckdb/src/include/duckdb/storage/buffer/temporary_file_information.hpp @@ -1,9 +1,16 @@ #pragma once #include "duckdb/common/common.hpp" +#include "duckdb/common/enums/memory_tag.hpp" namespace duckdb { +struct MemoryInformation { + MemoryTag tag; + idx_t size; + idx_t evicted_data; +}; + struct TemporaryFileInformation { string path; idx_t size; diff --git a/src/duckdb/src/include/duckdb/storage/buffer_manager.hpp b/src/duckdb/src/include/duckdb/storage/buffer_manager.hpp index 6c021c2ec..7238be784 100644 --- a/src/duckdb/src/include/duckdb/storage/buffer_manager.hpp +++ b/src/duckdb/src/include/duckdb/storage/buffer_manager.hpp @@ -12,6 +12,7 @@ #include "duckdb/storage/buffer/buffer_handle.hpp" #include "duckdb/storage/block_manager.hpp" #include "duckdb/common/file_system.hpp" +#include "duckdb/common/enums/memory_tag.hpp" #include "duckdb/storage/buffer/temporary_file_information.hpp" #include "duckdb/main/config.hpp" @@ -34,7 +35,7 @@ class BufferManager { public: static unique_ptr CreateStandardBufferManager(DatabaseInstance &db, DBConfig &config); - virtual BufferHandle Allocate(idx_t block_size, bool can_destroy = true, + virtual BufferHandle Allocate(MemoryTag tag, idx_t block_size, bool can_destroy = true, shared_ptr *block = nullptr) = 0; //! Reallocate an in-memory buffer that is pinned. virtual void ReAllocate(shared_ptr &handle, idx_t block_size) = 0; @@ -48,6 +49,7 @@ class BufferManager { virtual DUCKDB_API Allocator &GetBufferAllocator(); virtual DUCKDB_API void ReserveMemory(idx_t size); virtual DUCKDB_API void FreeReservedMemory(idx_t size); + virtual vector GetMemoryUsageInfo() const = 0; //! Set a new memory limit to the buffer manager, throws an exception if the new limit is too low and not enough //! blocks can be evicted virtual void SetLimit(idx_t limit = (idx_t)-1); @@ -78,8 +80,8 @@ class BufferManager { protected: virtual void PurgeQueue() = 0; virtual void AddToEvictionQueue(shared_ptr &handle); - virtual void WriteTemporaryBuffer(block_id_t block_id, FileBuffer &buffer); - virtual unique_ptr ReadTemporaryBuffer(block_id_t id, unique_ptr buffer); + virtual void WriteTemporaryBuffer(MemoryTag tag, block_id_t block_id, FileBuffer &buffer); + virtual unique_ptr ReadTemporaryBuffer(MemoryTag tag, block_id_t id, unique_ptr buffer); virtual void DeleteTemporaryFile(block_id_t id); }; diff --git a/src/duckdb/src/include/duckdb/storage/standard_buffer_manager.hpp b/src/duckdb/src/include/duckdb/storage/standard_buffer_manager.hpp index 1b63d38fe..f52f2058a 100644 --- a/src/duckdb/src/include/duckdb/storage/standard_buffer_manager.hpp +++ b/src/duckdb/src/include/duckdb/storage/standard_buffer_manager.hpp @@ -50,7 +50,7 @@ class StandardBufferManager : public BufferManager { //! Allocate an in-memory buffer with a single pin. //! The allocated memory is released when the buffer handle is destroyed. - DUCKDB_API BufferHandle Allocate(idx_t block_size, bool can_destroy = true, + DUCKDB_API BufferHandle Allocate(MemoryTag tag, idx_t block_size, bool can_destroy = true, shared_ptr *block = nullptr) final override; //! Reallocate an in-memory buffer that is pinned. @@ -63,6 +63,9 @@ class StandardBufferManager : public BufferManager { //! blocks can be evicted void SetLimit(idx_t limit = (idx_t)-1) final override; + //! Returns informaton about memory usage + vector GetMemoryUsageInfo() const override; + //! Returns a list of all temporary files vector GetTemporaryFiles() final override; @@ -89,23 +92,15 @@ class StandardBufferManager : public BufferManager { protected: //! Helper template - TempBufferPoolReservation EvictBlocksOrThrow(idx_t memory_delta, unique_ptr *buffer, ARGS...); + TempBufferPoolReservation EvictBlocksOrThrow(MemoryTag tag, idx_t memory_delta, unique_ptr *buffer, + ARGS...); //! Register an in-memory buffer of arbitrary size, as long as it is >= BLOCK_SIZE. can_destroy signifies whether or //! not the buffer can be destroyed when unpinned, or whether or not it needs to be written to a temporary file so //! it can be reloaded. The resulting buffer will already be allocated, but needs to be pinned in order to be used. //! This needs to be private to prevent creating blocks without ever pinning them: //! blocks that are never pinned are never added to the eviction queue - shared_ptr RegisterMemory(idx_t block_size, bool can_destroy); - - //! Evict blocks until the currently used memory + extra_memory fit, returns false if this was not possible - //! (i.e. not enough blocks could be evicted) - //! If the "buffer" argument is specified AND the system can find a buffer to re-use for the given allocation size - //! "buffer" will be made to point to the re-usable memory. Note that this is not guaranteed. - //! Returns a pair. result.first indicates if eviction was successful. result.second contains the - //! reservation handle, which can be moved to the BlockHandle that will own the reservation. - BufferPool::EvictionResult EvictBlocks(idx_t extra_memory, idx_t memory_limit, - unique_ptr *buffer = nullptr); + shared_ptr RegisterMemory(MemoryTag tag, idx_t block_size, bool can_destroy); //! Garbage collect eviction queue void PurgeQueue() final override; @@ -114,9 +109,10 @@ class StandardBufferManager : public BufferManager { TemporaryMemoryManager &GetTemporaryMemoryManager() final override; //! Write a temporary buffer to disk - void WriteTemporaryBuffer(block_id_t block_id, FileBuffer &buffer) final override; + void WriteTemporaryBuffer(MemoryTag tag, block_id_t block_id, FileBuffer &buffer) final override; //! Read a temporary buffer from disk - unique_ptr ReadTemporaryBuffer(block_id_t id, unique_ptr buffer = nullptr) final override; + unique_ptr ReadTemporaryBuffer(MemoryTag tag, block_id_t id, + unique_ptr buffer = nullptr) final override; //! Get the path of the temporary buffer string GetTemporaryPath(block_id_t id); @@ -154,6 +150,8 @@ class StandardBufferManager : public BufferManager { Allocator buffer_allocator; //! Block manager for temp data unique_ptr temp_block_manager; + //! Temporary evicted memory data per tag + atomic evicted_data_per_tag[MEMORY_TAG_COUNT]; }; } // namespace duckdb diff --git a/src/duckdb/src/include/duckdb/storage/storage_extension.hpp b/src/duckdb/src/include/duckdb/storage/storage_extension.hpp index 0eeeceeca..369dd71a9 100644 --- a/src/duckdb/src/include/duckdb/storage/storage_extension.hpp +++ b/src/duckdb/src/include/duckdb/storage/storage_extension.hpp @@ -24,8 +24,9 @@ struct StorageExtensionInfo { } }; -typedef unique_ptr (*attach_function_t)(StorageExtensionInfo *storage_info, AttachedDatabase &db, - const string &name, AttachInfo &info, AccessMode access_mode); +typedef unique_ptr (*attach_function_t)(StorageExtensionInfo *storage_info, ClientContext &context, + AttachedDatabase &db, const string &name, AttachInfo &info, + AccessMode access_mode); typedef unique_ptr (*create_transaction_manager_t)(StorageExtensionInfo *storage_info, AttachedDatabase &db, Catalog &catalog); diff --git a/src/duckdb/src/include/duckdb/storage/temporary_memory_manager.hpp b/src/duckdb/src/include/duckdb/storage/temporary_memory_manager.hpp index 01576a334..f0cca43f4 100644 --- a/src/duckdb/src/include/duckdb/storage/temporary_memory_manager.hpp +++ b/src/duckdb/src/include/duckdb/storage/temporary_memory_manager.hpp @@ -65,13 +65,13 @@ class TemporaryMemoryManager { //! TemporaryMemoryState is initialized with a minimum reservation guarantee, which is either //! 512 blocks per state per thread, which is 0.125GB per thread for Storage::BLOCK_ALLOC_SIZE = 262144 static constexpr const idx_t MINIMUM_RESERVATION_PER_STATE_PER_THREAD = idx_t(512) * Storage::BLOCK_ALLOC_SIZE; - //! Or 1/16th% of main memory, if that is lower + //! Or 1/16th of main memory, if that is lower static constexpr const idx_t MINIMUM_RESERVATION_MEMORY_LIMIT_DIVISOR = 16; //! The maximum ratio of the memory limit that we reserve using the TemporaryMemoryManager static constexpr const double MAXIMUM_MEMORY_LIMIT_RATIO = 0.8; //! The maximum ratio of the remaining memory that we reserve per TemporaryMemoryState - static constexpr const double MAXIMUM_FREE_MEMORY_RATIO = 0.6; + static constexpr const double MAXIMUM_FREE_MEMORY_RATIO = double(2) / double(3); public: //! Get the TemporaryMemoryManager @@ -105,6 +105,8 @@ class TemporaryMemoryManager { bool has_temporary_directory; //! Number of threads idx_t num_threads; + //! Max memory per query + idx_t query_max_memory; //! Currently active states reference_set_t active_states; diff --git a/src/duckdb/src/include/duckdb/transaction/transaction_context.hpp b/src/duckdb/src/include/duckdb/transaction/transaction_context.hpp index 1334ab09f..b0a50103b 100644 --- a/src/duckdb/src/include/duckdb/transaction/transaction_context.hpp +++ b/src/duckdb/src/include/duckdb/transaction/transaction_context.hpp @@ -23,7 +23,7 @@ class TransactionManager; //! current transaction class TransactionContext { public: - TransactionContext(ClientContext &context); + explicit TransactionContext(ClientContext &context); ~TransactionContext(); MetaTransaction &ActiveTransaction() { @@ -33,8 +33,8 @@ class TransactionContext { return *current_transaction; } - bool HasActiveTransaction() { - return !!current_transaction; + bool HasActiveTransaction() const { + return current_transaction.get(); } void BeginTransaction(); @@ -43,7 +43,7 @@ class TransactionContext { void ClearTransaction(); void SetAutoCommit(bool value); - bool IsAutoCommit() { + bool IsAutoCommit() const { return auto_commit; } diff --git a/src/duckdb/src/main/attached_database.cpp b/src/duckdb/src/main/attached_database.cpp index 8c099cc48..855e163bc 100644 --- a/src/duckdb/src/main/attached_database.cpp +++ b/src/duckdb/src/main/attached_database.cpp @@ -40,11 +40,13 @@ AttachedDatabase::AttachedDatabase(DatabaseInstance &db, Catalog &catalog_p, str } AttachedDatabase::AttachedDatabase(DatabaseInstance &db, Catalog &catalog_p, StorageExtension &storage_extension, - string name_p, const AttachInfo &info, AccessMode access_mode) + ClientContext &context, string name_p, const AttachInfo &info, + AccessMode access_mode) : CatalogEntry(CatalogType::DATABASE_ENTRY, catalog_p, std::move(name_p)), db(db), parent_catalog(&catalog_p) { type = access_mode == AccessMode::READ_ONLY ? AttachedDatabaseType::READ_ONLY_DATABASE : AttachedDatabaseType::READ_WRITE_DATABASE; - catalog = storage_extension.attach(storage_extension.storage_info.get(), *this, name, *info.Copy(), access_mode); + catalog = + storage_extension.attach(storage_extension.storage_info.get(), context, *this, name, *info.Copy(), access_mode); if (!catalog) { throw InternalException("AttachedDatabase - attach function did not return a catalog"); } diff --git a/src/duckdb/src/main/client_context.cpp b/src/duckdb/src/main/client_context.cpp index 672ebefa4..459dfe52f 100644 --- a/src/duckdb/src/main/client_context.cpp +++ b/src/duckdb/src/main/client_context.cpp @@ -43,6 +43,7 @@ #include "duckdb/transaction/transaction_manager.hpp" #include "duckdb/storage/data_table.hpp" #include "duckdb/common/exception/transaction_exception.hpp" +#include "duckdb/main/client_context_state.hpp" namespace duckdb { @@ -73,8 +74,56 @@ struct ActiveQueryContext { BaseQueryResult *open_result = nullptr; }; +#ifdef DEBUG +struct DebugClientContextState : public ClientContextState { + ~DebugClientContextState() override { + D_ASSERT(!active_transaction); + D_ASSERT(!active_query); + } + + bool active_transaction = false; + bool active_query = false; + + void QueryBegin(ClientContext &context) override { + if (active_query) { + throw InternalException("DebugClientContextState::QueryBegin called when a query is already active"); + } + active_query = true; + } + void QueryEnd(ClientContext &context) override { + if (!active_query) { + throw InternalException("DebugClientContextState::QueryEnd called when no query is active"); + } + active_query = false; + } + void TransactionBegin(MetaTransaction &transaction, ClientContext &context) override { + if (active_transaction) { + throw InternalException( + "DebugClientContextState::TransactionBegin called when a transaction is already active"); + } + active_transaction = true; + } + void TransactionCommit(MetaTransaction &transaction, ClientContext &context) override { + if (!active_transaction) { + throw InternalException("DebugClientContextState::TransactionCommit called when no transaction is active"); + } + active_transaction = false; + } + void TransactionRollback(MetaTransaction &transaction, ClientContext &context) override { + if (!active_transaction) { + throw InternalException( + "DebugClientContextState::TransactionRollback called when no transaction is active"); + } + active_transaction = false; + } +}; +#endif + ClientContext::ClientContext(shared_ptr database) : db(std::move(database)), interrupted(false), client_data(make_uniq(*this)), transaction(*this) { +#ifdef DEBUG + registered_state["debug_client_context_state"] = make_uniq(); +#endif } ClientContext::~ClientContext() { @@ -115,39 +164,39 @@ unique_ptr ClientContext::ErrorResult(ErrorData error, const string &query) { return make_uniq(std::move(error)); } -void ClientContext::BeginTransactionInternal(ClientContextLock &lock, bool requires_valid_transaction) { +void ClientContext::BeginQueryInternal(ClientContextLock &lock, const string &query) { // check if we are on AutoCommit. In this case we should start a transaction D_ASSERT(!active_query); auto &db_inst = DatabaseInstance::GetDatabase(*this); if (ValidChecker::IsInvalidated(db_inst)) { throw ErrorManager::InvalidatedDatabase(*this, ValidChecker::InvalidatedMessage(db_inst)); } - if (requires_valid_transaction && transaction.HasActiveTransaction() && - ValidChecker::IsInvalidated(transaction.ActiveTransaction())) { - throw ErrorManager::InvalidatedTransaction(*this); - } active_query = make_uniq(); if (transaction.IsAutoCommit()) { transaction.BeginTransaction(); } -} - -void ClientContext::BeginQueryInternal(ClientContextLock &lock, const string &query) { - BeginTransactionInternal(lock, false); + transaction.SetActiveQuery(db->GetDatabaseManager().GetNewQueryNumber()); LogQueryInternal(lock, query); active_query->query = query; query_progress.Initialize(); - transaction.SetActiveQuery(db->GetDatabaseManager().GetNewQueryNumber()); + // Notify any registered state of query begin + for (auto const &s : registered_state) { + s.second->QueryBegin(*this); + } } ErrorData ClientContext::EndQueryInternal(ClientContextLock &lock, bool success, bool invalidate_transaction) { client_data->profiler->EndQuery(); + if (active_query->executor) { + active_query->executor->CancelTasks(); + } // Notify any registered state of query end for (auto const &s : registered_state) { - s.second->QueryEnd(); + s.second->QueryEnd(*this); } + active_query->progress_bar.reset(); D_ASSERT(active_query.get()); active_query.reset(); @@ -155,17 +204,6 @@ ErrorData ClientContext::EndQueryInternal(ClientContextLock &lock, bool success, ErrorData error; try { if (transaction.HasActiveTransaction()) { - // Move the query profiler into the history - auto &prev_profilers = client_data->query_profiler_history->GetPrevProfilers(); - prev_profilers.emplace_back(transaction.GetActiveQuery(), std::move(client_data->profiler)); - // Reinitialize the query profiler - client_data->profiler = make_shared(*this); - // Propagate settings of the saved query into the new profiler. - client_data->profiler->Propagate(*prev_profilers.back().second); - if (prev_profilers.size() >= client_data->query_profiler_history->GetPrevProfilersSize()) { - prev_profilers.pop_front(); - } - transaction.ResetActiveQuery(); if (transaction.IsAutoCommit()) { if (success) { diff --git a/src/duckdb/src/main/client_data.cpp b/src/duckdb/src/main/client_data.cpp index 8553a65ef..5b061f1da 100644 --- a/src/duckdb/src/main/client_data.cpp +++ b/src/duckdb/src/main/client_data.cpp @@ -35,7 +35,6 @@ class ClientFileSystem : public OpenerFileSystem { ClientData::ClientData(ClientContext &context) : catalog_search_path(make_uniq(context)) { auto &db = DatabaseInstance::GetDatabase(context); profiler = make_shared(context); - query_profiler_history = make_uniq(); temporary_objects = make_shared(db, AttachedDatabaseType::TEMP_DATABASE); temporary_objects->oid = DatabaseManager::Get(db).ModifyCatalog(); random_engine = make_uniq(); diff --git a/src/duckdb/src/main/config.cpp b/src/duckdb/src/main/config.cpp index 1c866eb73..2dc67e40e 100644 --- a/src/duckdb/src/main/config.cpp +++ b/src/duckdb/src/main/config.cpp @@ -103,7 +103,6 @@ static ConfigurationOption internal_options[] = {DUCKDB_GLOBAL(AccessModeSetting DUCKDB_LOCAL(PivotLimitSetting), DUCKDB_LOCAL(PreserveIdentifierCase), DUCKDB_GLOBAL(PreserveInsertionOrder), - DUCKDB_LOCAL(ProfilerHistorySize), DUCKDB_LOCAL(ProfileOutputSetting), DUCKDB_LOCAL(ProfilingModeSetting), DUCKDB_LOCAL_ALIAS("profiling_output", ProfileOutputSetting), @@ -339,14 +338,6 @@ idx_t DBConfig::GetSystemMaxThreads(FileSystem &fs) { #endif } -void DBConfig::SetDefaultMaxThreads() { -#ifndef DUCKDB_NO_THREADS - options.maximum_threads = GetSystemMaxThreads(*file_system); -#else - options.maximum_threads = 1; -#endif -} - idx_t DBConfig::ParseMemoryLimit(const string &arg) { if (arg[0] == '-' || arg == "null" || arg == "none") { return DConstants::INVALID_INDEX; diff --git a/src/duckdb/src/main/database.cpp b/src/duckdb/src/main/database.cpp index 189131493..61655d427 100644 --- a/src/duckdb/src/main/database.cpp +++ b/src/duckdb/src/main/database.cpp @@ -34,6 +34,7 @@ DBConfig::DBConfig() { cast_functions = make_uniq(*this); index_types = make_uniq(); error_manager = make_uniq(); + secret_manager = make_uniq(); } DBConfig::DBConfig(bool read_only) : DBConfig::DBConfig() { @@ -123,8 +124,8 @@ ConnectionManager &ConnectionManager::Get(ClientContext &context) { return ConnectionManager::Get(DatabaseInstance::GetDatabase(context)); } -unique_ptr DatabaseInstance::CreateAttachedDatabase(const AttachInfo &info, const string &type, - AccessMode access_mode) { +unique_ptr DatabaseInstance::CreateAttachedDatabase(ClientContext &context, const AttachInfo &info, + const string &type, AccessMode access_mode) { unique_ptr attached_database; if (!type.empty()) { // find the storage extension @@ -137,7 +138,7 @@ unique_ptr DatabaseInstance::CreateAttachedDatabase(const Atta if (entry->second->attach != nullptr && entry->second->create_transaction_manager != nullptr) { // use storage extension to create the initial database attached_database = make_uniq(*this, Catalog::GetSystemCatalog(*this), *entry->second, - info.name, info, access_mode); + context, info.name, info, access_mode); } else { attached_database = make_uniq(*this, Catalog::GetSystemCatalog(*this), info.name, info.path, access_mode); @@ -243,7 +244,7 @@ void DatabaseInstance::Initialize(const char *database_path, DBConfig *user_conf } // only increase thread count after storage init because we get races on catalog otherwise - scheduler->SetThreads(config.options.maximum_threads); + scheduler->SetThreads(config.options.maximum_threads, config.options.external_threads); scheduler->RelaunchThreads(); } @@ -328,14 +329,12 @@ void DatabaseInstance::Configure(DBConfig &new_config) { } if (new_config.secret_manager) { config.secret_manager = std::move(new_config.secret_manager); - } else { - config.secret_manager = make_uniq(); } if (config.options.maximum_memory == (idx_t)-1) { config.SetDefaultMaxMemory(); } if (new_config.options.maximum_threads == (idx_t)-1) { - config.SetDefaultMaxThreads(); + config.options.maximum_threads = config.GetSystemMaxThreads(*config.file_system); } config.allocator = std::move(new_config.allocator); if (!config.allocator) { diff --git a/src/duckdb/src/main/database_manager.cpp b/src/duckdb/src/main/database_manager.cpp index c6cc705d3..77f5448a5 100644 --- a/src/duckdb/src/main/database_manager.cpp +++ b/src/duckdb/src/main/database_manager.cpp @@ -38,7 +38,7 @@ optional_ptr DatabaseManager::AttachDatabase(ClientContext &co const string &db_type, AccessMode access_mode) { // now create the attached database auto &db = DatabaseInstance::GetDatabase(context); - auto attached_db = db.CreateAttachedDatabase(info, db_type, access_mode); + auto attached_db = db.CreateAttachedDatabase(context, info, db_type, access_mode); if (db_type.empty()) { InsertDatabasePath(context, info.path, attached_db->name); diff --git a/src/duckdb/src/main/extension/extension_load.cpp b/src/duckdb/src/main/extension/extension_load.cpp index 6cd662a8f..a001f2b99 100644 --- a/src/duckdb/src/main/extension/extension_load.cpp +++ b/src/duckdb/src/main/extension/extension_load.cpp @@ -107,6 +107,8 @@ bool ExtensionHelper::TryInitialLoad(DBConfig &config, FileSystem &fs, const str } filename = fs.JoinPath(local_path, extension_name + ".duckdb_extension"); #endif + } else { + filename = fs.ExpandPath(filename); } if (!fs.FileExists(filename)) { string message; diff --git a/src/duckdb/src/main/query_result.cpp b/src/duckdb/src/main/query_result.cpp index c24fab135..cd3e3b943 100644 --- a/src/duckdb/src/main/query_result.cpp +++ b/src/duckdb/src/main/query_result.cpp @@ -67,6 +67,30 @@ QueryResult::QueryResult(QueryResultType type, ErrorData error) QueryResult::~QueryResult() { } +void QueryResult::DeduplicateColumns(vector &names) { + unordered_map name_map; + for (auto &column_name : names) { + // put it all lower_case + auto low_column_name = StringUtil::Lower(column_name); + if (name_map.find(low_column_name) == name_map.end()) { + // Name does not exist yet + name_map[low_column_name]++; + } else { + // Name already exists, we add _x where x is the repetition number + string new_column_name = column_name + "_" + std::to_string(name_map[low_column_name]); + auto new_column_name_low = StringUtil::Lower(new_column_name); + while (name_map.find(new_column_name_low) != name_map.end()) { + // This name is already here due to a previous definition + name_map[low_column_name]++; + new_column_name = column_name + "_" + std::to_string(name_map[low_column_name]); + new_column_name_low = StringUtil::Lower(new_column_name); + } + column_name = new_column_name; + name_map[new_column_name_low]++; + } + } +} + const string &QueryResult::ColumnName(idx_t index) const { D_ASSERT(index < names.size()); return names[index]; diff --git a/src/duckdb/src/main/secret/secret_manager.cpp b/src/duckdb/src/main/secret/secret_manager.cpp index a1faa37e7..c972a9ea9 100644 --- a/src/duckdb/src/main/secret/secret_manager.cpp +++ b/src/duckdb/src/main/secret/secret_manager.cpp @@ -31,8 +31,8 @@ SecretCatalogEntry::SecretCatalogEntry(unique_ptr secret_p, Ca secret = make_uniq(std::move(secret_p)); } -const BaseSecret &SecretMatch::GetSecret() { - return *secret_entry.get()->secret; +const BaseSecret &SecretMatch::GetSecret() const { + return *secret_entry->secret; } constexpr const char *SecretManager::TEMPORARY_STORAGE_NAME; @@ -44,12 +44,9 @@ void SecretManager::Initialize(DatabaseInstance &db) { // Construct default path LocalFileSystem fs; config.default_secret_path = fs.GetHomeDirectory(); - vector path_components = {".duckdb", "stored_secrets", ExtensionHelper::GetVersionDirectoryName()}; + vector path_components = {".duckdb", "stored_secrets"}; for (auto &path_ele : path_components) { config.default_secret_path = fs.JoinPath(config.default_secret_path, path_ele); - if (!fs.DirectoryExists(config.default_secret_path)) { - fs.CreateDirectory(config.default_secret_path); - } } config.secret_path = config.default_secret_path; @@ -121,18 +118,17 @@ void SecretManager::RegisterSecretFunction(CreateSecretFunction function, OnCrea secret_functions.insert({function.secret_type, new_set}); } -optional_ptr SecretManager::RegisterSecret(CatalogTransaction transaction, - unique_ptr secret, - OnCreateConflict on_conflict, SecretPersistType persist_type, - const string &storage) { +unique_ptr SecretManager::RegisterSecret(CatalogTransaction transaction, + unique_ptr secret, OnCreateConflict on_conflict, + SecretPersistType persist_type, const string &storage) { InitializeSecrets(transaction); return RegisterSecretInternal(transaction, std::move(secret), on_conflict, persist_type, storage); } -optional_ptr SecretManager::RegisterSecretInternal(CatalogTransaction transaction, - unique_ptr secret, - OnCreateConflict on_conflict, - SecretPersistType persist_type, const string &storage) { +unique_ptr SecretManager::RegisterSecretInternal(CatalogTransaction transaction, + unique_ptr secret, + OnCreateConflict on_conflict, + SecretPersistType persist_type, const string &storage) { //! Ensure we only create secrets for known types; LookupTypeInternal(secret->GetType()); @@ -159,6 +155,11 @@ optional_ptr SecretManager::RegisterSecretInternal(CatalogTransacti //! Lookup which backend to store the secret in auto backend = GetSecretStorage(resolved_storage); if (!backend) { + if (!config.allow_persistent_secrets && + (persist_type == SecretPersistType::PERSISTENT || storage == LOCAL_FILE_STORAGE_NAME)) { + throw InvalidInputException("Persistent secrets are disabled. Restart DuckDB and enable persistent secrets " + "through 'SET allow_persistent_secrets=true'"); + } throw InvalidInputException("Secret storage '%s' not found!", resolved_storage); } @@ -207,7 +208,7 @@ optional_ptr SecretManager::LookupFunctionInternal(const s return nullptr; } -optional_ptr SecretManager::CreateSecret(ClientContext &context, const CreateSecretInfo &info) { +unique_ptr SecretManager::CreateSecret(ClientContext &context, const CreateSecretInfo &info) { // Note that a context is required for CreateSecret, as the CreateSecretFunction expects one auto transaction = CatalogTransaction::GetSystemCatalogTransaction(context); InitializeSecrets(transaction); @@ -294,7 +295,7 @@ SecretMatch SecretManager::LookupSecret(CatalogTransaction transaction, const st InitializeSecrets(transaction); int64_t best_match_score = NumericLimits::Minimum(); - optional_ptr best_match = nullptr; + unique_ptr best_match = nullptr; for (const auto &storage_ref : GetSecretStorages()) { if (!storage_ref.get().IncludeInLookups()) { @@ -302,7 +303,7 @@ SecretMatch SecretManager::LookupSecret(CatalogTransaction transaction, const st } auto match = storage_ref.get().LookupSecret(path, type, &transaction); if (match.HasMatch() && match.score > best_match_score) { - best_match = match.secret_entry.get(); + best_match = std::move(match.secret_entry); best_match_score = match.score; } } @@ -314,11 +315,11 @@ SecretMatch SecretManager::LookupSecret(CatalogTransaction transaction, const st return SecretMatch(); } -optional_ptr SecretManager::GetSecretByName(CatalogTransaction transaction, const string &name, - const string &storage) { +unique_ptr SecretManager::GetSecretByName(CatalogTransaction transaction, const string &name, + const string &storage) { InitializeSecrets(transaction); - optional_ptr result; + unique_ptr result = nullptr; bool found = false; if (!storage.empty()) { @@ -339,7 +340,7 @@ optional_ptr SecretManager::GetSecretByName(CatalogTransaction tran "Ambiguity detected for secret name '%s', secret occurs in multiple storage backends.", name); } - result = lookup; + result = std::move(lookup); found = true; } } @@ -428,10 +429,10 @@ SecretType SecretManager::LookupTypeInternal(const string &type) { throw InvalidInputException("Secret type '%s' not found", type); } -vector> SecretManager::AllSecrets(CatalogTransaction transaction) { +vector SecretManager::AllSecrets(CatalogTransaction transaction) { InitializeSecrets(transaction); - vector> result; + vector result; // Add results from all backends to the result set for (const auto &backend : secret_storages) { @@ -504,9 +505,11 @@ void SecretManager::InitializeSecrets(CatalogTransaction transaction) { // load the tmp storage LoadSecretStorageInternal(make_uniq(TEMPORARY_STORAGE_NAME, *transaction.db)); - // load the persistent storage if enabled - LoadSecretStorageInternal( - make_uniq(*this, *transaction.db, LOCAL_FILE_STORAGE_NAME, config.secret_path)); + if (config.allow_persistent_secrets) { + // load the persistent storage if enabled + LoadSecretStorageInternal( + make_uniq(*this, *transaction.db, LOCAL_FILE_STORAGE_NAME, config.secret_path)); + } initialized = true; } diff --git a/src/duckdb/src/main/secret/secret_storage.cpp b/src/duckdb/src/main/secret/secret_storage.cpp index 83b53c47f..5be8a68a4 100644 --- a/src/duckdb/src/main/secret/secret_storage.cpp +++ b/src/duckdb/src/main/secret/secret_storage.cpp @@ -40,9 +40,9 @@ SecretMatch SecretStorage::SelectBestMatch(SecretEntry &secret_entry, const stri } } -optional_ptr CatalogSetSecretStorage::StoreSecret(unique_ptr secret, - OnCreateConflict on_conflict, - optional_ptr transaction) { +unique_ptr CatalogSetSecretStorage::StoreSecret(unique_ptr secret, + OnCreateConflict on_conflict, + optional_ptr transaction) { if (secrets->GetEntry(GetTransactionOrDefault(transaction), secret->GetName())) { if (on_conflict == OnCreateConflict::ERROR_ON_CONFLICT) { string persist_string = persistent ? "Persistent" : "Temporary"; @@ -71,11 +71,11 @@ optional_ptr CatalogSetSecretStorage::StoreSecret(unique_ptrGetEntry(GetTransactionOrDefault(transaction), secret_name)->Cast(); - return secret_catalog_entry->secret; + return make_uniq(*secret_catalog_entry->secret); } -vector> CatalogSetSecretStorage::AllSecrets(optional_ptr transaction) { - vector> ret_value; +vector CatalogSetSecretStorage::AllSecrets(optional_ptr transaction) { + vector ret_value; const std::function callback = [&](CatalogEntry &entry) { auto &cast_entry = entry.Cast(); ret_value.push_back(*cast_entry.secret); @@ -117,13 +117,13 @@ SecretMatch CatalogSetSecretStorage::LookupSecret(const string &path, const stri return SecretMatch(); } -optional_ptr CatalogSetSecretStorage::GetSecretByName(const string &name, - optional_ptr transaction) { +unique_ptr CatalogSetSecretStorage::GetSecretByName(const string &name, + optional_ptr transaction) { auto res = secrets->GetEntry(GetTransactionOrDefault(transaction), name); if (res) { auto &cast_entry = res->Cast(); - return cast_entry.secret; + return make_uniq(*cast_entry.secret); } return nullptr; @@ -134,13 +134,9 @@ LocalFileSecretStorage::LocalFileSecretStorage(SecretManager &manager, DatabaseI : CatalogSetSecretStorage(db_p, name_p), secret_path(secret_path) { persistent = true; + // Check existence of persistent secret dir LocalFileSystem fs; - - if (!fs.DirectoryExists(secret_path)) { - fs.CreateDirectory(secret_path); - } - - if (persistent_secrets.empty()) { + if (fs.DirectoryExists(secret_path)) { fs.ListFiles(secret_path, [&](const string &fname, bool is_dir) { string full_path = fs.JoinPath(secret_path, fname); @@ -172,6 +168,35 @@ CatalogTransaction CatalogSetSecretStorage::GetTransactionOrDefault(optional_ptr void LocalFileSecretStorage::WriteSecret(const BaseSecret &secret, OnCreateConflict on_conflict) { LocalFileSystem fs; + + // We may need to create the secret dir here if the directory was not present during LocalFileSecretStorage + // construction + if (!fs.DirectoryExists(secret_path)) { + // TODO: recursive directory creation should probably live in filesystem + auto sep = fs.PathSeparator(secret_path); + auto splits = StringUtil::Split(secret_path, sep); + D_ASSERT(!splits.empty()); + string extension_directory_prefix; + if (StringUtil::StartsWith(secret_path, sep)) { + extension_directory_prefix = sep; // this is swallowed by Split otherwise + } + try { + for (auto &split : splits) { + extension_directory_prefix = extension_directory_prefix + split + sep; + if (!fs.DirectoryExists(extension_directory_prefix)) { + fs.CreateDirectory(extension_directory_prefix); + } + } + } catch (std::exception &ex) { + ErrorData error(ex); + if (error.Type() == ExceptionType::IO) { + throw IOException("Failed to initialize persistent storage directory. (original error: '%s')", + error.RawMessage()); + } + throw; + } + } + auto file_path = fs.JoinPath(secret_path, secret.GetName() + ".duckdb_secret"); if (fs.FileExists(file_path)) { @@ -201,6 +226,7 @@ void LocalFileSecretStorage::RemoveSecret(const string &secret, OnEntryNotFound "instance. (original error: '%s')", file, error.RawMessage()); } + throw; } } diff --git a/src/duckdb/src/main/settings/settings.cpp b/src/duckdb/src/main/settings/settings.cpp index 2cecda573..2d801e988 100644 --- a/src/duckdb/src/main/settings/settings.cpp +++ b/src/duckdb/src/main/settings/settings.cpp @@ -560,7 +560,7 @@ void CustomExtensionRepository::ResetGlobal(DatabaseInstance *db, DBConfig &conf } void CustomExtensionRepository::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) { - config.options.custom_extension_repo = StringUtil::Lower(input.ToString()); + config.options.custom_extension_repo = input.ToString(); } Value CustomExtensionRepository::GetSetting(ClientContext &context) { @@ -576,7 +576,7 @@ void AutoloadExtensionRepository::ResetGlobal(DatabaseInstance *db, DBConfig &co } void AutoloadExtensionRepository::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) { - config.options.autoinstall_extension_repo = StringUtil::Lower(input.ToString()); + config.options.autoinstall_extension_repo = input.ToString(); } Value AutoloadExtensionRepository::GetSetting(ClientContext &context) { @@ -722,11 +722,23 @@ Value ExtensionDirectorySetting::GetSetting(ClientContext &context) { // External Threads Setting //===--------------------------------------------------------------------===// void ExternalThreadsSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) { - config.options.external_threads = input.GetValue(); + auto new_val = input.GetValue(); + if (new_val < 0) { + throw SyntaxException("Must have a non-negative number of external threads!"); + } + idx_t new_external_threads = new_val; + if (db) { + TaskScheduler::GetScheduler(*db).SetThreads(config.options.maximum_threads, new_external_threads); + } + config.options.external_threads = new_external_threads; } void ExternalThreadsSetting::ResetGlobal(DatabaseInstance *db, DBConfig &config) { - config.options.external_threads = DBConfig().options.external_threads; + idx_t new_external_threads = DBConfig().options.external_threads; + if (db) { + TaskScheduler::GetScheduler(*db).SetThreads(config.options.maximum_threads, new_external_threads); + } + config.options.external_threads = new_external_threads; } Value ExternalThreadsSetting::GetSetting(ClientContext &context) { @@ -1066,27 +1078,6 @@ Value ExportLargeBufferArrow::GetSetting(ClientContext &context) { return Value::BOOLEAN(export_large_buffers_arrow); } -//===--------------------------------------------------------------------===// -// Profiler History Size -//===--------------------------------------------------------------------===// -void ProfilerHistorySize::ResetLocal(ClientContext &context) { - auto &client_data = ClientData::Get(context); - client_data.query_profiler_history->ResetProfilerHistorySize(); -} - -void ProfilerHistorySize::SetLocal(ClientContext &context, const Value &input) { - auto size = input.GetValue(); - if (size <= 0) { - throw ParserException("Size should be >= 0"); - } - auto &client_data = ClientData::Get(context); - client_data.query_profiler_history->SetProfilerHistorySize(size); -} - -Value ProfilerHistorySize::GetSetting(ClientContext &context) { - return Value(); -} - //===--------------------------------------------------------------------===// // Profile Output //===--------------------------------------------------------------------===// @@ -1246,14 +1237,23 @@ Value TempDirectorySetting::GetSetting(ClientContext &context) { // Threads Setting //===--------------------------------------------------------------------===// void ThreadsSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) { - config.options.maximum_threads = input.GetValue(); + auto new_val = input.GetValue(); + if (new_val < 1) { + throw SyntaxException("Must have at least 1 thread!"); + } + idx_t new_maximum_threads = new_val; if (db) { - TaskScheduler::GetScheduler(*db).SetThreads(config.options.maximum_threads); + TaskScheduler::GetScheduler(*db).SetThreads(new_maximum_threads, config.options.external_threads); } + config.options.maximum_threads = new_maximum_threads; } void ThreadsSetting::ResetGlobal(DatabaseInstance *db, DBConfig &config) { - config.SetDefaultMaxThreads(); + idx_t new_maximum_threads = config.GetSystemMaxThreads(*config.file_system); + if (db) { + TaskScheduler::GetScheduler(*db).SetThreads(new_maximum_threads, config.options.external_threads); + } + config.options.maximum_threads = new_maximum_threads; } Value ThreadsSetting::GetSetting(ClientContext &context) { diff --git a/src/duckdb/src/optimizer/statistics/operator/propagate_filter.cpp b/src/duckdb/src/optimizer/statistics/operator/propagate_filter.cpp index a9bfe91c6..39d9d61e2 100644 --- a/src/duckdb/src/optimizer/statistics/operator/propagate_filter.cpp +++ b/src/duckdb/src/optimizer/statistics/operator/propagate_filter.cpp @@ -234,8 +234,7 @@ unique_ptr StatisticsPropagator::PropagateStatistics(LogicalFilt filter.expressions.erase(filter.expressions.begin() + i); i--; if (filter.expressions.empty()) { - // all conditions have been erased: remove the entire filter - *node_ptr = std::move(filter.children[0]); + // just break. The physical filter planner will plan a projection instead break; } } else if (ExpressionIsConstant(*condition, Value::BOOLEAN(false)) || diff --git a/src/duckdb/src/parallel/executor.cpp b/src/duckdb/src/parallel/executor.cpp index 730f04a09..2eb6aa922 100644 --- a/src/duckdb/src/parallel/executor.cpp +++ b/src/duckdb/src/parallel/executor.cpp @@ -160,9 +160,8 @@ void Executor::SchedulePipeline(const shared_ptr &meta_pipeline, S auto root_entry = event_map.find(*pipeline); D_ASSERT(root_entry != event_map.end()); auto &pipeline_stack = root_entry->second; - // iterate in reverse so the deepest dependencies are added first - for (auto it = dependencies->rbegin(); it != dependencies->rend(); ++it) { - auto event_entry = event_map.find(*it); + for (auto &dependency : *dependencies) { + auto event_entry = event_map.find(dependency); D_ASSERT(event_entry != event_map.end()); auto &dependency_stack = event_entry->second; pipeline_stack.pipeline_event.AddDependency(dependency_stack.pipeline_event); @@ -175,8 +174,8 @@ void Executor::ScheduleEventsInternal(ScheduleEventData &event_data) { D_ASSERT(events.empty()); // create all the required pipeline events - for (auto &pipeline : event_data.meta_pipelines) { - SchedulePipeline(pipeline, event_data); + for (auto &meta_pipeline : event_data.meta_pipelines) { + SchedulePipeline(meta_pipeline, event_data); } // set up the dependencies across MetaPipelines @@ -187,12 +186,36 @@ void Executor::ScheduleEventsInternal(ScheduleEventData &event_data) { auto dep = dependency.lock(); D_ASSERT(dep); auto event_map_entry = event_map.find(*dep); + if (event_map_entry == event_map.end()) { + continue; + } D_ASSERT(event_map_entry != event_map.end()); auto &dep_entry = event_map_entry->second; entry.second.pipeline_event.AddDependency(dep_entry.pipeline_complete_event); } } + // make pipeline_finish_event of each MetaPipeline depend on the pipeline_event of the base pipeline of its sublings + // this allows TemporaryMemoryManager to more fairly distribute memory + for (auto &meta_pipeline : event_data.meta_pipelines) { + vector> children; + meta_pipeline->GetMetaPipelines(children, false, true); + for (auto &child1 : children) { + auto &child1_base = *child1->GetBasePipeline(); + auto child1_entry = event_map.find(child1_base); + D_ASSERT(child1_entry != event_map.end()); + for (auto &child2 : children) { + if (RefersToSameObject(*child1, *child2)) { + continue; + } + auto &child2_base = *child2->GetBasePipeline(); + auto child2_entry = event_map.find(child2_base); + D_ASSERT(child2_entry != event_map.end()); + child1_entry->second.pipeline_finish_event.AddDependency(child2_entry->second.pipeline_event); + } + } + } + // verify that we have no cyclic dependencies VerifyScheduledEvents(event_data); @@ -268,10 +291,6 @@ void Executor::AddRecursiveCTE(PhysicalOperator &rec_cte) { recursive_ctes.push_back(rec_cte); } -void Executor::AddMaterializedCTE(PhysicalOperator &mat_cte) { - materialized_ctes.push_back(mat_cte); -} - void Executor::ReschedulePipelines(const vector> &pipelines_p, vector> &events_p) { ScheduleEventData event_data(pipelines_p, events_p, false); @@ -349,12 +368,6 @@ void Executor::InitializeInternal(PhysicalOperator &plan) { rec_cte.recursive_meta_pipeline->Ready(); } - // ready materialized cte pipelines too - for (auto &mat_cte_ref : materialized_ctes) { - auto &mat_cte = mat_cte_ref.get().Cast(); - mat_cte.recursive_meta_pipeline->Ready(); - } - // set root pipelines, i.e., all pipelines that end in the final sink root_pipeline->GetPipelines(root_pipelines, false); root_pipeline_idx = 0; @@ -392,16 +405,14 @@ void Executor::CancelTasks() { auto &rec_cte = rec_cte_ref.get().Cast(); rec_cte.recursive_meta_pipeline.reset(); } - for (auto &mat_cte_ref : materialized_ctes) { - auto &mat_cte = mat_cte_ref.get().Cast(); - mat_cte.recursive_meta_pipeline.reset(); - } pipelines.clear(); root_pipelines.clear(); to_be_rescheduled_tasks.clear(); events.clear(); } + // Take all pending tasks and execute them until they cancel WorkOnTasks(); + // In case there are still tasks being worked, wait for those to properly finish as well for (auto &weak_ref : weak_references) { while (true) { auto weak = weak_ref.lock(); diff --git a/src/duckdb/src/parallel/task_scheduler.cpp b/src/duckdb/src/parallel/task_scheduler.cpp index 9455e12eb..aad0a1336 100644 --- a/src/duckdb/src/parallel/task_scheduler.cpp +++ b/src/duckdb/src/parallel/task_scheduler.cpp @@ -97,12 +97,12 @@ ProducerToken::~ProducerToken() { TaskScheduler::TaskScheduler(DatabaseInstance &db) : db(db), queue(make_uniq()), - allocator_flush_threshold(db.config.options.allocator_flush_threshold), thread_count(1) { + allocator_flush_threshold(db.config.options.allocator_flush_threshold), thread_count(0) { } TaskScheduler::~TaskScheduler() { #ifndef DUCKDB_NO_THREADS - RelaunchThreadsInternal(1); + RelaunchThreadsInternal(0); #endif } @@ -231,18 +231,22 @@ static void ThreadExecuteTasks(TaskScheduler *scheduler, atomic *marker) { int32_t TaskScheduler::NumberOfThreads() { lock_guard t(thread_lock); auto &config = DBConfig::GetConfig(db); - return threads.size() + config.options.external_threads + 1; + return threads.size() + config.options.external_threads; } -void TaskScheduler::SetThreads(int32_t n) { +void TaskScheduler::SetThreads(idx_t total_threads, idx_t external_threads) { + if (total_threads == 0) { + throw SyntaxException("Number of threads must be positive!"); + } #ifndef DUCKDB_NO_THREADS - if (n < 1) { - throw SyntaxException("Must have at least 1 thread!"); + if (total_threads < external_threads) { + throw SyntaxException("Number of threads can't be smaller than number of external threads!"); } - thread_count = n; + thread_count = total_threads - external_threads; #else - if (n != 1) { - throw NotImplementedException("DuckDB was compiled without threads! Setting threads > 1 is not allowed."); + if (threads.size() != external_threads) { + throw NotImplementedException( + "DuckDB was compiled without threads! Setting threads != external_threads is not allowed."); } #endif } @@ -270,10 +274,10 @@ void TaskScheduler::RelaunchThreads() { void TaskScheduler::RelaunchThreadsInternal(int32_t n) { #ifndef DUCKDB_NO_THREADS - if (threads.size() == idx_t(n - 1)) { + idx_t new_thread_count = n; + if (threads.size() == new_thread_count) { return; } - idx_t new_thread_count = n - 1; if (threads.size() > new_thread_count) { // we are reducing the number of threads: clear all threads first for (idx_t i = 0; i < threads.size(); i++) { diff --git a/src/duckdb/src/parser/column_list.cpp b/src/duckdb/src/parser/column_list.cpp index 0f94cbc6d..f0b29f483 100644 --- a/src/duckdb/src/parser/column_list.cpp +++ b/src/duckdb/src/parser/column_list.cpp @@ -40,7 +40,7 @@ void ColumnList::AddToNameMap(ColumnDefinition &col) { idx_t index = 1; string base_name = col.Name(); while (name_map.find(col.Name()) != name_map.end()) { - col.SetName(base_name + ":" + to_string(index++)); + col.SetName(base_name + "_" + to_string(index++)); } } else { if (name_map.find(col.Name()) != name_map.end()) { diff --git a/src/duckdb/src/planner/bind_context.cpp b/src/duckdb/src/planner/bind_context.cpp index 62e504173..6323ebad9 100644 --- a/src/duckdb/src/planner/bind_context.cpp +++ b/src/duckdb/src/planner/bind_context.cpp @@ -461,7 +461,7 @@ static string AddColumnNameToBinding(const string &base_name, case_insensitive_s idx_t index = 1; string name = base_name; while (current_names.find(name) != current_names.end()) { - name = base_name + ":" + std::to_string(index++); + name = base_name + "_" + std::to_string(index++); } current_names.insert(name); return name; diff --git a/src/duckdb/src/planner/binder/expression/bind_columnref_expression.cpp b/src/duckdb/src/planner/binder/expression/bind_columnref_expression.cpp index 20ec401d7..ce6cb104b 100644 --- a/src/duckdb/src/planner/binder/expression/bind_columnref_expression.cpp +++ b/src/duckdb/src/planner/binder/expression/bind_columnref_expression.cpp @@ -103,12 +103,6 @@ unique_ptr ExpressionBinder::QualifyColumnName(const string &c return binder.bind_context.CreateColumnReference(table_name, column_name); } - // column was not found - check if it is a SQL value function - auto value_function = GetSQLValueFunction(column_name); - if (value_function) { - return value_function; - } - // it's not, find candidates and error auto similar_bindings = binder.bind_context.GetSimilarBindings(column_name); error = ErrorData(BinderException::ColumnNotFound(column_name, similar_bindings)); @@ -420,7 +414,6 @@ BindResult ExpressionBinder::BindExpression(LambdaRefExpression &lambda_ref, idx } BindResult ExpressionBinder::BindExpression(ColumnRefExpression &col_ref_p, idx_t depth) { - if (binder.GetBindingMode() == BindingMode::EXTRACT_NAMES) { return BindResult(make_uniq(Value(LogicalType::SQLNULL))); } @@ -428,6 +421,13 @@ BindResult ExpressionBinder::BindExpression(ColumnRefExpression &col_ref_p, idx_ ErrorData error; auto expr = QualifyColumnName(col_ref_p, error); if (!expr) { + if (!col_ref_p.IsQualified()) { + // column was not found - check if it is a SQL value function + auto value_function = GetSQLValueFunction(col_ref_p.GetColumnName()); + if (value_function) { + return BindExpression(value_function, depth); + } + } error.AddQueryLocation(col_ref_p); return BindResult(std::move(error)); } diff --git a/src/duckdb/src/planner/binder/expression/bind_unnest_expression.cpp b/src/duckdb/src/planner/binder/expression/bind_unnest_expression.cpp index 59ddc83b5..92a41d7f6 100644 --- a/src/duckdb/src/planner/binder/expression/bind_unnest_expression.cpp +++ b/src/duckdb/src/planner/binder/expression/bind_unnest_expression.cpp @@ -20,7 +20,7 @@ unique_ptr CreateBoundStructExtract(ClientContext &context, unique_p vector> arguments; arguments.push_back(std::move(expr)); arguments.push_back(make_uniq(Value(key))); - auto extract_function = StructExtractFun::GetFunction(); + auto extract_function = StructExtractFun::KeyExtractFunction(); auto bind_info = extract_function.bind(context, extract_function, arguments); auto return_type = extract_function.return_type; auto result = make_uniq(return_type, std::move(extract_function), std::move(arguments), @@ -29,6 +29,19 @@ unique_ptr CreateBoundStructExtract(ClientContext &context, unique_p return std::move(result); } +unique_ptr CreateBoundStructExtractIndex(ClientContext &context, unique_ptr expr, idx_t key) { + vector> arguments; + arguments.push_back(std::move(expr)); + arguments.push_back(make_uniq(Value::BIGINT(int64_t(key)))); + auto extract_function = StructExtractFun::IndexExtractFunction(); + auto bind_info = extract_function.bind(context, extract_function, arguments); + auto return_type = extract_function.return_type; + auto result = make_uniq(return_type, std::move(extract_function), std::move(arguments), + std::move(bind_info)); + result->alias = "element" + to_string(key); + return std::move(result); +} + BindResult SelectBinder::BindUnnest(FunctionExpression &function, idx_t depth, bool root_expression) { // bind the children of the function expression if (depth > 0) { @@ -185,8 +198,15 @@ BindResult SelectBinder::BindUnnest(FunctionExpression &function, idx_t depth, b if (expr->return_type.id() == LogicalTypeId::STRUCT) { // struct! push a struct_extract auto &child_types = StructType::GetChildTypes(expr->return_type); - for (auto &entry : child_types) { - new_expressions.push_back(CreateBoundStructExtract(context, expr->Copy(), entry.first)); + if (StructType::IsUnnamed(expr->return_type)) { + for (idx_t child_index = 0; child_index < child_types.size(); child_index++) { + new_expressions.push_back( + CreateBoundStructExtractIndex(context, expr->Copy(), child_index + 1)); + } + } else { + for (auto &entry : child_types) { + new_expressions.push_back(CreateBoundStructExtract(context, expr->Copy(), entry.first)); + } } has_structs = true; } else { diff --git a/src/duckdb/src/planner/binder/query_node/bind_cte_node.cpp b/src/duckdb/src/planner/binder/query_node/bind_cte_node.cpp index bc8772a55..c422b7d84 100644 --- a/src/duckdb/src/planner/binder/query_node/bind_cte_node.cpp +++ b/src/duckdb/src/planner/binder/query_node/bind_cte_node.cpp @@ -36,7 +36,7 @@ unique_ptr Binder::BindNode(CTENode &statement) { for (auto &n : result->names) { string name = n; while (find(names.begin(), names.end(), name) != names.end()) { - name = n + ":" + std::to_string(index++); + name = n + "_" + std::to_string(index++); } names.push_back(name); } @@ -56,6 +56,9 @@ unique_ptr Binder::BindNode(CTENode &statement) { // Add bindings of left side to temporary CTE bindings context result->child_binder->bind_context.AddCTEBinding(result->setop_index, statement.ctename, names, result->types); result->child = result->child_binder->BindNode(*statement.child); + for (auto &c : result->query_binder->correlated_columns) { + result->child_binder->AddCorrelatedColumn(c); + } // the result types of the CTE are the types of the LHS result->types = result->child->types; diff --git a/src/duckdb/src/planner/binder/query_node/bind_recursive_cte_node.cpp b/src/duckdb/src/planner/binder/query_node/bind_recursive_cte_node.cpp index cf9ebface..1c5e34261 100644 --- a/src/duckdb/src/planner/binder/query_node/bind_recursive_cte_node.cpp +++ b/src/duckdb/src/planner/binder/query_node/bind_recursive_cte_node.cpp @@ -40,6 +40,9 @@ unique_ptr Binder::BindNode(RecursiveCTENode &statement) { result->right_binder->bind_context.AddCTEBinding(result->setop_index, statement.ctename, result->names, result->types); result->right = result->right_binder->BindNode(*statement.right); + for (auto &c : result->left_binder->correlated_columns) { + result->right_binder->AddCorrelatedColumn(c); + } // move the correlated expressions from the child binders to this binder MoveCorrelatedExpressions(*result->left_binder); diff --git a/src/duckdb/src/planner/binder/query_node/plan_cte_node.cpp b/src/duckdb/src/planner/binder/query_node/plan_cte_node.cpp index cfc079153..b3dbe43f3 100644 --- a/src/duckdb/src/planner/binder/query_node/plan_cte_node.cpp +++ b/src/duckdb/src/planner/binder/query_node/plan_cte_node.cpp @@ -17,8 +17,8 @@ unique_ptr Binder::CreatePlan(BoundCTENode &node) { std::move(cte_query), std::move(cte_child)); // check if there are any unplanned subqueries left in either child - has_unplanned_dependent_joins = - node.child_binder->has_unplanned_dependent_joins || node.query_binder->has_unplanned_dependent_joins; + has_unplanned_dependent_joins = has_unplanned_dependent_joins || node.child_binder->has_unplanned_dependent_joins || + node.query_binder->has_unplanned_dependent_joins; return VisitQueryNode(node, std::move(root)); } diff --git a/src/duckdb/src/planner/binder/query_node/plan_recursive_cte_node.cpp b/src/duckdb/src/planner/binder/query_node/plan_recursive_cte_node.cpp index 65d539f1f..7f2eafefd 100644 --- a/src/duckdb/src/planner/binder/query_node/plan_recursive_cte_node.cpp +++ b/src/duckdb/src/planner/binder/query_node/plan_recursive_cte_node.cpp @@ -17,8 +17,8 @@ unique_ptr Binder::CreatePlan(BoundRecursiveCTENode &node) { auto right_node = node.right_binder->CreatePlan(*node.right); // check if there are any unplanned subqueries left in either child - has_unplanned_dependent_joins = - node.left_binder->has_unplanned_dependent_joins || node.right_binder->has_unplanned_dependent_joins; + has_unplanned_dependent_joins = has_unplanned_dependent_joins || node.left_binder->has_unplanned_dependent_joins || + node.right_binder->has_unplanned_dependent_joins; // for both the left and right sides, cast them to the same types left_node = CastLogicalOperatorToTypes(node.left->types, node.types, std::move(left_node)); diff --git a/src/duckdb/src/planner/binder/query_node/plan_setop.cpp b/src/duckdb/src/planner/binder/query_node/plan_setop.cpp index c313ae016..caccbc45d 100644 --- a/src/duckdb/src/planner/binder/query_node/plan_setop.cpp +++ b/src/duckdb/src/planner/binder/query_node/plan_setop.cpp @@ -95,8 +95,8 @@ unique_ptr Binder::CreatePlan(BoundSetOperationNode &node) { } // check if there are any unplanned subqueries left in either child - has_unplanned_dependent_joins = - node.left_binder->has_unplanned_dependent_joins || node.right_binder->has_unplanned_dependent_joins; + has_unplanned_dependent_joins = has_unplanned_dependent_joins || node.left_binder->has_unplanned_dependent_joins || + node.right_binder->has_unplanned_dependent_joins; // create actual logical ops for setops LogicalOperatorType logical_type; diff --git a/src/duckdb/src/planner/binder/query_node/plan_subquery.cpp b/src/duckdb/src/planner/binder/query_node/plan_subquery.cpp index 8d019e142..d2ad056bb 100644 --- a/src/duckdb/src/planner/binder/query_node/plan_subquery.cpp +++ b/src/duckdb/src/planner/binder/query_node/plan_subquery.cpp @@ -333,6 +333,11 @@ static unique_ptr PlanCorrelatedSubquery(Binder &binder, BoundSubque void RecursiveDependentJoinPlanner::VisitOperator(LogicalOperator &op) { if (!op.children.empty()) { + // Collect all recursive CTEs during recursive descend + if (op.type == LogicalOperatorType::LOGICAL_RECURSIVE_CTE) { + auto &rec_cte = op.Cast(); + binder.recursive_ctes[rec_cte.table_index] = &op; + } root = std::move(op.children[0]); D_ASSERT(root); if (root->type == LogicalOperatorType::LOGICAL_DEPENDENT_JOIN) { @@ -433,6 +438,11 @@ unique_ptr Binder::PlanLateralJoin(unique_ptr // fetch the set of columns auto plan_columns = dependent_join->GetColumnBindings(); + // in case of a materialized CTE, the output is defined by the second children operator + if (dependent_join->type == LogicalOperatorType::LOGICAL_MATERIALIZED_CTE) { + plan_columns = dependent_join->children[1]->GetColumnBindings(); + } + // now create the join conditions // start off with the conditions that were passed in (if any) D_ASSERT(delim_join->conditions.empty()); diff --git a/src/duckdb/src/planner/binder/statement/bind_copy.cpp b/src/duckdb/src/planner/binder/statement/bind_copy.cpp index ac519a5dc..d696a2c29 100644 --- a/src/duckdb/src/planner/binder/statement/bind_copy.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_copy.cpp @@ -23,34 +23,6 @@ namespace duckdb { -vector GetUniqueNames(const vector &original_names) { - unordered_set name_set; - vector unique_names; - unique_names.reserve(original_names.size()); - - for (auto &name : original_names) { - auto insert_result = name_set.insert(name); - if (insert_result.second == false) { - // Could not be inserted, name already exists - idx_t index = 1; - string postfixed_name; - while (true) { - postfixed_name = StringUtil::Format("%s:%d", name, index); - auto res = name_set.insert(postfixed_name); - if (!res.second) { - index++; - continue; - } - break; - } - unique_names.push_back(postfixed_name); - } else { - unique_names.push_back(name); - } - } - return unique_names; -} - static bool GetBooleanArg(ClientContext &context, const vector &arg) { return arg.empty() || arg[0].CastAs(context, LogicalType::BOOLEAN).GetValue(); } @@ -160,7 +132,8 @@ BoundStatement Binder::BindCopyTo(CopyStatement &stmt) { } } - auto unique_column_names = GetUniqueNames(select_node.names); + auto unique_column_names = select_node.names; + QueryResult::DeduplicateColumns(unique_column_names); auto file_path = stmt.info->file_path; auto function_data = diff --git a/src/duckdb/src/planner/binder/statement/bind_create.cpp b/src/duckdb/src/planner/binder/statement/bind_create.cpp index e0c7fc1c7..fef636538 100644 --- a/src/duckdb/src/planner/binder/statement/bind_create.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_create.cpp @@ -139,12 +139,8 @@ void Binder::BindCreateViewInfo(CreateViewInfo &base) { if (base.aliases.size() > query_node.names.size()) { throw BinderException("More VIEW aliases than columns in query result"); } - // fill up the aliases with the remaining names of the bound query - base.aliases.reserve(query_node.names.size()); - for (idx_t i = base.aliases.size(); i < query_node.names.size(); i++) { - base.aliases.push_back(query_node.names[i]); - } base.types = query_node.types; + base.names = query_node.names; } SchemaCatalogEntry &Binder::BindCreateFunctionInfo(CreateInfo &info) { @@ -159,8 +155,8 @@ SchemaCatalogEntry &Binder::BindCreateFunctionInfo(CreateInfo &info) { vector dummy_types; vector dummy_names; // positional parameters - for (idx_t i = 0; i < base.function->parameters.size(); i++) { - auto param = base.function->parameters[i]->Cast(); + for (auto ¶m_expr : base.function->parameters) { + auto param = param_expr->Cast(); if (param.IsQualified()) { throw BinderException("Invalid parameter name '%s': must be unqualified", param.ToString()); } @@ -168,10 +164,10 @@ SchemaCatalogEntry &Binder::BindCreateFunctionInfo(CreateInfo &info) { dummy_names.push_back(param.GetColumnName()); } // default parameters - for (auto it = base.function->default_parameters.begin(); it != base.function->default_parameters.end(); it++) { - auto &val = it->second->Cast(); + for (auto &entry : base.function->default_parameters) { + auto &val = entry.second->Cast(); dummy_types.push_back(val.value.type()); - dummy_names.push_back(it->first); + dummy_names.push_back(entry.first); } auto this_macro_binding = make_uniq(dummy_types, dummy_names, base.name); macro_binding = this_macro_binding.get(); @@ -634,13 +630,18 @@ BoundStatement Binder::Bind(CreateStatement &stmt) { } result.plan->AddChild(std::move(query)); - } else { + } else if (create_type_info.type.id() == LogicalTypeId::USER) { // two cases: // 1: create a type with a non-existent type as source, Binder::BindLogicalType(...) will throw exception. // 2: create a type alias with a custom type. // eg. CREATE TYPE a AS INT; CREATE TYPE b AS a; // We set b to be an alias for the underlying type of a - Binder::BindLogicalType(context, create_type_info.type); + create_type_info.type = Catalog::GetType(context, schema.catalog.GetName(), schema.name, + UserType::GetTypeName(create_type_info.type)); + } else { + auto preserved_type = create_type_info.type; + BindLogicalType(context, create_type_info.type); + create_type_info.type = preserved_type; } break; } diff --git a/src/duckdb/src/planner/binder/tableref/bind_basetableref.cpp b/src/duckdb/src/planner/binder/tableref/bind_basetableref.cpp index 2300fe8c7..f8d04c5bf 100644 --- a/src/duckdb/src/planner/binder/tableref/bind_basetableref.cpp +++ b/src/duckdb/src/planner/binder/tableref/bind_basetableref.cpp @@ -225,8 +225,13 @@ unique_ptr Binder::Bind(BaseTableRef &ref) { view_binder->can_contain_nulls = true; SubqueryRef subquery(unique_ptr_cast(view_catalog_entry.query->Copy())); subquery.alias = ref.alias.empty() ? ref.table_name : ref.alias; - subquery.column_name_alias = - BindContext::AliasColumnNames(subquery.alias, view_catalog_entry.aliases, ref.column_name_alias); + // construct view names by first (1) taking the view aliases, (2) adding the view names, then (3) applying + // subquery aliases + vector view_names = view_catalog_entry.aliases; + for (idx_t n = view_names.size(); n < view_catalog_entry.names.size(); n++) { + view_names.push_back(view_catalog_entry.names[n]); + } + subquery.column_name_alias = BindContext::AliasColumnNames(subquery.alias, view_names, ref.column_name_alias); // bind the child subquery view_binder->AddBoundView(view_catalog_entry); auto bound_child = view_binder->Bind(subquery); @@ -237,9 +242,14 @@ unique_ptr Binder::Bind(BaseTableRef &ref) { D_ASSERT(bound_child->type == TableReferenceType::SUBQUERY); // verify that the types and names match up with the expected types and names auto &bound_subquery = bound_child->Cast(); - if (GetBindingMode() != BindingMode::EXTRACT_NAMES && - bound_subquery.subquery->types != view_catalog_entry.types) { - throw BinderException("Contents of view were altered: types don't match!"); + if (GetBindingMode() != BindingMode::EXTRACT_NAMES) { + if (bound_subquery.subquery->types != view_catalog_entry.types) { + throw BinderException("Contents of view were altered: types don't match!"); + } + if (bound_subquery.subquery->names.size() == view_catalog_entry.names.size() && + bound_subquery.subquery->names != view_catalog_entry.names) { + throw BinderException("Contents of view were altered: names don't match!"); + } } bind_context.AddView(bound_subquery.subquery->GetRootIndex(), subquery.alias, subquery, *bound_subquery.subquery, &view_catalog_entry); diff --git a/src/duckdb/src/planner/binder/tableref/plan_cteref.cpp b/src/duckdb/src/planner/binder/tableref/plan_cteref.cpp index 6f5ba9013..1f0fcb6b8 100644 --- a/src/duckdb/src/planner/binder/tableref/plan_cteref.cpp +++ b/src/duckdb/src/planner/binder/tableref/plan_cteref.cpp @@ -5,15 +5,7 @@ namespace duckdb { unique_ptr Binder::CreatePlan(BoundCTERef &ref) { - auto index = ref.bind_index; - - vector types; - types.reserve(ref.types.size()); - for (auto &type : ref.types) { - types.push_back(type); - } - - return make_uniq(index, ref.cte_index, types, ref.bound_columns, ref.materialized_cte); + return make_uniq(ref.bind_index, ref.cte_index, ref.types, ref.bound_columns, ref.materialized_cte); } } // namespace duckdb diff --git a/src/duckdb/src/planner/expression_iterator.cpp b/src/duckdb/src/planner/expression_iterator.cpp index 5b2ffbe87..4ed384b9f 100644 --- a/src/duckdb/src/planner/expression_iterator.cpp +++ b/src/duckdb/src/planner/expression_iterator.cpp @@ -202,6 +202,7 @@ void ExpressionIterator::EnumerateQueryNodeChildren(BoundQueryNode &node, case QueryNodeType::CTE_NODE: { auto &cte_node = node.Cast(); EnumerateQueryNodeChildren(*cte_node.child, callback); + EnumerateQueryNodeChildren(*cte_node.query, callback); break; } case QueryNodeType::SELECT_NODE: { diff --git a/src/duckdb/src/planner/subquery/flatten_dependent_join.cpp b/src/duckdb/src/planner/subquery/flatten_dependent_join.cpp index 81fa3e963..6720e8ad3 100644 --- a/src/duckdb/src/planner/subquery/flatten_dependent_join.cpp +++ b/src/duckdb/src/planner/subquery/flatten_dependent_join.cpp @@ -10,6 +10,7 @@ #include "duckdb/planner/operator/list.hpp" #include "duckdb/planner/subquery/has_correlated_expressions.hpp" #include "duckdb/planner/subquery/rewrite_correlated_expressions.hpp" +#include "duckdb/planner/subquery/rewrite_cte_scan.hpp" #include "duckdb/planner/operator/logical_dependent_join.hpp" namespace duckdb { @@ -54,6 +55,35 @@ bool FlattenDependentJoins::DetectCorrelatedExpressions(LogicalOperator *op, boo } // set the entry in the map has_correlated_expressions[op] = has_correlation; + + // If we detect correlation in a materialized or recursive CTE, the entire right side of the operator + // needs to be marked as correlated. Otherwise, function PushDownDependentJoinInternal does not do the + // right thing. + if (op->type == LogicalOperatorType::LOGICAL_MATERIALIZED_CTE || + op->type == LogicalOperatorType::LOGICAL_RECURSIVE_CTE) { + if (has_correlation) { + MarkSubtreeCorrelated(*op->children[1].get()); + } + } + return has_correlation; +} + +bool FlattenDependentJoins::MarkSubtreeCorrelated(LogicalOperator &op) { + // Do not mark base table scans as correlated + auto entry = has_correlated_expressions.find(&op); + D_ASSERT(entry != has_correlated_expressions.end()); + bool has_correlation = entry->second; + for (auto &child : op.children) { + has_correlation |= MarkSubtreeCorrelated(*child.get()); + } + if (op.type != LogicalOperatorType::LOGICAL_GET || op.children.size() == 1) { + if (op.type == LogicalOperatorType::LOGICAL_CTE_REF) { + has_correlated_expressions[&op] = true; + return true; + } else { + has_correlated_expressions[&op] = has_correlation; + } + } return has_correlation; } @@ -94,6 +124,18 @@ unique_ptr FlattenDependentJoins::PushDownDependentJoinInternal // we reached a node without correlated expressions // we can eliminate the dependent join now and create a simple cross product // now create the duplicate eliminated scan for this node + if (plan->type == LogicalOperatorType::LOGICAL_CTE_REF) { + auto &op = plan->Cast(); + + auto rec_cte = binder.recursive_ctes.find(op.cte_index); + if (rec_cte != binder.recursive_ctes.end()) { + D_ASSERT(rec_cte->second->type == LogicalOperatorType::LOGICAL_RECURSIVE_CTE); + auto &rec_cte_op = rec_cte->second->Cast(); + RewriteCTEScan cte_rewriter(op.cte_index, rec_cte_op.correlated_columns); + cte_rewriter.VisitOperator(*plan); + } + } + auto left_columns = plan->GetColumnBindings().size(); auto delim_index = binder.GenerateTableIndex(); this->base_binding = ColumnBinding(delim_index, 0); @@ -575,11 +617,62 @@ unique_ptr FlattenDependentJoins::PushDownDependentJoinInternal this->data_offset = 0; return plan; } + case LogicalOperatorType::LOGICAL_MATERIALIZED_CTE: case LogicalOperatorType::LOGICAL_RECURSIVE_CTE: { - throw BinderException("Recursive CTEs not (yet) supported in correlated subquery"); + +#ifdef DEBUG + plan->children[0]->ResolveOperatorTypes(); + plan->children[1]->ResolveOperatorTypes(); +#endif + idx_t table_index = 0; + plan->children[0] = + PushDownDependentJoinInternal(std::move(plan->children[0]), parent_propagate_null_values, lateral_depth); + if (plan->type == LogicalOperatorType::LOGICAL_RECURSIVE_CTE) { + auto &setop = plan->Cast(); + base_binding.table_index = setop.table_index; + base_binding.column_index = setop.column_count; + table_index = setop.table_index; + setop.correlated_columns = correlated_columns; + } else if (plan->type == LogicalOperatorType::LOGICAL_MATERIALIZED_CTE) { + auto &setop = plan->Cast(); + base_binding.table_index = setop.table_index; + base_binding.column_index = setop.column_count; + table_index = setop.table_index; + } + + RewriteCTEScan cte_rewriter(table_index, correlated_columns); + cte_rewriter.VisitOperator(*plan->children[1]); + + parent_propagate_null_values = false; + plan->children[1] = + PushDownDependentJoinInternal(std::move(plan->children[1]), parent_propagate_null_values, lateral_depth); + RewriteCorrelatedExpressions rewriter(this->base_binding, correlated_map, lateral_depth); + rewriter.VisitOperator(*plan); + + RewriteCorrelatedExpressions recursive_rewriter(this->base_binding, correlated_map, lateral_depth, true); + recursive_rewriter.VisitOperator(*plan->children[0]); + recursive_rewriter.VisitOperator(*plan->children[1]); + +#ifdef DEBUG + plan->children[0]->ResolveOperatorTypes(); + plan->children[1]->ResolveOperatorTypes(); +#endif + if (plan->type == LogicalOperatorType::LOGICAL_RECURSIVE_CTE) { + // we have to refer to the recursive CTE index now + auto &setop = plan->Cast(); + base_binding.table_index = setop.table_index; + base_binding.column_index = setop.column_count; + setop.column_count += correlated_columns.size(); + } + + return plan; } - case LogicalOperatorType::LOGICAL_MATERIALIZED_CTE: { - throw BinderException("Materialized CTEs not (yet) supported in correlated subquery"); + case LogicalOperatorType::LOGICAL_CTE_REF: { + auto &cteref = plan->Cast(); + // Read correlated columns from CTE_SCAN instead of from DELIM_SCAN + base_binding.table_index = cteref.table_index; + base_binding.column_index = cteref.chunk_types.size() - cteref.correlated_columns; + return plan; } case LogicalOperatorType::LOGICAL_DELIM_JOIN: { throw BinderException("Nested lateral joins or lateral joins in correlated subqueries are not (yet) supported"); diff --git a/src/duckdb/src/planner/subquery/rewrite_cte_scan.cpp b/src/duckdb/src/planner/subquery/rewrite_cte_scan.cpp new file mode 100644 index 000000000..5ac0a59be --- /dev/null +++ b/src/duckdb/src/planner/subquery/rewrite_cte_scan.cpp @@ -0,0 +1,36 @@ +#include "duckdb/planner/subquery/rewrite_cte_scan.hpp" + +#include "duckdb/planner/operator/list.hpp" + +#include "duckdb/planner/expression/bound_case_expression.hpp" +#include "duckdb/planner/expression/bound_columnref_expression.hpp" +#include "duckdb/planner/expression/bound_constant_expression.hpp" +#include "duckdb/planner/expression/bound_operator_expression.hpp" +#include "duckdb/planner/expression/bound_subquery_expression.hpp" +#include "duckdb/planner/query_node/bound_select_node.hpp" +#include "duckdb/planner/expression_iterator.hpp" +#include "duckdb/planner/tableref/bound_joinref.hpp" +#include "duckdb/planner/operator/logical_dependent_join.hpp" + +namespace duckdb { + +RewriteCTEScan::RewriteCTEScan(idx_t table_index, const vector &correlated_columns) + : table_index(table_index), correlated_columns(correlated_columns) { +} + +void RewriteCTEScan::VisitOperator(LogicalOperator &op) { + if (op.type == LogicalOperatorType::LOGICAL_CTE_REF) { + auto &cteref = op.Cast(); + + if (cteref.cte_index == table_index) { + for (auto &c : this->correlated_columns) { + cteref.chunk_types.push_back(c.type); + cteref.bound_columns.push_back(c.name); + } + cteref.correlated_columns += correlated_columns.size(); + } + } + VisitOperatorChildren(op); +} + +} // namespace duckdb diff --git a/src/duckdb/src/storage/buffer/block_handle.cpp b/src/duckdb/src/storage/buffer/block_handle.cpp index a9c9f9672..e462fd73e 100644 --- a/src/duckdb/src/storage/buffer/block_handle.cpp +++ b/src/duckdb/src/storage/buffer/block_handle.cpp @@ -8,18 +8,20 @@ namespace duckdb { -BlockHandle::BlockHandle(BlockManager &block_manager, block_id_t block_id_p) - : block_manager(block_manager), readers(0), block_id(block_id_p), buffer(nullptr), eviction_timestamp(0), - can_destroy(false), memory_charge(block_manager.buffer_manager.GetBufferPool()), unswizzled(nullptr) { +BlockHandle::BlockHandle(BlockManager &block_manager, block_id_t block_id_p, MemoryTag tag) + : block_manager(block_manager), readers(0), block_id(block_id_p), tag(tag), buffer(nullptr), eviction_timestamp(0), + can_destroy(false), memory_charge(tag, block_manager.buffer_manager.GetBufferPool()), unswizzled(nullptr) { eviction_timestamp = 0; state = BlockState::BLOCK_UNLOADED; memory_usage = Storage::BLOCK_ALLOC_SIZE; } -BlockHandle::BlockHandle(BlockManager &block_manager, block_id_t block_id_p, unique_ptr buffer_p, - bool can_destroy_p, idx_t block_size, BufferPoolReservation &&reservation) - : block_manager(block_manager), readers(0), block_id(block_id_p), eviction_timestamp(0), can_destroy(can_destroy_p), - memory_charge(block_manager.buffer_manager.GetBufferPool()), unswizzled(nullptr) { +BlockHandle::BlockHandle(BlockManager &block_manager, block_id_t block_id_p, MemoryTag tag, + unique_ptr buffer_p, bool can_destroy_p, idx_t block_size, + BufferPoolReservation &&reservation) + : block_manager(block_manager), readers(0), block_id(block_id_p), tag(tag), eviction_timestamp(0), + can_destroy(can_destroy_p), memory_charge(tag, block_manager.buffer_manager.GetBufferPool()), + unswizzled(nullptr) { buffer = std::move(buffer_p); state = BlockState::BLOCK_LOADED; memory_usage = block_size; @@ -78,8 +80,8 @@ BufferHandle BlockHandle::Load(shared_ptr &handle, unique_ptrcan_destroy) { return BufferHandle(); } else { - handle->buffer = - block_manager.buffer_manager.ReadTemporaryBuffer(handle->block_id, std::move(reusable_buffer)); + handle->buffer = block_manager.buffer_manager.ReadTemporaryBuffer(handle->tag, handle->block_id, + std::move(reusable_buffer)); } } handle->state = BlockState::BLOCK_LOADED; @@ -96,7 +98,7 @@ unique_ptr BlockHandle::UnloadAndTakeBlock() { if (block_id >= MAXIMUM_BLOCK && !can_destroy) { // temporary block that cannot be destroyed: write to temporary file - block_manager.buffer_manager.WriteTemporaryBuffer(block_id, *buffer); + block_manager.buffer_manager.WriteTemporaryBuffer(tag, block_id, *buffer); } memory_charge.Resize(0); state = BlockState::BLOCK_UNLOADED; diff --git a/src/duckdb/src/storage/buffer/block_manager.cpp b/src/duckdb/src/storage/buffer/block_manager.cpp index 20871d013..bc35dff61 100644 --- a/src/duckdb/src/storage/buffer/block_manager.cpp +++ b/src/duckdb/src/storage/buffer/block_manager.cpp @@ -23,7 +23,7 @@ shared_ptr BlockManager::RegisterBlock(block_id_t block_id) { } } // create a new block pointer for this block - auto result = make_shared(*this, block_id); + auto result = make_shared(*this, block_id, MemoryTag::BASE_TABLE); // register the block pointer in the set of blocks as a weak pointer blocks[block_id] = weak_ptr(result); return result; diff --git a/src/duckdb/src/storage/buffer/buffer_pool.cpp b/src/duckdb/src/storage/buffer/buffer_pool.cpp index 77d1bf8a3..6940ac2bf 100644 --- a/src/duckdb/src/storage/buffer/buffer_pool.cpp +++ b/src/duckdb/src/storage/buffer/buffer_pool.cpp @@ -37,6 +37,9 @@ shared_ptr BufferEvictionNode::TryGetBlockHandle() { BufferPool::BufferPool(idx_t maximum_memory) : current_memory(0), maximum_memory(maximum_memory), queue(make_uniq()), queue_insertions(0), temporary_memory_manager(make_uniq()) { + for (idx_t i = 0; i < MEMORY_TAG_COUNT; i++) { + memory_usage_per_tag[i] = 0; + } } BufferPool::~BufferPool() { } @@ -53,8 +56,9 @@ void BufferPool::AddToEvictionQueue(shared_ptr &handle) { queue->q.enqueue(BufferEvictionNode(weak_ptr(handle), handle->eviction_timestamp)); } -void BufferPool::IncreaseUsedMemory(idx_t size) { +void BufferPool::IncreaseUsedMemory(MemoryTag tag, idx_t size) { current_memory += size; + memory_usage_per_tag[uint8_t(tag)] += size; } idx_t BufferPool::GetUsedMemory() const { @@ -73,10 +77,10 @@ TemporaryMemoryManager &BufferPool::GetTemporaryMemoryManager() { return *temporary_memory_manager; } -BufferPool::EvictionResult BufferPool::EvictBlocks(idx_t extra_memory, idx_t memory_limit, +BufferPool::EvictionResult BufferPool::EvictBlocks(MemoryTag tag, idx_t extra_memory, idx_t memory_limit, unique_ptr *buffer) { BufferEvictionNode node; - TempBufferPoolReservation r(*this, extra_memory); + TempBufferPoolReservation r(tag, *this, extra_memory); while (current_memory > memory_limit) { // get a block to unpin from the queue if (!queue->q.try_dequeue(node)) { @@ -127,7 +131,7 @@ void BufferPool::PurgeQueue() { void BufferPool::SetLimit(idx_t limit, const char *exception_postscript) { lock_guard l_lock(limit_lock); // try to evict until the limit is reached - if (!EvictBlocks(0, limit).success) { + if (!EvictBlocks(MemoryTag::EXTENSION, 0, limit).success) { throw OutOfMemoryException( "Failed to change memory limit to %lld: could not free up enough memory for the new limit%s", limit, exception_postscript); @@ -136,7 +140,7 @@ void BufferPool::SetLimit(idx_t limit, const char *exception_postscript) { // set the global maximum memory to the new limit if successful maximum_memory = limit; // evict again - if (!EvictBlocks(0, limit).success) { + if (!EvictBlocks(MemoryTag::EXTENSION, 0, limit).success) { // failed: go back to old limit maximum_memory = old_limit; throw OutOfMemoryException( diff --git a/src/duckdb/src/storage/buffer/buffer_pool_reservation.cpp b/src/duckdb/src/storage/buffer/buffer_pool_reservation.cpp index 312d3bf14..c602af1df 100644 --- a/src/duckdb/src/storage/buffer/buffer_pool_reservation.cpp +++ b/src/duckdb/src/storage/buffer/buffer_pool_reservation.cpp @@ -3,15 +3,16 @@ namespace duckdb { -BufferPoolReservation::BufferPoolReservation(BufferPool &pool) : pool(pool) { +BufferPoolReservation::BufferPoolReservation(MemoryTag tag, BufferPool &pool) : tag(tag), pool(pool) { } -BufferPoolReservation::BufferPoolReservation(BufferPoolReservation &&src) noexcept : pool(src.pool) { +BufferPoolReservation::BufferPoolReservation(BufferPoolReservation &&src) noexcept : tag(src.tag), pool(src.pool) { size = src.size; src.size = 0; } BufferPoolReservation &BufferPoolReservation::operator=(BufferPoolReservation &&src) noexcept { + tag = src.tag; size = src.size; src.size = 0; return *this; @@ -23,7 +24,7 @@ BufferPoolReservation::~BufferPoolReservation() { void BufferPoolReservation::Resize(idx_t new_size) { int64_t delta = (int64_t)new_size - size; - pool.IncreaseUsedMemory(delta); + pool.IncreaseUsedMemory(tag, delta); size = new_size; } diff --git a/src/duckdb/src/storage/buffer_manager.cpp b/src/duckdb/src/storage/buffer_manager.cpp index c9b880860..5b0a89296 100644 --- a/src/duckdb/src/storage/buffer_manager.cpp +++ b/src/duckdb/src/storage/buffer_manager.cpp @@ -75,11 +75,11 @@ void BufferManager::AddToEvictionQueue(shared_ptr &handle) { throw NotImplementedException("This type of BufferManager does not support 'AddToEvictionQueue"); } -void BufferManager::WriteTemporaryBuffer(block_id_t block_id, FileBuffer &buffer) { +void BufferManager::WriteTemporaryBuffer(MemoryTag tag, block_id_t block_id, FileBuffer &buffer) { throw NotImplementedException("This type of BufferManager does not support 'WriteTemporaryBuffer"); } -unique_ptr BufferManager::ReadTemporaryBuffer(block_id_t id, unique_ptr buffer) { +unique_ptr BufferManager::ReadTemporaryBuffer(MemoryTag tag, block_id_t id, unique_ptr buffer) { throw NotImplementedException("This type of BufferManager does not support 'ReadTemporaryBuffer"); } diff --git a/src/duckdb/src/storage/checkpoint/write_overflow_strings_to_disk.cpp b/src/duckdb/src/storage/checkpoint/write_overflow_strings_to_disk.cpp index 3c0f08df5..665168ef9 100644 --- a/src/duckdb/src/storage/checkpoint/write_overflow_strings_to_disk.cpp +++ b/src/duckdb/src/storage/checkpoint/write_overflow_strings_to_disk.cpp @@ -40,7 +40,7 @@ void WriteOverflowStringsToDisk::WriteString(UncompressedStringSegmentState &sta block_id_t &result_block, int32_t &result_offset) { auto &buffer_manager = block_manager.buffer_manager; if (!handle.IsValid()) { - handle = buffer_manager.Allocate(Storage::BLOCK_SIZE); + handle = buffer_manager.Allocate(MemoryTag::OVERFLOW_STRINGS, Storage::BLOCK_SIZE); } // first write the length of the string if (block_id == INVALID_BLOCK || offset + 2 * sizeof(uint32_t) >= STRING_SPACE) { diff --git a/src/duckdb/src/storage/compression/string_uncompressed.cpp b/src/duckdb/src/storage/compression/string_uncompressed.cpp index fd8dfce01..7d0a594c7 100644 --- a/src/duckdb/src/storage/compression/string_uncompressed.cpp +++ b/src/duckdb/src/storage/compression/string_uncompressed.cpp @@ -298,7 +298,7 @@ void UncompressedStringStorage::WriteStringMemory(ColumnSegment &segment, string new_block->offset = 0; new_block->size = alloc_size; // allocate an in-memory buffer for it - handle = buffer_manager.Allocate(alloc_size, false, &block); + handle = buffer_manager.Allocate(MemoryTag::OVERFLOW_STRINGS, alloc_size, false, &block); state.overflow_blocks.insert(make_pair(block->BlockId(), reference(*new_block))); new_block->block = std::move(block); new_block->next = std::move(state.head); @@ -342,7 +342,7 @@ string_t UncompressedStringStorage::ReadOverflowString(ColumnSegment &segment, V auto alloc_size = MaxValue(Storage::BLOCK_SIZE, length); // allocate a buffer to store the compressed string // TODO: profile this to check if we need to reuse buffer - auto target_handle = buffer_manager.Allocate(alloc_size); + auto target_handle = buffer_manager.Allocate(MemoryTag::OVERFLOW_STRINGS, alloc_size); auto target_ptr = target_handle.Ptr(); // now append the string to the single buffer diff --git a/src/duckdb/src/storage/compression/validity_uncompressed.cpp b/src/duckdb/src/storage/compression/validity_uncompressed.cpp index 7dfa6bd1a..3df0cae80 100644 --- a/src/duckdb/src/storage/compression/validity_uncompressed.cpp +++ b/src/duckdb/src/storage/compression/validity_uncompressed.cpp @@ -242,7 +242,7 @@ void ValidityScanPartial(ColumnSegment &segment, ColumnScanState &state, idx_t s for (idx_t i = 0; i < scan_count; i++) { if (!source_mask.RowIsValid(start + i)) { if (result_mask.AllValid()) { - result_mask.Initialize(); + result_mask.Initialize(result_mask.TargetCount()); } result_mask.SetInvalid(result_offset + i); } @@ -323,7 +323,7 @@ void ValidityScanPartial(ColumnSegment &segment, ColumnScanState &state, idx_t s // now finally we can merge the input mask with the result mask if (input_mask != ValidityMask::ValidityBuffer::MAX_ENTRY) { if (!result_data) { - result_mask.Initialize(); + result_mask.Initialize(result_mask.TargetCount()); result_data = (validity_t *)result_mask.GetData(); } result_data[current_result_idx] &= input_mask; @@ -363,7 +363,7 @@ void ValidityScan(ColumnSegment &segment, ColumnScanState &state, idx_t scan_cou continue; } if (!result_data) { - result_mask.Initialize(); + result_mask.Initialize(result_mask.TargetCount()); result_data = result_mask.GetData(); } result_data[i] = input_entry; diff --git a/src/duckdb/src/storage/metadata/metadata_manager.cpp b/src/duckdb/src/storage/metadata/metadata_manager.cpp index 0bd58d68e..808ede828 100644 --- a/src/duckdb/src/storage/metadata/metadata_manager.cpp +++ b/src/duckdb/src/storage/metadata/metadata_manager.cpp @@ -67,7 +67,7 @@ void MetadataManager::ConvertToTransient(MetadataBlock &block) { // allocate a new transient block to replace it shared_ptr new_block; - auto new_buffer = buffer_manager.Allocate(Storage::BLOCK_SIZE, false, &new_block); + auto new_buffer = buffer_manager.Allocate(MemoryTag::METADATA, Storage::BLOCK_SIZE, false, &new_block); // copy the data to the transient block memcpy(new_buffer.Ptr(), old_buffer.Ptr(), Storage::BLOCK_SIZE); @@ -82,7 +82,7 @@ block_id_t MetadataManager::AllocateNewBlock() { auto new_block_id = GetNextBlockId(); MetadataBlock new_block; - auto handle = buffer_manager.Allocate(Storage::BLOCK_SIZE, false, &new_block.block); + auto handle = buffer_manager.Allocate(MemoryTag::METADATA, Storage::BLOCK_SIZE, false, &new_block.block); new_block.block_id = new_block_id; for (idx_t i = 0; i < METADATA_BLOCK_COUNT; i++) { new_block.free_blocks.push_back(METADATA_BLOCK_COUNT - i - 1); diff --git a/src/duckdb/src/storage/serialization/serialize_create_info.cpp b/src/duckdb/src/storage/serialization/serialize_create_info.cpp index 3631e3197..8d667acf6 100644 --- a/src/duckdb/src/storage/serialization/serialize_create_info.cpp +++ b/src/duckdb/src/storage/serialization/serialize_create_info.cpp @@ -187,6 +187,7 @@ void CreateViewInfo::Serialize(Serializer &serializer) const { serializer.WritePropertyWithDefault>(201, "aliases", aliases); serializer.WritePropertyWithDefault>(202, "types", types); serializer.WritePropertyWithDefault>(203, "query", query); + serializer.WritePropertyWithDefault>(204, "names", names); } unique_ptr CreateViewInfo::Deserialize(Deserializer &deserializer) { @@ -195,6 +196,7 @@ unique_ptr CreateViewInfo::Deserialize(Deserializer &deserializer) { deserializer.ReadPropertyWithDefault>(201, "aliases", result->aliases); deserializer.ReadPropertyWithDefault>(202, "types", result->types); deserializer.ReadPropertyWithDefault>(203, "query", result->query); + deserializer.ReadPropertyWithDefault>(204, "names", result->names); return std::move(result); } diff --git a/src/duckdb/src/storage/standard_buffer_manager.cpp b/src/duckdb/src/storage/standard_buffer_manager.cpp index df8c37d73..b3c932057 100644 --- a/src/duckdb/src/storage/standard_buffer_manager.cpp +++ b/src/duckdb/src/storage/standard_buffer_manager.cpp @@ -62,6 +62,9 @@ StandardBufferManager::StandardBufferManager(DatabaseInstance &db, string tmp) temporary_id(MAXIMUM_BLOCK), buffer_allocator(BufferAllocatorAllocate, BufferAllocatorFree, BufferAllocatorRealloc, make_uniq(*this)) { temp_block_manager = make_uniq(*this); + for (idx_t i = 0; i < MEMORY_TAG_COUNT; i++) { + evicted_data_per_tag[i] = 0; + } } StandardBufferManager::~StandardBufferManager() { @@ -83,9 +86,9 @@ idx_t StandardBufferManager::GetMaxMemory() const { } template -TempBufferPoolReservation StandardBufferManager::EvictBlocksOrThrow(idx_t memory_delta, unique_ptr *buffer, - ARGS... args) { - auto r = buffer_pool.EvictBlocks(memory_delta, buffer_pool.maximum_memory, buffer); +TempBufferPoolReservation StandardBufferManager::EvictBlocksOrThrow(MemoryTag tag, idx_t memory_delta, + unique_ptr *buffer, ARGS... args) { + auto r = buffer_pool.EvictBlocks(tag, memory_delta, buffer_pool.maximum_memory, buffer); if (!r.success) { string extra_text = StringUtil::Format(" (%s/%s used)", StringUtil::BytesToHumanReadableString(GetUsedMemory()), StringUtil::BytesToHumanReadableString(GetMaxMemory())); @@ -97,35 +100,36 @@ TempBufferPoolReservation StandardBufferManager::EvictBlocksOrThrow(idx_t memory shared_ptr StandardBufferManager::RegisterSmallMemory(idx_t block_size) { D_ASSERT(block_size < Storage::BLOCK_SIZE); - auto res = EvictBlocksOrThrow(block_size, nullptr, "could not allocate block of size %s%s", + auto res = EvictBlocksOrThrow(MemoryTag::BASE_TABLE, block_size, nullptr, "could not allocate block of size %s%s", StringUtil::BytesToHumanReadableString(block_size)); auto buffer = ConstructManagedBuffer(block_size, nullptr, FileBufferType::TINY_BUFFER); // create a new block pointer for this block - return make_shared(*temp_block_manager, ++temporary_id, std::move(buffer), false, block_size, - std::move(res)); + return make_shared(*temp_block_manager, ++temporary_id, MemoryTag::BASE_TABLE, std::move(buffer), + false, block_size, std::move(res)); } -shared_ptr StandardBufferManager::RegisterMemory(idx_t block_size, bool can_destroy) { +shared_ptr StandardBufferManager::RegisterMemory(MemoryTag tag, idx_t block_size, bool can_destroy) { D_ASSERT(block_size >= Storage::BLOCK_SIZE); auto alloc_size = GetAllocSize(block_size); // first evict blocks until we have enough memory to store this buffer unique_ptr reusable_buffer; - auto res = EvictBlocksOrThrow(alloc_size, &reusable_buffer, "could not allocate block of size %s%s", + auto res = EvictBlocksOrThrow(tag, alloc_size, &reusable_buffer, "could not allocate block of size %s%s", StringUtil::BytesToHumanReadableString(alloc_size)); auto buffer = ConstructManagedBuffer(block_size, std::move(reusable_buffer)); // create a new block pointer for this block - return make_shared(*temp_block_manager, ++temporary_id, std::move(buffer), can_destroy, alloc_size, - std::move(res)); + return make_shared(*temp_block_manager, ++temporary_id, tag, std::move(buffer), can_destroy, + alloc_size, std::move(res)); } -BufferHandle StandardBufferManager::Allocate(idx_t block_size, bool can_destroy, shared_ptr *block) { +BufferHandle StandardBufferManager::Allocate(MemoryTag tag, idx_t block_size, bool can_destroy, + shared_ptr *block) { shared_ptr local_block; auto block_ptr = block ? block : &local_block; - *block_ptr = RegisterMemory(block_size, can_destroy); + *block_ptr = RegisterMemory(tag, block_size, can_destroy); return Pin(*block_ptr); } @@ -143,9 +147,10 @@ void StandardBufferManager::ReAllocate(shared_ptr &handle, idx_t bl return; } else if (memory_delta > 0) { // evict blocks until we have space to resize this block - auto reservation = EvictBlocksOrThrow(memory_delta, nullptr, "failed to resize block from %s to %s%s", - StringUtil::BytesToHumanReadableString(handle->memory_usage), - StringUtil::BytesToHumanReadableString(req.alloc_size)); + auto reservation = + EvictBlocksOrThrow(handle->tag, memory_delta, nullptr, "failed to resize block from %s to %s%s", + StringUtil::BytesToHumanReadableString(handle->memory_usage), + StringUtil::BytesToHumanReadableString(req.alloc_size)); // EvictBlocks decrements 'current_memory' for us. handle->memory_charge.Merge(std::move(reservation)); } else { @@ -171,8 +176,9 @@ BufferHandle StandardBufferManager::Pin(shared_ptr &handle) { } // evict blocks until we have space for the current block unique_ptr reusable_buffer; - auto reservation = EvictBlocksOrThrow(required_memory, &reusable_buffer, "failed to pin block of size %s%s", - StringUtil::BytesToHumanReadableString(required_memory)); + auto reservation = + EvictBlocksOrThrow(handle->tag, required_memory, &reusable_buffer, "failed to pin block of size %s%s", + StringUtil::BytesToHumanReadableString(required_memory)); // lock the handle again and repeat the check (in case anybody loaded in the mean time) lock_guard lock(handle->lock); // check if the block is already loaded @@ -233,6 +239,18 @@ void StandardBufferManager::SetLimit(idx_t limit) { buffer_pool.SetLimit(limit, InMemoryWarning()); } +vector StandardBufferManager::GetMemoryUsageInfo() const { + vector result; + for (idx_t k = 0; k < MEMORY_TAG_COUNT; k++) { + MemoryInformation info; + info.tag = MemoryTag(k); + info.size = buffer_pool.memory_usage_per_tag[k].load(); + info.evicted_data = evicted_data_per_tag[k].load(); + result.push_back(info); + } + return result; +} + //===--------------------------------------------------------------------===// // Temporary File Management //===--------------------------------------------------------------------===// @@ -624,12 +642,14 @@ void StandardBufferManager::RequireTemporaryDirectory() { } } -void StandardBufferManager::WriteTemporaryBuffer(block_id_t block_id, FileBuffer &buffer) { +void StandardBufferManager::WriteTemporaryBuffer(MemoryTag tag, block_id_t block_id, FileBuffer &buffer) { RequireTemporaryDirectory(); if (buffer.size == Storage::BLOCK_SIZE) { + evicted_data_per_tag[uint8_t(tag)] += Storage::BLOCK_SIZE; temp_directory_handle->GetTempFile().WriteTemporaryBuffer(block_id, buffer); return; } + evicted_data_per_tag[uint8_t(tag)] += buffer.size; // get the path to write to auto path = GetTemporaryPath(block_id); D_ASSERT(buffer.size > Storage::BLOCK_SIZE); @@ -640,11 +660,12 @@ void StandardBufferManager::WriteTemporaryBuffer(block_id_t block_id, FileBuffer buffer.Write(*handle, sizeof(idx_t)); } -unique_ptr StandardBufferManager::ReadTemporaryBuffer(block_id_t id, +unique_ptr StandardBufferManager::ReadTemporaryBuffer(MemoryTag tag, block_id_t id, unique_ptr reusable_buffer) { D_ASSERT(!temp_directory.empty()); D_ASSERT(temp_directory_handle.get()); if (temp_directory_handle->GetTempFile().HasTemporaryBuffer(id)) { + evicted_data_per_tag[uint8_t(tag)] -= Storage::BLOCK_SIZE; return temp_directory_handle->GetTempFile().ReadTemporaryBuffer(id, std::move(reusable_buffer)); } idx_t block_size; @@ -653,6 +674,7 @@ unique_ptr StandardBufferManager::ReadTemporaryBuffer(block_id_t id, auto &fs = FileSystem::GetFileSystem(db); auto handle = fs.OpenFile(path, FileFlags::FILE_FLAGS_READ); handle->Read(&block_size, sizeof(idx_t), 0); + evicted_data_per_tag[uint8_t(tag)] -= block_size; // now allocate a buffer of this size and read the data into that buffer auto buffer = @@ -727,15 +749,16 @@ const char *StandardBufferManager::InMemoryWarning() { return "\nDatabase is launched in in-memory mode and no temporary directory is specified." "\nUnused blocks cannot be offloaded to disk." "\n\nLaunch the database with a persistent storage back-end" - "\nOr set PRAGMA temp_directory='/path/to/tmp.tmp'"; + "\nOr set SET temp_directory='/path/to/tmp.tmp'"; } void StandardBufferManager::ReserveMemory(idx_t size) { if (size == 0) { return; } - auto reservation = EvictBlocksOrThrow(size, nullptr, "failed to reserve memory data of size %s%s", - StringUtil::BytesToHumanReadableString(size)); + auto reservation = + EvictBlocksOrThrow(MemoryTag::EXTENSION, size, nullptr, "failed to reserve memory data of size %s%s", + StringUtil::BytesToHumanReadableString(size)); reservation.size = 0; } @@ -751,8 +774,9 @@ void StandardBufferManager::FreeReservedMemory(idx_t size) { //===--------------------------------------------------------------------===// data_ptr_t StandardBufferManager::BufferAllocatorAllocate(PrivateAllocatorData *private_data, idx_t size) { auto &data = private_data->Cast(); - auto reservation = data.manager.EvictBlocksOrThrow(size, nullptr, "failed to allocate data of size %s%s", - StringUtil::BytesToHumanReadableString(size)); + auto reservation = + data.manager.EvictBlocksOrThrow(MemoryTag::ALLOCATOR, size, nullptr, "failed to allocate data of size %s%s", + StringUtil::BytesToHumanReadableString(size)); // We rely on manual tracking of this one. :( reservation.size = 0; return Allocator::Get(data.manager.db).AllocateData(size); @@ -760,7 +784,7 @@ data_ptr_t StandardBufferManager::BufferAllocatorAllocate(PrivateAllocatorData * void StandardBufferManager::BufferAllocatorFree(PrivateAllocatorData *private_data, data_ptr_t pointer, idx_t size) { auto &data = private_data->Cast(); - BufferPoolReservation r(data.manager.GetBufferPool()); + BufferPoolReservation r(MemoryTag::ALLOCATOR, data.manager.GetBufferPool()); r.size = size; r.Resize(0); return Allocator::Get(data.manager.db).FreeData(pointer, size); @@ -772,7 +796,7 @@ data_ptr_t StandardBufferManager::BufferAllocatorRealloc(PrivateAllocatorData *p return pointer; } auto &data = private_data->Cast(); - BufferPoolReservation r(data.manager.GetBufferPool()); + BufferPoolReservation r(MemoryTag::ALLOCATOR, data.manager.GetBufferPool()); r.size = old_size; r.Resize(size); r.size = 0; diff --git a/src/duckdb/src/storage/table/column_segment.cpp b/src/duckdb/src/storage/table/column_segment.cpp index 5a77c881c..c0f67a0ca 100644 --- a/src/duckdb/src/storage/table/column_segment.cpp +++ b/src/duckdb/src/storage/table/column_segment.cpp @@ -58,7 +58,7 @@ unique_ptr ColumnSegment::CreateTransientSegment(DatabaseInstance if (segment_size < Storage::BLOCK_SIZE) { block = buffer_manager.RegisterSmallMemory(segment_size); } else { - buffer_manager.Allocate(segment_size, false, &block); + buffer_manager.Allocate(MemoryTag::IN_MEMORY_TABLE, segment_size, false, &block); } return make_uniq(db, std::move(block), type, ColumnSegmentType::TRANSIENT, start, 0, *function, BaseStatistics::CreateEmpty(type), INVALID_BLOCK, 0, segment_size); @@ -150,7 +150,7 @@ void ColumnSegment::Resize(idx_t new_size) { auto &buffer_manager = BufferManager::GetBufferManager(db); auto old_handle = buffer_manager.Pin(block); shared_ptr new_block; - auto new_handle = buffer_manager.Allocate(new_size, false, &new_block); + auto new_handle = buffer_manager.Allocate(MemoryTag::IN_MEMORY_TABLE, new_size, false, &new_block); memcpy(new_handle.Ptr(), old_handle.Ptr(), segment_size); this->block_id = new_block->BlockId(); diff --git a/src/duckdb/src/storage/table/row_group_collection.cpp b/src/duckdb/src/storage/table/row_group_collection.cpp index a8fc2e81b..e730fbb22 100644 --- a/src/duckdb/src/storage/table/row_group_collection.cpp +++ b/src/duckdb/src/storage/table/row_group_collection.cpp @@ -642,6 +642,27 @@ struct CollectionCheckpointState { } return false; } + void CancelTasks() { + // This should only be called after an error has occurred, no other mechanism to cancel checkpoint tasks exists + // currently + D_ASSERT(error_manager.HasError()); + // Give every pending task the chance to cancel + WorkOnTasks(); + // Wait for all active tasks to realize they have been canceled + while (completed_tasks != total_tasks) { + } + } + + void WorkOnTasks() { + shared_ptr task_from_producer; + while (scheduler.GetTaskFromProducer(*token, task_from_producer)) { + auto res = task_from_producer->Execute(TaskExecutionMode::PROCESS_ALL); + (void)res; + D_ASSERT(res != TaskExecutionResult::TASK_BLOCKED); + task_from_producer.reset(); + } + } + bool GetTask(shared_ptr &task) { return scheduler.GetTaskFromProducer(*token, task); } @@ -660,7 +681,10 @@ class BaseCheckpointTask : public Task { virtual void ExecuteTask() = 0; TaskExecutionResult Execute(TaskExecutionMode mode) override { + (void)mode; + D_ASSERT(mode == TaskExecutionMode::PROCESS_ALL); if (checkpoint_state.HasError()) { + checkpoint_state.FinishTask(); return TaskExecutionResult::TASK_FINISHED; } try { @@ -672,6 +696,7 @@ class BaseCheckpointTask : public Task { } catch (...) { // LCOV_EXCL_START checkpoint_state.PushError(ErrorData("Unknown exception during Checkpoint!")); } // LCOV_EXCL_STOP + checkpoint_state.FinishTask(); return TaskExecutionResult::TASK_ERROR; } @@ -775,7 +800,9 @@ class VacuumTask : public BaseCheckpointTask { append_count); append_counts[current_append_idx] += append_count; remaining -= append_count; - if (remaining > 0) { + const bool row_group_full = append_counts[current_append_idx] == Storage::ROW_GROUP_SIZE; + const bool last_row_group = current_append_idx + 1 >= new_row_groups.size(); + if (remaining > 0 || (row_group_full && !last_row_group)) { // move to the next row group current_append_idx++; new_row_groups[current_append_idx]->InitializeAppend(append_state.row_group_append_state); @@ -942,8 +969,10 @@ void RowGroupCollection::Checkpoint(TableDataWriter &writer, TableStatistics &gl // check if we ran into any errors while checkpointing if (checkpoint_state.HasError()) { // throw the error + checkpoint_state.CancelTasks(); checkpoint_state.ThrowError(); } + // no errors - finalize the row groups idx_t new_total_rows = 0; for (idx_t segment_idx = 0; segment_idx < segments.size(); segment_idx++) { diff --git a/src/duckdb/src/storage/table_index_list.cpp b/src/duckdb/src/storage/table_index_list.cpp index 0f5df1f0c..ef14073f3 100644 --- a/src/duckdb/src/storage/table_index_list.cpp +++ b/src/duckdb/src/storage/table_index_list.cpp @@ -75,9 +75,11 @@ void TableIndexList::InitializeIndexes(ClientContext &context, DataTableInfo &ta auto &create_info = unknown_index.GetCreateInfo(); auto &storage_info = unknown_index.GetStorageInfo(); - auto index_instance = index_type->create_instance(create_info.index_name, create_info.constraint_type, - create_info.column_ids, unknown_index.unbound_expressions, - *table_info.table_io_manager, table_info.db, storage_info); + CreateIndexInput input(*table_info.table_io_manager, table_info.db, create_info.constraint_type, + create_info.index_name, create_info.column_ids, unknown_index.unbound_expressions, + storage_info, create_info.options); + + auto index_instance = index_type->create_instance(input); index = std::move(index_instance); } diff --git a/src/duckdb/src/storage/temporary_memory_manager.cpp b/src/duckdb/src/storage/temporary_memory_manager.cpp index 227794505..a6d07b9bf 100644 --- a/src/duckdb/src/storage/temporary_memory_manager.cpp +++ b/src/duckdb/src/storage/temporary_memory_manager.cpp @@ -48,6 +48,7 @@ void TemporaryMemoryManager::UpdateConfiguration(ClientContext &context) { memory_limit = MAXIMUM_MEMORY_LIMIT_RATIO * double(buffer_manager.GetMaxMemory()); has_temporary_directory = buffer_manager.HasTemporaryDirectory(); num_threads = task_scheduler.NumberOfThreads(); + query_max_memory = buffer_manager.GetQueryMaxMemory(); } TemporaryMemoryManager &TemporaryMemoryManager::Get(ClientContext &context) { @@ -87,10 +88,11 @@ void TemporaryMemoryManager::UpdateState(ClientContext &context, TemporaryMemory // The upper bound for the reservation of this state is the minimum of: // 1. Remaining size of the state - // 2. MAXIMUM_FREE_MEMORY_RATIO * free memory + // 2. The max memory per query + // 3. MAXIMUM_FREE_MEMORY_RATIO * free memory + auto upper_bound = MinValue(temporary_memory_state.remaining_size, query_max_memory); auto free_memory = memory_limit - (reservation - temporary_memory_state.reservation); - auto upper_bound = - MinValue(temporary_memory_state.remaining_size, MAXIMUM_FREE_MEMORY_RATIO * free_memory); + upper_bound = MinValue(upper_bound, MAXIMUM_FREE_MEMORY_RATIO * free_memory); if (remaining_size > memory_limit) { // We're processing more data than fits in memory, so we must further limit memory usage. diff --git a/src/duckdb/src/storage/wal_replay.cpp b/src/duckdb/src/storage/wal_replay.cpp index 0ffd1502c..e66f60603 100644 --- a/src/duckdb/src/storage/wal_replay.cpp +++ b/src/duckdb/src/storage/wal_replay.cpp @@ -548,7 +548,7 @@ void WriteAheadLogDeserializer::ReplayCreateIndex() { // read the data into a buffer handle shared_ptr block_handle; - buffer_manager.Allocate(Storage::BLOCK_SIZE, false, &block_handle); + buffer_manager.Allocate(MemoryTag::ART_INDEX, Storage::BLOCK_SIZE, false, &block_handle); auto buffer_handle = buffer_manager.Pin(block_handle); auto data_ptr = buffer_handle.Ptr(); @@ -615,9 +615,11 @@ void WriteAheadLogDeserializer::ReplayCreateIndex() { } auto &data_table = table.GetStorage(); - auto index_instance = - index_type->create_instance(info.index_name, info.constraint_type, info.column_ids, unbound_expressions, - TableIOManager::Get(data_table), data_table.db, index_info); + + CreateIndexInput input(TableIOManager::Get(data_table), data_table.db, info.constraint_type, info.index_name, + info.column_ids, unbound_expressions, index_info, info.options); + + auto index_instance = index_type->create_instance(input); data_table.info->indexes.AddIndex(std::move(index_instance)); } diff --git a/src/duckdb/src/transaction/commit_state.cpp b/src/duckdb/src/transaction/commit_state.cpp index 58d00267f..37756f1a9 100644 --- a/src/duckdb/src/transaction/commit_state.cpp +++ b/src/duckdb/src/transaction/commit_state.cpp @@ -45,8 +45,14 @@ void CommitState::WriteCatalogEntry(CatalogEntry &entry, data_ptr_t dataptr) { switch (parent.type) { case CatalogType::TABLE_ENTRY: - if (entry.type == CatalogType::RENAMED_ENTRY || entry.type == CatalogType::TABLE_ENTRY) { - // ALTER TABLE statement, read the extra data after the entry + case CatalogType::VIEW_ENTRY: + case CatalogType::INDEX_ENTRY: + case CatalogType::SEQUENCE_ENTRY: + case CatalogType::TYPE_ENTRY: + case CatalogType::MACRO_ENTRY: + case CatalogType::TABLE_MACRO_ENTRY: + if (entry.type == CatalogType::RENAMED_ENTRY || entry.type == parent.type) { + // ALTER statement, read the extra data after the entry auto extra_data_size = Load(dataptr); auto extra_data = data_ptr_cast(dataptr + sizeof(idx_t)); @@ -57,18 +63,61 @@ void CommitState::WriteCatalogEntry(CatalogEntry &entry, data_ptr_t dataptr) { auto parse_info = deserializer.ReadProperty>(101, "alter_info"); deserializer.End(); - if (!column_name.empty()) { - D_ASSERT(entry.type != CatalogType::RENAMED_ENTRY); - auto &table_entry = entry.Cast(); - D_ASSERT(table_entry.IsDuckTable()); - // write the alter table in the log - table_entry.CommitAlter(column_name); + switch (parent.type) { + case CatalogType::TABLE_ENTRY: + if (!column_name.empty()) { + D_ASSERT(entry.type != CatalogType::RENAMED_ENTRY); + auto &table_entry = entry.Cast(); + D_ASSERT(table_entry.IsDuckTable()); + // write the alter table in the log + table_entry.CommitAlter(column_name); + } + break; + case CatalogType::VIEW_ENTRY: + case CatalogType::INDEX_ENTRY: + case CatalogType::SEQUENCE_ENTRY: + case CatalogType::TYPE_ENTRY: + case CatalogType::MACRO_ENTRY: + case CatalogType::TABLE_MACRO_ENTRY: + (void)column_name; + break; + default: + throw InternalException("Don't know how to drop this type!"); } + auto &alter_info = parse_info->Cast(); log->WriteAlter(alter_info); } else { - // CREATE TABLE statement - log->WriteCreateTable(parent.Cast()); + switch (parent.type) { + case CatalogType::TABLE_ENTRY: + // CREATE TABLE statement + log->WriteCreateTable(parent.Cast()); + break; + case CatalogType::VIEW_ENTRY: + // CREATE VIEW statement + log->WriteCreateView(parent.Cast()); + break; + case CatalogType::INDEX_ENTRY: + // CREATE INDEX statement + log->WriteCreateIndex(parent.Cast()); + break; + case CatalogType::SEQUENCE_ENTRY: + // CREATE SEQUENCE statement + log->WriteCreateSequence(parent.Cast()); + break; + case CatalogType::TYPE_ENTRY: + // CREATE TYPE statement + log->WriteCreateType(parent.Cast()); + break; + case CatalogType::MACRO_ENTRY: + log->WriteCreateMacro(parent.Cast()); + break; + case CatalogType::TABLE_MACRO_ENTRY: + log->WriteCreateTableMacro(parent.Cast()); + break; + default: + throw InternalException("Don't know how to drop this type!"); + } } break; case CatalogType::SCHEMA_ENTRY: @@ -78,43 +127,6 @@ void CommitState::WriteCatalogEntry(CatalogEntry &entry, data_ptr_t dataptr) { } log->WriteCreateSchema(parent.Cast()); break; - case CatalogType::VIEW_ENTRY: - if (entry.type == CatalogType::RENAMED_ENTRY || entry.type == CatalogType::VIEW_ENTRY) { - // ALTER TABLE statement, read the extra data after the entry - auto extra_data_size = Load(dataptr); - auto extra_data = data_ptr_cast(dataptr + sizeof(idx_t)); - // deserialize it - MemoryStream source(extra_data, extra_data_size); - BinaryDeserializer deserializer(source); - deserializer.Begin(); - auto column_name = deserializer.ReadProperty(100, "column_name"); - auto parse_info = deserializer.ReadProperty>(101, "alter_info"); - deserializer.End(); - - (void)column_name; - - // write the alter table in the log - auto &alter_info = parse_info->Cast(); - log->WriteAlter(alter_info); - } else { - log->WriteCreateView(parent.Cast()); - } - break; - case CatalogType::SEQUENCE_ENTRY: - log->WriteCreateSequence(parent.Cast()); - break; - case CatalogType::MACRO_ENTRY: - log->WriteCreateMacro(parent.Cast()); - break; - case CatalogType::TABLE_MACRO_ENTRY: - log->WriteCreateTableMacro(parent.Cast()); - break; - case CatalogType::INDEX_ENTRY: - log->WriteCreateIndex(parent.Cast()); - break; - case CatalogType::TYPE_ENTRY: - log->WriteCreateType(parent.Cast()); - break; case CatalogType::RENAMED_ENTRY: // This is a rename, nothing needs to be done for this break; diff --git a/src/duckdb/src/transaction/transaction_context.cpp b/src/duckdb/src/transaction/transaction_context.cpp index 22c8b85ba..f67b89a02 100644 --- a/src/duckdb/src/transaction/transaction_context.cpp +++ b/src/duckdb/src/transaction/transaction_context.cpp @@ -5,6 +5,8 @@ #include "duckdb/transaction/transaction_manager.hpp" #include "duckdb/main/config.hpp" #include "duckdb/main/database_manager.hpp" +#include "duckdb/main/client_context.hpp" +#include "duckdb/main/client_context_state.hpp" namespace duckdb { @@ -29,6 +31,11 @@ void TransactionContext::BeginTransaction() { auto catalog_version = Catalog::GetSystemCatalog(context).GetCatalogVersion(); current_transaction = make_uniq(context, start_timestamp, catalog_version); + // Notify any registered state of transaction begin + for (auto const &s : context.registered_state) { + s.second->TransactionBegin(*current_transaction, context); + } + auto &config = DBConfig::GetConfig(context); if (config.options.immediate_transaction_mode) { // if immediate transaction mode is enabled then start all transactions immediately @@ -46,8 +53,16 @@ void TransactionContext::Commit() { auto transaction = std::move(current_transaction); ClearTransaction(); auto error = transaction->Commit(); + // Notify any registered state of transaction commit if (error.HasError()) { + for (auto const &s : context.registered_state) { + s.second->TransactionRollback(*transaction, context); + } throw TransactionException("Failed to commit: %s", error.RawMessage()); + } else { + for (auto const &s : context.registered_state) { + s.second->TransactionCommit(*transaction, context); + } } } @@ -65,6 +80,10 @@ void TransactionContext::Rollback() { auto transaction = std::move(current_transaction); ClearTransaction(); transaction->Rollback(); + // Notify any registered state of transaction rollback + for (auto const &s : context.registered_state) { + s.second->TransactionRollback(*transaction, context); + } } void TransactionContext::ClearTransaction() { diff --git a/src/duckdb/third_party/fast_float/fast_float/fast_float.h b/src/duckdb/third_party/fast_float/fast_float/fast_float.h index 34e627b21..b5e048289 100644 --- a/src/duckdb/third_party/fast_float/fast_float/fast_float.h +++ b/src/duckdb/third_party/fast_float/fast_float/fast_float.h @@ -242,7 +242,7 @@ fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd, fastfloat_really_inline value128 full_multiplication(uint64_t a, uint64_t b) { value128 answer; -#ifdef _M_ARM64 +#if defined(FASTFLOAT_VISUAL_STUDIO) && defined(_M_ARM64) // ARM64 has native support for 64-bit multiplications, no need to emulate answer.high = __umulh(a, b); answer.low = a * b; diff --git a/src/duckdb/ub_src_function_table.cpp b/src/duckdb/ub_src_function_table.cpp index 0250ec7e1..3f2d82c8c 100644 --- a/src/duckdb/ub_src_function_table.cpp +++ b/src/duckdb/ub_src_function_table.cpp @@ -26,9 +26,5 @@ #include "src/function/table/table_scan.cpp" -#include "src/function/table/pragma_last_profiling_output.cpp" - -#include "src/function/table/pragma_detailed_profiling_output.cpp" - #include "src/function/table/unnest.cpp" diff --git a/src/duckdb/ub_src_function_table_system.cpp b/src/duckdb/ub_src_function_table_system.cpp index 05edcd725..a16a9d1a0 100644 --- a/src/duckdb/ub_src_function_table_system.cpp +++ b/src/duckdb/ub_src_function_table_system.cpp @@ -14,6 +14,8 @@ #include "src/function/table/system/duckdb_indexes.cpp" +#include "src/function/table/system/duckdb_memory.cpp" + #include "src/function/table/system/duckdb_optimizers.cpp" #include "src/function/table/system/duckdb_schemas.cpp" diff --git a/src/duckdb/ub_src_planner_subquery.cpp b/src/duckdb/ub_src_planner_subquery.cpp index 6db15ff2d..d3713024f 100644 --- a/src/duckdb/ub_src_planner_subquery.cpp +++ b/src/duckdb/ub_src_planner_subquery.cpp @@ -4,3 +4,5 @@ #include "src/planner/subquery/rewrite_correlated_expressions.cpp" +#include "src/planner/subquery/rewrite_cte_scan.cpp" + From 8735f7c8876a79340b0180d2e93ad212c75787de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Fri, 23 Feb 2024 10:34:48 +0100 Subject: [PATCH 5/5] Pretend we're on the old version --- src/duckdb/src/function/table/version/pragma_version.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/duckdb/src/function/table/version/pragma_version.cpp b/src/duckdb/src/function/table/version/pragma_version.cpp index 654dbaa4c..cb9e238c1 100644 --- a/src/duckdb/src/function/table/version/pragma_version.cpp +++ b/src/duckdb/src/function/table/version/pragma_version.cpp @@ -1,8 +1,8 @@ #ifndef DUCKDB_VERSION -#define DUCKDB_VERSION "v0.10.1-dev2" +#define DUCKDB_VERSION "v0.10.0" #endif #ifndef DUCKDB_SOURCE_ID -#define DUCKDB_SOURCE_ID "583508495b" +#define DUCKDB_SOURCE_ID "20b1486d11" #endif #include "duckdb/function/table/system_functions.hpp" #include "duckdb/main/database.hpp"