Block addMissingDefaults(const Block & block, const NamesAndTypesList & required_columns, const ColumnDefaults & column_defaults, const Context & context) { /// For missing columns of nested structure, you need to create not a column of empty arrays, but a column of arrays of correct lengths. /// First, remember the offset columns for all arrays in the block. std::map<String, ColumnPtr> offset_columns; for (size_t i = 0, size = block.columns(); i < size; ++i) { const auto & elem = block.getByPosition(i); if (const ColumnArray * array = typeid_cast<const ColumnArray *>(&*elem.column)) { String offsets_name = Nested::extractTableName(elem.name); auto & offsets_column = offset_columns[offsets_name]; /// If for some reason there are different offset columns for one nested structure, then we take nonempty. if (!offsets_column || offsets_column->empty()) offsets_column = array->getOffsetsPtr(); } } const size_t rows = block.rows(); Block res; /// We take given columns from input block and missed columns without default value /// (default and materialized will be computed later). for (const auto & column : required_columns) { if (block.has(column.name)) { res.insert(block.getByName(column.name)); continue; } if (column_defaults.count(column.name)) continue; String offsets_name = Nested::extractTableName(column.name); if (offset_columns.count(offsets_name)) { ColumnPtr offsets_column = offset_columns[offsets_name]; DataTypePtr nested_type = typeid_cast<const DataTypeArray &>(*column.type).getNestedType(); UInt64 nested_rows = rows ? get<UInt64>((*offsets_column)[rows - 1]) : 0; ColumnPtr nested_column = nested_type->createColumnConstWithDefaultValue(nested_rows)->convertToFullColumnIfConst(); auto new_column = ColumnArray::create(nested_column, offsets_column); res.insert(ColumnWithTypeAndName(std::move(new_column), column.type, column.name)); continue; } /** It is necessary to turn a constant column into a full column, since in part of blocks (from other parts), * it can be full (or the interpreter may decide that it is constant everywhere). */ auto new_column = column.type->createColumnConstWithDefaultValue(rows)->convertToFullColumnIfConst(); res.insert(ColumnWithTypeAndName(std::move(new_column), column.type, column.name)); } /// Computes explicitly specified values (in column_defaults) by default and materialized columns. evaluateMissingDefaults(res, required_columns, column_defaults, context); return res; }
void MergeTreeReader::fillMissingColumns(Block & res, const Names & ordered_names, bool always_reorder) { if (!res) throw Exception("Empty block passed to fillMissingColumns", ErrorCodes::LOGICAL_ERROR); try { /// For a missing column of a nested data structure we must create not a column of empty /// arrays, but a column of arrays of correct length. /// NOTE: Similar, but slightly different code is present in Block::addDefaults. /// First, collect offset columns for all arrays in the block. OffsetColumns offset_columns; for (size_t i = 0; i < res.columns(); ++i) { const ColumnWithTypeAndName & column = res.safeGetByPosition(i); if (const ColumnArray * array = typeid_cast<const ColumnArray *>(column.column.get())) { String offsets_name = Nested::extractTableName(column.name); auto & offsets_column = offset_columns[offsets_name]; /// If for some reason multiple offsets columns are present for the same nested data structure, /// choose the one that is not empty. if (!offsets_column || offsets_column->empty()) offsets_column = array->getOffsetsPtr(); } } bool should_evaluate_defaults = false; bool should_sort = always_reorder; size_t rows = res.rows(); /// insert default values only for columns without default expressions for (const auto & requested_column : columns) { bool has_column = res.has(requested_column.name); if (has_column) { const auto & col = *res.getByName(requested_column.name).column; if (arrayHasNoElementsRead(col)) { res.erase(requested_column.name); has_column = false; } } if (!has_column) { should_sort = true; if (storage.column_defaults.count(requested_column.name) != 0) { should_evaluate_defaults = true; continue; } ColumnWithTypeAndName column_to_add; column_to_add.name = requested_column.name; column_to_add.type = requested_column.type; String offsets_name = Nested::extractTableName(column_to_add.name); if (offset_columns.count(offsets_name)) { ColumnPtr offsets_column = offset_columns[offsets_name]; DataTypePtr nested_type = typeid_cast<const DataTypeArray &>(*column_to_add.type).getNestedType(); size_t nested_rows = offsets_column->empty() ? 0 : typeid_cast<const ColumnUInt64 &>(*offsets_column).getData().back(); ColumnPtr nested_column = nested_type->createColumnConstWithDefaultValue(nested_rows)->convertToFullColumnIfConst(); column_to_add.column = ColumnArray::create(nested_column, offsets_column); } else { /// We must turn a constant column into a full column because the interpreter could infer that it is constant everywhere /// but in some blocks (from other parts) it can be a full column. column_to_add.column = column_to_add.type->createColumnConstWithDefaultValue(rows)->convertToFullColumnIfConst(); } res.insert(std::move(column_to_add)); } } /// evaluate defaulted columns if necessary if (should_evaluate_defaults) evaluateMissingDefaults(res, columns, storage.column_defaults, storage.context); /// sort columns to ensure consistent order among all blocks if (should_sort) { Block ordered_block; for (const auto & name : ordered_names) if (res.has(name)) ordered_block.insert(res.getByName(name)); std::swap(res, ordered_block); } } catch (Exception & e) { /// Better diagnostics. e.addMessage("(while reading from part " + path + ")"); throw; } }