44#include < Storages/MergeTree/MergeTreeData.h>
55#include < Interpreters/Context.h>
66#include < Interpreters/DatabaseCatalog.h>
7+ #include < Interpreters/inplaceBlockConversions.h>
78#include < Core/Settings.h>
89#include < Interpreters/ExpressionActions.h>
910#include < Processors/Executors/CompletedPipelineExecutor.h>
1011#include < Processors/QueryPlan/BuildQueryPipelineSettings.h>
1112#include < Processors/QueryPlan/Optimizations/QueryPlanOptimizationSettings.h>
1213#include < Processors/QueryPlan/QueryPlan.h>
14+ #include < Processors/QueryPlan/ExpressionStep.h>
1315#include < QueryPipeline/QueryPipelineBuilder.h>
1416#include < Common/Exception.h>
1517#include < Common/ProfileEventsScope.h>
1618#include < Storages/MergeTree/ExportList.h>
1719#include < Formats/FormatFactory.h>
20+ #include < Databases/enableAllExperimentalSettings.h>
1821
1922namespace ProfileEvents
2023{
@@ -42,6 +45,43 @@ namespace Setting
4245 extern const SettingsUInt64 export_merge_tree_part_max_rows_per_file;
4346}
4447
48+ namespace
49+ {
50+ void materializeSpecialColumns (
51+ const SharedHeader & header,
52+ const StorageMetadataPtr & storage_metadata,
53+ const ContextPtr & local_context,
54+ QueryPlan & plan_for_part
55+ )
56+ {
57+ const auto readable_columns = storage_metadata->getColumns ().getReadable ();
58+
59+ // Enable all experimental settings for default expressions
60+ // (same pattern as in IMergeTreeReader::evaluateMissingDefaults)
61+ auto context_for_defaults = Context::createCopy (local_context);
62+ enableAllExperimentalSettings (context_for_defaults);
63+
64+ auto defaults_dag = evaluateMissingDefaults (
65+ *header,
66+ readable_columns,
67+ storage_metadata->getColumns (),
68+ context_for_defaults);
69+
70+ if (defaults_dag)
71+ {
72+ // / Ensure columns are in the correct order matching readable_columns
73+ defaults_dag->removeUnusedActions (readable_columns.getNames (), false );
74+ defaults_dag->addMaterializingOutputActions (/* materialize_sparse=*/ false );
75+
76+ auto expression_step = std::make_unique<ExpressionStep>(
77+ header,
78+ std::move (*defaults_dag));
79+ expression_step->setStepDescription (" Compute alias and default expressions for export" );
80+ plan_for_part.addStep (std::move (expression_step));
81+ }
82+ }
83+ }
84+
4585ExportPartTask::ExportPartTask (MergeTreeData & storage_, const MergeTreePartExportManifest & manifest_)
4686 : storage(storage_),
4787 manifest (manifest_)
@@ -58,7 +98,8 @@ bool ExportPartTask::executeStep()
5898
5999 const auto & metadata_snapshot = manifest.metadata_snapshot ;
60100
61- Names columns_to_read = metadata_snapshot->getColumns ().getNamesOfPhysical ();
101+ // / Read only physical columns from the part
102+ const auto columns_to_read = metadata_snapshot->getColumns ().getNamesOfPhysical ();
62103
63104 MergeTreeSequentialSourceType read_type = MergeTreeSequentialSourceType::Export;
64105
@@ -146,6 +187,10 @@ bool ExportPartTask::executeStep()
146187 local_context,
147188 getLogger (" ExportPartition" ));
148189
190+ // / We need to support exporting materialized and alias columns to object storage. For some reason, object storage engines don't support them.
191+ // / This is a hack that materializes the columns before the export so they can be exported to tables that have matching columns
192+ materializeSpecialColumns (plan_for_part.getCurrentHeader (), metadata_snapshot, local_context, plan_for_part);
193+
149194 ThreadGroupSwitcher switcher ((*exports_list_entry)->thread_group , " " );
150195
151196 QueryPlanOptimizationSettings optimization_settings (local_context);
0 commit comments