diff --git a/dbms/include/DB/Core/Block.h b/dbms/include/DB/Core/Block.h index 4f79eebe96e..978aa73c87a 100644 --- a/dbms/include/DB/Core/Block.h +++ b/dbms/include/DB/Core/Block.h @@ -137,6 +137,9 @@ using BlocksList = std::list; /// Сравнить типы столбцов у блоков. Порядок столбцов имеет значение. Имена не имеют значения. bool blocksHaveEqualStructure(const Block & lhs, const Block & rhs); +/// Записать различные столбцы у блоков. +void getColumnDiff(const Block & lhs, const Block & rhs, std::string & lhs_diff, std::string & rhs_diff); + /** Дополнительные данные к блокам. Они пока нужны только для запроса * DESCRIBE TABLE с Distributed-таблицами. */ diff --git a/dbms/include/DB/Core/ColumnWithTypeAndName.h b/dbms/include/DB/Core/ColumnWithTypeAndName.h index 25b62658227..c88541047a8 100644 --- a/dbms/include/DB/Core/ColumnWithTypeAndName.h +++ b/dbms/include/DB/Core/ColumnWithTypeAndName.h @@ -31,6 +31,24 @@ struct ColumnWithTypeAndName return res; } + + bool operator==(const ColumnWithTypeAndName & other) const + { + return name == other.name + && ((!type && !other.type) || (type && other.type && type->getName() == other.type->getName())) + && ((!column && !other.column) || (column && other.column && column->getName() == other.column->getName())); + } + + std::string prettyPrint() const + { + std::stringstream str; + str << name << ' '; + if (type) + str << type->getName() << ' '; + if (column) + str << column->getName(); + return str.str(); + } }; } diff --git a/dbms/src/Core/Block.cpp b/dbms/src/Core/Block.cpp index 92251005a6a..dcd8f65ba7f 100644 --- a/dbms/src/Core/Block.cpp +++ b/dbms/src/Core/Block.cpp @@ -455,6 +455,61 @@ bool blocksHaveEqualStructure(const Block & lhs, const Block & rhs) return true; } +void getColumnDiff(const Block & lhs, const Block & rhs, std::string & lhs_diff, std::string & rhs_diff) +{ + /// Традиционная задача: наибольшая общая подпоследовательность (LCS). + /// Полагаем, что порядок важен. Если это когда-то станет не так, упростим: например, намутим 2 set'а. + std::vector> LCS; + LCS.resize(lhs.columns() + 1); + for (auto & v : LCS) + v.resize(rhs.columns() + 1, 0); + for (size_t i = 1; i <= lhs.columns(); ++i) + for (size_t j = 1; j <= rhs.columns(); ++j) + { + if (lhs.getByPosition(i-1) == rhs.getByPosition(j-1)) + LCS[i][j] = LCS[i-1][j-1] + 1; + else + LCS[i][j] = std::max(LCS[i-1][j], LCS[i][j-1]); + } + + /// Теперь идем обратно и собираем ответ. + std::vector left_columns, right_columns; + size_t l = lhs.columns(), r = rhs.columns(); + while (l > 0 && r > 0) + { + if (lhs.getByPosition(l-1) == rhs.getByPosition(r-1)) + { + /// Данный элемент в обеих последовательностях, значит, в diff не попадает. + --l; + --r; + } + else + { + /// Маленькая эвристика: чаще всего используется при получении разницы для (expected_block, actual_block). + /// Поэтому предпочтение будем отдавать полю, которое есть в левом блоке (expected_block), поэтому + /// в diff попадет столбец из actual_block. + if (LCS[l][r-1] >= LCS[l-1][r]) + right_columns.push_back(rhs.getByPosition(--r).prettyPrint()); + else + left_columns.push_back(lhs.getByPosition(--l).prettyPrint()); + } + } + + while (l > 0) + left_columns.push_back(lhs.getByPosition(--l).prettyPrint()); + while (r > 0) + right_columns.push_back(rhs.getByPosition(--r).prettyPrint()); + + std::stringstream lhs_columns_diff, rhs_columns_diff; + for (auto it = left_columns.rbegin(); it != left_columns.rend(); ++it) + lhs_columns_diff << *it << '\n'; + for (auto it = right_columns.rbegin(); it != right_columns.rend(); ++it) + rhs_columns_diff << *it << '\n'; + + lhs_diff = lhs_columns_diff.str(); + rhs_diff = rhs_columns_diff.str(); +} + void Block::clear() {