Skip to content

Commit

Permalink
Update vendored DuckDB sources to 3ca8544
Browse files Browse the repository at this point in the history
  • Loading branch information
duckdblabs-bot committed Nov 9, 2024
1 parent 3ca8544 commit 7bad582
Show file tree
Hide file tree
Showing 65 changed files with 854 additions and 290 deletions.
6 changes: 3 additions & 3 deletions src/duckdb/src/catalog/catalog_entry/duck_table_entry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -671,12 +671,12 @@ unique_ptr<CatalogEntry> DuckTableEntry::ChangeColumnType(ClientContext &context

auto bound_create_info = binder->BindCreateTableInfo(std::move(create_info), schema);

vector<column_t> storage_oids;
vector<StorageIndex> storage_oids;
for (idx_t i = 0; i < bound_columns.size(); i++) {
storage_oids.push_back(columns.LogicalToPhysical(bound_columns[i]).index);
storage_oids.emplace_back(columns.LogicalToPhysical(bound_columns[i]).index);
}
if (storage_oids.empty()) {
storage_oids.push_back(COLUMN_IDENTIFIER_ROW_ID);
storage_oids.emplace_back(COLUMN_IDENTIFIER_ROW_ID);
}

auto new_storage =
Expand Down
8 changes: 4 additions & 4 deletions src/duckdb/src/common/enum_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2178,9 +2178,9 @@ const StringUtil::EnumStringLiteral *GetMetricsTypeValues() {
{ static_cast<uint32_t>(MetricsType::CUMULATIVE_ROWS_SCANNED), "CUMULATIVE_ROWS_SCANNED" },
{ static_cast<uint32_t>(MetricsType::OPERATOR_ROWS_SCANNED), "OPERATOR_ROWS_SCANNED" },
{ static_cast<uint32_t>(MetricsType::OPERATOR_TIMING), "OPERATOR_TIMING" },
{ static_cast<uint32_t>(MetricsType::RESULT_SET_SIZE), "RESULT_SET_SIZE" },
{ static_cast<uint32_t>(MetricsType::LATENCY), "LATENCY" },
{ static_cast<uint32_t>(MetricsType::ROWS_RETURNED), "ROWS_RETURNED" },
{ static_cast<uint32_t>(MetricsType::RESULT_SET_SIZE), "RESULT_SET_SIZE" },
{ static_cast<uint32_t>(MetricsType::ALL_OPTIMIZERS), "ALL_OPTIMIZERS" },
{ static_cast<uint32_t>(MetricsType::CUMULATIVE_OPTIMIZER_TIMING), "CUMULATIVE_OPTIMIZER_TIMING" },
{ static_cast<uint32_t>(MetricsType::PLANNER), "PLANNER" },
Expand All @@ -2192,6 +2192,7 @@ const StringUtil::EnumStringLiteral *GetMetricsTypeValues() {
{ static_cast<uint32_t>(MetricsType::OPTIMIZER_EXPRESSION_REWRITER), "OPTIMIZER_EXPRESSION_REWRITER" },
{ static_cast<uint32_t>(MetricsType::OPTIMIZER_FILTER_PULLUP), "OPTIMIZER_FILTER_PULLUP" },
{ static_cast<uint32_t>(MetricsType::OPTIMIZER_FILTER_PUSHDOWN), "OPTIMIZER_FILTER_PUSHDOWN" },
{ static_cast<uint32_t>(MetricsType::OPTIMIZER_EMPTY_RESULT_PULLUP), "OPTIMIZER_EMPTY_RESULT_PULLUP" },
{ static_cast<uint32_t>(MetricsType::OPTIMIZER_CTE_FILTER_PUSHER), "OPTIMIZER_CTE_FILTER_PUSHER" },
{ static_cast<uint32_t>(MetricsType::OPTIMIZER_REGEX_RANGE), "OPTIMIZER_REGEX_RANGE" },
{ static_cast<uint32_t>(MetricsType::OPTIMIZER_IN_CLAUSE), "OPTIMIZER_IN_CLAUSE" },
Expand All @@ -2205,15 +2206,14 @@ const StringUtil::EnumStringLiteral *GetMetricsTypeValues() {
{ static_cast<uint32_t>(MetricsType::OPTIMIZER_COLUMN_LIFETIME), "OPTIMIZER_COLUMN_LIFETIME" },
{ static_cast<uint32_t>(MetricsType::OPTIMIZER_BUILD_SIDE_PROBE_SIDE), "OPTIMIZER_BUILD_SIDE_PROBE_SIDE" },
{ static_cast<uint32_t>(MetricsType::OPTIMIZER_LIMIT_PUSHDOWN), "OPTIMIZER_LIMIT_PUSHDOWN" },
{ static_cast<uint32_t>(MetricsType::OPTIMIZER_SAMPLING_PUSHDOWN), "OPTIMIZER_SAMPLING_PUSHDOWN" },
{ static_cast<uint32_t>(MetricsType::OPTIMIZER_TOP_N), "OPTIMIZER_TOP_N" },
{ static_cast<uint32_t>(MetricsType::OPTIMIZER_COMPRESSED_MATERIALIZATION), "OPTIMIZER_COMPRESSED_MATERIALIZATION" },
{ static_cast<uint32_t>(MetricsType::OPTIMIZER_DUPLICATE_GROUPS), "OPTIMIZER_DUPLICATE_GROUPS" },
{ static_cast<uint32_t>(MetricsType::OPTIMIZER_REORDER_FILTER), "OPTIMIZER_REORDER_FILTER" },
{ static_cast<uint32_t>(MetricsType::OPTIMIZER_SAMPLING_PUSHDOWN), "OPTIMIZER_SAMPLING_PUSHDOWN" },
{ static_cast<uint32_t>(MetricsType::OPTIMIZER_JOIN_FILTER_PUSHDOWN), "OPTIMIZER_JOIN_FILTER_PUSHDOWN" },
{ static_cast<uint32_t>(MetricsType::OPTIMIZER_EXTENSION), "OPTIMIZER_EXTENSION" },
{ static_cast<uint32_t>(MetricsType::OPTIMIZER_MATERIALIZED_CTE), "OPTIMIZER_MATERIALIZED_CTE" },
{ static_cast<uint32_t>(MetricsType::OPTIMIZER_EMPTY_RESULT_PULLUP), "OPTIMIZER_EMPTY_RESULT_PULLUP" }
{ static_cast<uint32_t>(MetricsType::OPTIMIZER_MATERIALIZED_CTE), "OPTIMIZER_MATERIALIZED_CTE" }
};
return values;
}
Expand Down
20 changes: 12 additions & 8 deletions src/duckdb/src/common/enums/metric_type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ profiler_settings_t MetricsUtils::GetOptimizerMetrics() {
MetricsType::OPTIMIZER_EXPRESSION_REWRITER,
MetricsType::OPTIMIZER_FILTER_PULLUP,
MetricsType::OPTIMIZER_FILTER_PUSHDOWN,
MetricsType::OPTIMIZER_EMPTY_RESULT_PULLUP,
MetricsType::OPTIMIZER_CTE_FILTER_PUSHER,
MetricsType::OPTIMIZER_REGEX_RANGE,
MetricsType::OPTIMIZER_IN_CLAUSE,
Expand All @@ -33,6 +34,7 @@ profiler_settings_t MetricsUtils::GetOptimizerMetrics() {
MetricsType::OPTIMIZER_COMPRESSED_MATERIALIZATION,
MetricsType::OPTIMIZER_DUPLICATE_GROUPS,
MetricsType::OPTIMIZER_REORDER_FILTER,
MetricsType::OPTIMIZER_SAMPLING_PUSHDOWN,
MetricsType::OPTIMIZER_JOIN_FILTER_PUSHDOWN,
MetricsType::OPTIMIZER_EXTENSION,
MetricsType::OPTIMIZER_MATERIALIZED_CTE,
Expand Down Expand Up @@ -60,6 +62,8 @@ MetricsType MetricsUtils::GetOptimizerMetricByType(OptimizerType type) {
return MetricsType::OPTIMIZER_FILTER_PULLUP;
case OptimizerType::FILTER_PUSHDOWN:
return MetricsType::OPTIMIZER_FILTER_PUSHDOWN;
case OptimizerType::EMPTY_RESULT_PULLUP:
return MetricsType::OPTIMIZER_EMPTY_RESULT_PULLUP;
case OptimizerType::CTE_FILTER_PUSHER:
return MetricsType::OPTIMIZER_CTE_FILTER_PUSHER;
case OptimizerType::REGEX_RANGE:
Expand All @@ -86,8 +90,6 @@ MetricsType MetricsUtils::GetOptimizerMetricByType(OptimizerType type) {
return MetricsType::OPTIMIZER_BUILD_SIDE_PROBE_SIDE;
case OptimizerType::LIMIT_PUSHDOWN:
return MetricsType::OPTIMIZER_LIMIT_PUSHDOWN;
case OptimizerType::SAMPLING_PUSHDOWN:
return MetricsType::OPTIMIZER_SAMPLING_PUSHDOWN;
case OptimizerType::TOP_N:
return MetricsType::OPTIMIZER_TOP_N;
case OptimizerType::COMPRESSED_MATERIALIZATION:
Expand All @@ -96,14 +98,14 @@ MetricsType MetricsUtils::GetOptimizerMetricByType(OptimizerType type) {
return MetricsType::OPTIMIZER_DUPLICATE_GROUPS;
case OptimizerType::REORDER_FILTER:
return MetricsType::OPTIMIZER_REORDER_FILTER;
case OptimizerType::SAMPLING_PUSHDOWN:
return MetricsType::OPTIMIZER_SAMPLING_PUSHDOWN;
case OptimizerType::JOIN_FILTER_PUSHDOWN:
return MetricsType::OPTIMIZER_JOIN_FILTER_PUSHDOWN;
case OptimizerType::EXTENSION:
return MetricsType::OPTIMIZER_EXTENSION;
case OptimizerType::MATERIALIZED_CTE:
return MetricsType::OPTIMIZER_MATERIALIZED_CTE;
case OptimizerType::EMPTY_RESULT_PULLUP:
return MetricsType::OPTIMIZER_EMPTY_RESULT_PULLUP;
default:
throw InternalException("OptimizerType %s cannot be converted to a MetricsType", EnumUtil::ToString(type));
};
Expand All @@ -117,6 +119,8 @@ OptimizerType MetricsUtils::GetOptimizerTypeByMetric(MetricsType type) {
return OptimizerType::FILTER_PULLUP;
case MetricsType::OPTIMIZER_FILTER_PUSHDOWN:
return OptimizerType::FILTER_PUSHDOWN;
case MetricsType::OPTIMIZER_EMPTY_RESULT_PULLUP:
return OptimizerType::EMPTY_RESULT_PULLUP;
case MetricsType::OPTIMIZER_CTE_FILTER_PUSHER:
return OptimizerType::CTE_FILTER_PUSHER;
case MetricsType::OPTIMIZER_REGEX_RANGE:
Expand Down Expand Up @@ -151,14 +155,14 @@ OptimizerType MetricsUtils::GetOptimizerTypeByMetric(MetricsType type) {
return OptimizerType::DUPLICATE_GROUPS;
case MetricsType::OPTIMIZER_REORDER_FILTER:
return OptimizerType::REORDER_FILTER;
case MetricsType::OPTIMIZER_SAMPLING_PUSHDOWN:
return OptimizerType::SAMPLING_PUSHDOWN;
case MetricsType::OPTIMIZER_JOIN_FILTER_PUSHDOWN:
return OptimizerType::JOIN_FILTER_PUSHDOWN;
case MetricsType::OPTIMIZER_EXTENSION:
return OptimizerType::EXTENSION;
case MetricsType::OPTIMIZER_MATERIALIZED_CTE:
return OptimizerType::MATERIALIZED_CTE;
case MetricsType::OPTIMIZER_EMPTY_RESULT_PULLUP:
return OptimizerType::EMPTY_RESULT_PULLUP;
default:
return OptimizerType::INVALID;
};
Expand All @@ -169,6 +173,7 @@ bool MetricsUtils::IsOptimizerMetric(MetricsType type) {
case MetricsType::OPTIMIZER_EXPRESSION_REWRITER:
case MetricsType::OPTIMIZER_FILTER_PULLUP:
case MetricsType::OPTIMIZER_FILTER_PUSHDOWN:
case MetricsType::OPTIMIZER_EMPTY_RESULT_PULLUP:
case MetricsType::OPTIMIZER_CTE_FILTER_PUSHER:
case MetricsType::OPTIMIZER_REGEX_RANGE:
case MetricsType::OPTIMIZER_IN_CLAUSE:
Expand All @@ -182,15 +187,14 @@ bool MetricsUtils::IsOptimizerMetric(MetricsType type) {
case MetricsType::OPTIMIZER_COLUMN_LIFETIME:
case MetricsType::OPTIMIZER_BUILD_SIDE_PROBE_SIDE:
case MetricsType::OPTIMIZER_LIMIT_PUSHDOWN:
case MetricsType::OPTIMIZER_SAMPLING_PUSHDOWN:
case MetricsType::OPTIMIZER_TOP_N:
case MetricsType::OPTIMIZER_COMPRESSED_MATERIALIZATION:
case MetricsType::OPTIMIZER_DUPLICATE_GROUPS:
case MetricsType::OPTIMIZER_REORDER_FILTER:
case MetricsType::OPTIMIZER_SAMPLING_PUSHDOWN:
case MetricsType::OPTIMIZER_JOIN_FILTER_PUSHDOWN:
case MetricsType::OPTIMIZER_EXTENSION:
case MetricsType::OPTIMIZER_MATERIALIZED_CTE:
case MetricsType::OPTIMIZER_EMPTY_RESULT_PULLUP:
return true;
default:
return false;
Expand Down
5 changes: 4 additions & 1 deletion src/duckdb/src/common/multi_file_list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@
namespace duckdb {

MultiFilePushdownInfo::MultiFilePushdownInfo(LogicalGet &get)
: table_index(get.table_index), column_names(get.names), column_ids(get.GetColumnIds()),
: table_index(get.table_index), column_names(get.names), column_indexes(get.GetColumnIds()),
extra_info(get.extra_info) {
for (auto &col_id : column_indexes) {
column_ids.push_back(col_id.GetPrimaryIndex());
}
}

MultiFilePushdownInfo::MultiFilePushdownInfo(idx_t table_index, const vector<string> &column_names,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,26 @@ PhysicalBatchInsert::PhysicalBatchInsert(LogicalOperator &op, SchemaCatalogEntry
//===--------------------------------------------------------------------===//
// CollectionMerger
//===--------------------------------------------------------------------===//
enum class RowGroupBatchType : uint8_t { FLUSHED, NOT_FLUSHED };

class CollectionMerger {
public:
explicit CollectionMerger(ClientContext &context) : context(context) {
}

ClientContext &context;
vector<unique_ptr<RowGroupCollection>> current_collections;
RowGroupBatchType batch_type = RowGroupBatchType::NOT_FLUSHED;

public:
void AddCollection(unique_ptr<RowGroupCollection> collection) {
void AddCollection(unique_ptr<RowGroupCollection> collection, RowGroupBatchType type) {
current_collections.push_back(std::move(collection));
if (type == RowGroupBatchType::FLUSHED) {
batch_type = RowGroupBatchType::FLUSHED;
if (current_collections.size() > 1) {
throw InternalException("Cannot merge flushed collections");
}
}
}

bool Empty() {
Expand All @@ -65,9 +74,9 @@ class CollectionMerger {
DataChunk scan_chunk;
scan_chunk.Initialize(context, types);

vector<column_t> column_ids;
vector<StorageIndex> column_ids;
for (idx_t i = 0; i < types.size(); i++) {
column_ids.push_back(i);
column_ids.emplace_back(i);
}
for (auto &collection : current_collections) {
if (!collection) {
Expand All @@ -91,13 +100,14 @@ class CollectionMerger {
}
new_collection->FinalizeAppend(TransactionData(0, 0), append_state);
writer.WriteLastRowGroup(*new_collection);
} else if (batch_type == RowGroupBatchType::NOT_FLUSHED) {
writer.WriteLastRowGroup(*new_collection);
}
current_collections.clear();
return new_collection;
}
};

enum class RowGroupBatchType : uint8_t { FLUSHED, NOT_FLUSHED };
struct RowGroupBatchEntry {
RowGroupBatchEntry(idx_t batch_idx, unique_ptr<RowGroupCollection> collection_p, RowGroupBatchType type)
: batch_idx(batch_idx), total_rows(collection_p->GetTotalRows()), unflushed_memory(0),
Expand Down Expand Up @@ -332,7 +342,7 @@ unique_ptr<RowGroupCollection> BatchInsertGlobalState::MergeCollections(ClientCo
CollectionMerger merger(context);
idx_t written_data = 0;
for (auto &entry : merge_collections) {
merger.AddCollection(std::move(entry.collection));
merger.AddCollection(std::move(entry.collection), RowGroupBatchType::NOT_FLUSHED);
written_data += entry.unflushed_memory;
}
optimistically_written = true;
Expand Down Expand Up @@ -571,7 +581,7 @@ SinkFinalizeType PhysicalBatchInsert::Finalize(Pipeline &pipeline, Event &event,
if (!current_merger) {
current_merger = make_uniq<CollectionMerger>(context);
}
current_merger->AddCollection(std::move(entry.collection));
current_merger->AddCollection(std::move(entry.collection), entry.type);
memory_manager.ReduceUnflushedMemory(entry.unflushed_memory);
} else {
// this collection has been flushed: it does not need to be merged
Expand All @@ -582,7 +592,7 @@ SinkFinalizeType PhysicalBatchInsert::Finalize(Pipeline &pipeline, Event &event,
current_merger.reset();
}
auto larger_merger = make_uniq<CollectionMerger>(context);
larger_merger->AddCollection(std::move(entry.collection));
larger_merger->AddCollection(std::move(entry.collection), entry.type);
mergers.push_back(std::move(larger_merger));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,12 @@ SinkFinalizeType PhysicalCopyToFile::Finalize(Pipeline &pipeline, Event &event,
}
if (per_thread_output) {
// already happened in combine
if (NumericCast<int64_t>(gstate.rows_copied.load()) == 0 && sink_state != nullptr) {
// no rows from source, write schema to file
auto global_lock = gstate.lock.GetExclusiveLock();
gstate.global_state = CreateFileState(context, *sink_state, *global_lock);
function.copy_to_finalize(context, *bind_data, *gstate.global_state);
}
return SinkFinalizeType::READY;
}
if (function.copy_to_finalize) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ SinkResultType PhysicalDelete::Sink(ExecutionContext &context, DataChunk &chunk,
auto &transaction = DuckTransaction::Get(context.client, table.db);
auto &row_identifiers = chunk.data[row_id_index];

vector<column_t> column_ids;
vector<StorageIndex> column_ids;
for (idx_t i = 0; i < table.ColumnCount(); i++) {
column_ids.emplace_back(i);
};
Expand Down
11 changes: 7 additions & 4 deletions src/duckdb/src/execution/operator/persistent/physical_insert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ PhysicalInsert::PhysicalInsert(
return_chunk(return_chunk), parallel(parallel), action_type(action_type),
set_expressions(std::move(set_expressions)), set_columns(std::move(set_columns)), set_types(std::move(set_types)),
on_conflict_condition(std::move(on_conflict_condition_p)), do_update_condition(std::move(do_update_condition_p)),
conflict_target(std::move(conflict_target_p)), columns_to_fetch(std::move(columns_to_fetch_p)) {
conflict_target(std::move(conflict_target_p)) {

if (action_type == OnConflictAction::THROW) {
return;
Expand All @@ -44,11 +44,12 @@ PhysicalInsert::PhysicalInsert(

// One or more columns are referenced from the existing table,
// we use the 'insert_types' to figure out which types these columns have
types_to_fetch = vector<LogicalType>(columns_to_fetch.size(), LogicalType::SQLNULL);
for (idx_t i = 0; i < columns_to_fetch.size(); i++) {
auto &id = columns_to_fetch[i];
types_to_fetch = vector<LogicalType>(columns_to_fetch_p.size(), LogicalType::SQLNULL);
for (idx_t i = 0; i < columns_to_fetch_p.size(); i++) {
auto &id = columns_to_fetch_p[i];
D_ASSERT(id < insert_types.size());
types_to_fetch[i] = insert_types[id];
columns_to_fetch.emplace_back(id);
}
}

Expand Down Expand Up @@ -526,6 +527,8 @@ SinkCombineResultType PhysicalInsert::Combine(ExecutionContext &context, Operato
storage.FinalizeLocalAppend(gstate.append_state);
} else {
// we have written rows to disk optimistically - merge directly into the transaction-local storage
lstate.writer->WriteLastRowGroup(*lstate.local_collection);

gstate.table.GetStorage().LocalMerge(context.client, *lstate.local_collection);
gstate.table.GetStorage().FinalizeOptimisticWriter(context.client, *lstate.writer);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class TableInOutGlobalState : public GlobalOperatorState {

PhysicalTableInOutFunction::PhysicalTableInOutFunction(vector<LogicalType> types, TableFunction function_p,
unique_ptr<FunctionData> bind_data_p,
vector<column_t> column_ids_p, idx_t estimated_cardinality,
vector<ColumnIndex> column_ids_p, idx_t estimated_cardinality,
vector<column_t> project_input_p)
: PhysicalOperator(PhysicalOperatorType::INOUT_FUNCTION, std::move(types), estimated_cardinality),
function(std::move(function_p)), bind_data(std::move(bind_data_p)), column_ids(std::move(column_ids_p)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace duckdb {

PhysicalTableScan::PhysicalTableScan(vector<LogicalType> types, TableFunction function_p,
unique_ptr<FunctionData> bind_data_p, vector<LogicalType> returned_types_p,
vector<column_t> column_ids_p, vector<idx_t> projection_ids_p,
vector<ColumnIndex> column_ids_p, vector<idx_t> projection_ids_p,
vector<string> names_p, unique_ptr<TableFilterSet> table_filters_p,
idx_t estimated_cardinality, ExtraOperatorInfo extra_info,
vector<Value> parameters_p)
Expand Down Expand Up @@ -156,7 +156,7 @@ InsertionOrderPreservingMap<string> PhysicalTableScan::ParamsToString() const {
if (function.filter_prune) {
string projections;
for (idx_t i = 0; i < projection_ids.size(); i++) {
const auto &column_id = column_ids[projection_ids[i]];
auto column_id = column_ids[projection_ids[i]].GetPrimaryIndex();
if (column_id < names.size()) {
if (i > 0) {
projections += "\n";
Expand All @@ -168,7 +168,7 @@ InsertionOrderPreservingMap<string> PhysicalTableScan::ParamsToString() const {
} else {
string projections;
for (idx_t i = 0; i < column_ids.size(); i++) {
const auto &column_id = column_ids[i];
auto column_id = column_ids[i].GetPrimaryIndex();
if (column_id < names.size()) {
if (i > 0) {
projections += "\n";
Expand All @@ -190,7 +190,8 @@ InsertionOrderPreservingMap<string> PhysicalTableScan::ParamsToString() const {
filters_info += "\n";
}
first_item = false;
filters_info += filter->ToString(names[column_ids[column_index]]);
auto &col_name = names[column_ids[column_index].GetPrimaryIndex()];
filters_info += filter->ToString(col_name);
}
}
result["Filters"] = filters_info;
Expand Down
2 changes: 1 addition & 1 deletion src/duckdb/src/execution/physical_plan/plan_aggregate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ static bool CanUsePartitionedAggregate(ClientContext &context, LogicalAggregate
vector<column_t> base_columns;
for (const auto &partition_idx : partition_columns) {
auto col_idx = partition_idx;
col_idx = table_scan.column_ids[col_idx];
col_idx = table_scan.column_ids[col_idx].GetPrimaryIndex();
base_columns.push_back(col_idx);
}
// check if the source operator is partitioned by the grouping columns
Expand Down
Loading

0 comments on commit 7bad582

Please sign in to comment.