Fixed code review issues

This commit is contained in:
Maksim Kita 2022-10-19 18:49:17 +02:00
parent 561d585100
commit d083200d65
32 changed files with 196 additions and 198 deletions

View File

@ -285,7 +285,7 @@ void ReplaceColumnTransformerNode::dumpTreeImpl(WriteBuffer & buffer, FormatStat
{
buffer << std::string(indent, ' ') << "REPLACE COLUMN TRANSFORMER id: " << format_state.getNodeId(this);
auto & replacements_nodes = getReplacements().getNodes();
const auto & replacements_nodes = getReplacements().getNodes();
size_t replacements_size = replacements_nodes.size();
buffer << '\n' << std::string(indent + 2, ' ') << "REPLACEMENTS " << replacements_size << '\n';
@ -312,7 +312,7 @@ void ReplaceColumnTransformerNode::updateTreeHashImpl(IQueryTreeNode::HashState
{
hash_state.update(static_cast<size_t>(getTransformerType()));
auto & replacement_expressions_nodes = getReplacements().getNodes();
const auto & replacement_expressions_nodes = getReplacements().getNodes();
size_t replacements_size = replacement_expressions_nodes.size();
hash_state.update(replacements_size);
@ -338,7 +338,7 @@ ASTPtr ReplaceColumnTransformerNode::toASTImpl() const
{
auto ast_replace_transformer = std::make_shared<ASTColumnsReplaceTransformer>();
auto & replacement_expressions_nodes = getReplacements().getNodes();
const auto & replacement_expressions_nodes = getReplacements().getNodes();
size_t replacements_size = replacement_expressions_nodes.size();
ast_replace_transformer->children.reserve(replacements_size);

View File

@ -47,12 +47,12 @@ class AggregateFunctionsArithmericOperationsVisitor : public InDepthQueryTreeVis
{
public:
/// Traverse tree bottom to top
bool shouldTraverseTopToBottom() const
static bool shouldTraverseTopToBottom()
{
return false;
}
void visitImpl(QueryTreeNodePtr & node)
static void visitImpl(QueryTreeNodePtr & node)
{
auto * aggregate_function_node = node->as<FunctionNode>();
if (!aggregate_function_node || !aggregate_function_node->isAggregateFunction())

View File

@ -33,7 +33,7 @@ public:
return;
/// Check that query has only single node in projection
auto projection_nodes = query_node->getProjection().getNodes();
auto & projection_nodes = query_node->getProjection().getNodes();
if (projection_nodes.size() != 1)
return;
@ -43,7 +43,8 @@ public:
if (!function_node)
return;
if (Poco::toLower(function_node->getFunctionName()) != "countdistinct" && Poco::toLower(function_node->getFunctionName()) != "uniqexact")
auto lower_function_name = Poco::toLower(function_node->getFunctionName());
if (lower_function_name != "countdistinct" && lower_function_name != "uniqexact")
return;
/// Check that `countDistinct` function has single COLUMN argument

View File

@ -47,7 +47,7 @@ public:
function_name_lowercase = Poco::toLower(function_name);
}
/// Replace countDistinct with countIfDistinct with countDistinctIf implementation
/// Replace countIfDistinct with countDistinctIf implementation
if (function_name_lowercase == "countifdistinct")
{
resolveAggregateOrWindowFunctionNode(*function_node, count_distinct_implementation_function_name + "If");
@ -55,7 +55,7 @@ public:
function_name_lowercase = Poco::toLower(function_name);
}
/// Swap aggregateFunctionIfDistinct into aggregateFunctionDistinctIf to make execution more optimal
/// Replace aggregateFunctionIfDistinct into aggregateFunctionDistinctIf to make execution more optimal
if (function_name_lowercase.ends_with("ifdistinct"))
{
size_t prefix_length = function_name_lowercase.size() - strlen("ifdistinct");
@ -93,10 +93,13 @@ public:
static constexpr std::array<std::string_view, 4> suffixes_to_replace = {"MergeState", "Merge", "State", "If"};
for (const auto & suffix : suffixes_to_replace)
{
if (!function_name_lowercase.ends_with(suffix))
auto suffix_string_value = String(suffix);
auto suffix_to_check = suffix_string_value + "OrNull";
if (!function_name.ends_with(suffix_to_check))
continue;
auto updated_function_name = function_name_lowercase.substr(0, function_name_size - suffix.size()) + "OrNull" + String(suffix);
auto updated_function_name = function_name.substr(0, function_name_size - suffix_to_check.size()) + "OrNull" + suffix_string_value;
resolveAggregateOrWindowFunctionNode(*function_node, updated_function_name);
function_name = function_node->getFunctionName();
function_name_lowercase = Poco::toLower(function_name);

View File

@ -35,7 +35,7 @@ public:
if (!function_node)
return;
auto function_arguments_nodes = function_node->getArguments().getNodes();
auto & function_arguments_nodes = function_node->getArguments().getNodes();
size_t function_arguments_nodes_size = function_arguments_nodes.size();
if (function_arguments_nodes.empty() || function_arguments_nodes_size > 2)
@ -67,7 +67,7 @@ public:
{
if (function_name == "length")
{
/// Replace `length(array_argument)` with `array_argument.length`
/// Replace `length(array_argument)` with `array_argument.size0`
column.name += ".size0";
node = std::make_shared<ColumnNode>(column, column_source);
@ -78,15 +78,11 @@ public:
column.name += ".size0";
column.type = std::make_shared<DataTypeUInt64>();
auto equals_function = std::make_shared<FunctionNode>("equals");
resolveOrdinaryFunctionNode(*equals_function, "equals");
resolveOrdinaryFunctionNode(*function_node, "equals");
auto & equals_function_arguments = equals_function->getArguments().getNodes();
equals_function_arguments.reserve(2);
equals_function_arguments.push_back(std::make_shared<ColumnNode>(column, column_source));
equals_function_arguments.push_back(std::make_shared<ConstantNode>(static_cast<UInt64>(0)));
node = std::move(equals_function);
function_arguments_nodes.clear();
function_arguments_nodes.push_back(std::make_shared<ColumnNode>(column, column_source));
function_arguments_nodes.push_back(std::make_shared<ConstantNode>(static_cast<UInt64>(0)));
}
else if (function_name == "notEmpty")
{
@ -94,15 +90,11 @@ public:
column.name += ".size0";
column.type = std::make_shared<DataTypeUInt64>();
auto not_equals_function = std::make_shared<FunctionNode>("notEquals");
resolveOrdinaryFunctionNode(*not_equals_function, "notEquals");
resolveOrdinaryFunctionNode(*function_node, "notEquals");
auto & not_equals_function_arguments = not_equals_function->getArguments().getNodes();
not_equals_function_arguments.reserve(2);
not_equals_function_arguments.push_back(std::make_shared<ColumnNode>(column, column_source));
not_equals_function_arguments.push_back(std::make_shared<ConstantNode>(static_cast<UInt64>(0)));
node = std::move(not_equals_function);
function_arguments_nodes.clear();
function_arguments_nodes.push_back(std::make_shared<ColumnNode>(column, column_source));
function_arguments_nodes.push_back(std::make_shared<ConstantNode>(static_cast<UInt64>(0)));
}
}
else if (column_type.isNullable())
@ -120,24 +112,18 @@ public:
column.name += ".null";
column.type = std::make_shared<DataTypeUInt8>();
auto not_function = std::make_shared<FunctionNode>("not");
resolveOrdinaryFunctionNode(*not_function, "not");
resolveOrdinaryFunctionNode(*function_node, "not");
auto & not_function_arguments = not_function->getArguments().getNodes();
not_function_arguments.push_back(std::make_shared<ColumnNode>(column, column_source));
node = std::move(not_function);
function_arguments_nodes = {std::make_shared<ColumnNode>(column, column_source)};
}
}
else if (column_type.isMap())
{
const auto & data_type_map = assert_cast<const DataTypeMap &>(*column.type);
if (function_name == "mapKeys")
{
/// Replace `mapKeys(map_argument)` with `map_argument.keys`
column.name += ".keys";
column.type = data_type_map.getKeyType();
column.type = function_node->getResultType();
node = std::make_shared<ColumnNode>(column, column_source);
}
@ -145,7 +131,7 @@ public:
{
/// Replace `mapValues(map_argument)` with `map_argument.values`
column.name += ".values";
column.type = data_type_map.getValueType();
column.type = function_node->getResultType();
node = std::make_shared<ColumnNode>(column, column_source);
}
@ -183,9 +169,7 @@ public:
column.name += '.';
column.name += subcolumn_name;
size_t subcolumn_position = data_type_tuple.getPositionByName(subcolumn_name);
column.type = data_type_tuple.getElement(subcolumn_position);
column.type = function_node->getResultType();
node = std::make_shared<ColumnNode>(column, column_source);
}
@ -198,15 +182,9 @@ public:
column.type = data_type_map.getKeyType();
auto has_function_argument = std::make_shared<ColumnNode>(column, column_source);
auto has_function = std::make_shared<FunctionNode>("has");
resolveOrdinaryFunctionNode(*has_function, "has");
resolveOrdinaryFunctionNode(*function_node, "has");
auto & has_function_arguments = has_function->getArguments().getNodes();
has_function_arguments.reserve(2);
has_function_arguments.push_back(std::move(has_function_argument));
has_function_arguments.push_back(std::move(function_arguments_nodes[1]));
node = std::move(has_function);
function_arguments_nodes[0] = std::move(has_function_argument);
}
}
}

View File

@ -13,7 +13,7 @@ namespace
class IfConstantConditionVisitor : public InDepthQueryTreeVisitor<IfConstantConditionVisitor>
{
public:
void visitImpl(QueryTreeNodePtr & node)
static void visitImpl(QueryTreeNodePtr & node)
{
auto * function_node = node->as<FunctionNode>();
if (!function_node || (function_node->getFunctionName() != "if" && function_node->getFunctionName() != "multiIf"))

View File

@ -7,6 +7,7 @@ namespace DB
/** Convert `if` with constant condition or `multiIf` with single constant condition into true condition argument value
* or false condition argument value.
*
* Example: SELECT if(1, true_value, false_value);
* Result: SELECT true_value;
*

View File

@ -27,7 +27,7 @@ public:
return;
auto result_type = function_node->getResultType();
function_node->resolveAsFunction(if_function_ptr, result_type);
function_node->resolveAsFunction(if_function_ptr, std::move(result_type));
}
private:

View File

@ -5,7 +5,8 @@
namespace DB
{
/** Convert `multiIf` with single argument into `if`.
/** Convert `multiIf` with single condition into `if`.
*
* Example: SELECT multiIf(x, 1, 0);
* Result: SELECT if(x, 1, 0);
*/
@ -14,7 +15,7 @@ class MultiIfToIfPass final : public IQueryTreePass
public:
String getName() override { return "MultiIfToIf"; }
String getDescription() override { return "Optimize multiIf to if for single argument."; }
String getDescription() override { return "Optimize multiIf with single condition to if."; }
void run(QueryTreeNodePtr query_tree_node, ContextPtr context) override;

View File

@ -15,7 +15,7 @@ namespace
class NormalizeCountVariantsVisitor : public InDepthQueryTreeVisitor<NormalizeCountVariantsVisitor>
{
public:
void visitImpl(QueryTreeNodePtr & node)
static void visitImpl(QueryTreeNodePtr & node)
{
auto * function_node = node->as<FunctionNode>();
if (!function_node || !function_node->isAggregateFunction() || (function_node->getFunctionName() != "count" && function_node->getFunctionName() != "sum"))

View File

@ -7,7 +7,7 @@ namespace DB
/** Remove single literal argument from `count`. Convert `sum` with single `1` literal argument into `count`.
*
* Example: SELECT count(1)
* Example: SELECT count(1);
* Result: SELECT count();
*
* Example: SELECT sum(1);

View File

@ -70,7 +70,7 @@ public:
auto & query_limit_by_nodes = query_node->getLimitBy().getNodes();
for (auto & limit_by_node : query_node->getLimitBy().getNodes())
for (auto & limit_by_node : query_limit_by_nodes)
{
auto [_, inserted] = unique_expressions_nodes_set.emplace(limit_by_node.get());
if (inserted)
@ -79,8 +79,6 @@ public:
query_limit_by_nodes = std::move(result_nodes);
}
unique_expressions_nodes_set.clear();
}
private:

View File

@ -6,6 +6,7 @@ namespace DB
{
/** Eliminate duplicate columns from ORDER BY and LIMIT BY.
*
* Example: SELECT * FROM test_table ORDER BY id, id;
* Result: SELECT * FROM test_table ORDER BY id;
*

View File

@ -15,7 +15,7 @@ namespace
class OrderByTupleEliminationVisitor : public InDepthQueryTreeVisitor<OrderByTupleEliminationVisitor>
{
public:
void visitImpl(QueryTreeNodePtr & node)
static void visitImpl(QueryTreeNodePtr & node)
{
auto * query_node = node->as<QueryNode>();
if (!query_node || !query_node->hasOrderBy())

View File

@ -6,6 +6,7 @@ namespace DB
{
/** Eliminate tuples from ORDER BY.
*
* Example: SELECT * FROM test_table ORDER BY (a, b);
* Result: SELECT * FROM test_table ORDER BY a, b;
*/

View File

@ -16,29 +16,34 @@ namespace DB
* as aggregate or non aggregate function.
* 4. All lambda expressions that are function arguments are resolved. Next passes can expect that LambaNode expression is resolved, and lambda has concrete arguments.
* 5. All standalone lambda expressions are resolved. Next passes can expect that there will be no standalone LambaNode expressions in query.
* 6. Constants are folded. Example: SELECT plus(1, 1). After step will be: SELECT 2.
* 6. Constants are folded. Example: SELECT plus(1, 1).
* Motivation for this, there are places in query tree that must contain constant:
* Function parameters Example: SELECT quantile(0.5)(x).
* Functions in which result type depends on constant expression. Example: cast(x, 'type_name').
* Expressions that are part of LIMIT. Example: SELECT * FROM test_table LIMIT expr.
* Function parameters. Example: SELECT quantile(0.5)(x).
* Functions in which result type depends on constant expression argument. Example: cast(x, 'type_name').
* Expressions that are part of LIMIT BY LIMIT, LIMIT BY OFFSET, LIMIT, OFFSET. Example: SELECT * FROM test_table LIMIT expr.
* Window function window frame OFFSET begin and OFFSET end.
*
* 7. All scalar subqueries are evaluated.
* TODO: Scalar subqueries must be evaluated only if they are part of query tree where we must have constant. This is currently not done
* because execution layer does not support scalar subqueries execution.
*
* 8. For query node projection columns are calculated. Later passes cannot change type, display name of projection column, and cannot add or remove
* 8. For query node.
*
* Projection columns are calculated. Later passes cannot change type, display name of projection column, and cannot add or remove
* columns in projection section.
* WITH and WINDOW sections are removed.
*
* 9. Query is validated. Parts that are validated:
*
* Constness of function parameters.
* Constness of LIMIT and OFFSET.
* Window functions frame. Constness of window functions frame begin OFFSET, end OFFSET.
* In SELECT, ORDER BY only columns that are specified in GROUP BY keys after GROUP BY are used.
* In query only columns that are specified in GROUP BY keys after GROUP BY are used.
* GROUPING function arguments are specified in GROUP BY keys.
* No GROUPING function if there is no GROUP BY.
* No aggregate functions in JOIN TREE, WHERE, PREWHERE, GROUP BY and inside another aggregate functions.
* GROUP BY modifiers CUBE, ROLLUP, GROUPING SETS and WITH TOTALS.
* Table expression modifiers are validated for table and table function nodes in JOIN TREE.
* Table expression modifiers are disabled for subqueries in JOIN TREE.
* For JOIN, ARRAY JOIN subqueries and table functions must have alias (Can be changed using joined_subquery_requires_alias setting).
*
@ -53,7 +58,7 @@ namespace DB
*
* For function `in` and its variations arguments are resolved, but sets are not build.
* If left and right arguments are constants constant folding is performed.
* If right argument resolved as table function, or table, and table is not of type Set, it is replaced with query that read only ordinary columns from underlying
* If right argument resolved as table, and table is not of type Set, it is replaced with query that read only ordinary columns from underlying
* storage.
* Example: SELECT id FROM test_table WHERE id IN test_table_other;
* Result: SELECT id FROM test_table WHERE id IN (SELECT test_table_column FROM test_table_other);

View File

@ -1,5 +1,8 @@
#include <Analyzer/Passes/SumIfToCountIfPass.h>
#include <DataTypes/DataTypesNumber.h>
#include <DataTypes/DataTypeNullable.h>
#include <AggregateFunctions/AggregateFunctionFactory.h>
#include <AggregateFunctions/IAggregateFunction.h>
@ -73,7 +76,7 @@ public:
if (!nested_function || nested_function->getFunctionName() != "if")
return;
auto nested_if_function_arguments_nodes = nested_function->getArguments().getNodes();
auto & nested_if_function_arguments_nodes = nested_function->getArguments().getNodes();
if (nested_if_function_arguments_nodes.size() != 3)
return;
@ -106,8 +109,13 @@ public:
/// Rewrite `sum(if(cond, 0, 1))` into `countIf(not(cond))`.
if (if_true_condition_value == 0 && if_false_condition_value == 1)
{
auto condition_result_type = nested_if_function_arguments_nodes[0]->getResultType();
DataTypePtr not_function_result_type = std::make_shared<DataTypeUInt8>();
if (condition_result_type->isNullable())
not_function_result_type = makeNullable(not_function_result_type);
auto not_function = std::make_shared<FunctionNode>("not");
resolveOrdinaryFunctionNode(*not_function, "not");
not_function->resolveAsFunction(FunctionFactory::instance().get("not", context), std::move(not_function_result_type));
auto & not_function_arguments = not_function->getArguments().getNodes();
not_function_arguments.push_back(std::move(nested_if_function_arguments_nodes[0]));
@ -121,13 +129,6 @@ public:
}
private:
inline void resolveOrdinaryFunctionNode(FunctionNode & function_node, const String & function_name) const
{
auto function_result_type = function_node.getResultType();
auto function = FunctionFactory::instance().get(function_name, context);
function_node.resolveAsFunction(function, std::move(function_result_type));
}
static inline void resolveAggregateFunctionNode(FunctionNode & function_node, const String & aggregate_function_name)
{
auto function_result_type = function_node.getResultType();

View File

@ -24,7 +24,7 @@ bool isUniqFunction(const String & function_name)
class UniqInjectiveFunctionsEliminationVisitor : public InDepthQueryTreeVisitor<UniqInjectiveFunctionsEliminationVisitor>
{
public:
void visitImpl(QueryTreeNodePtr & node)
static void visitImpl(QueryTreeNodePtr & node)
{
auto * function_node = node->as<FunctionNode>();
if (!function_node || !function_node->isAggregateFunction() || !isUniqFunction(function_node->getFunctionName()))
@ -48,7 +48,7 @@ public:
continue;
/// Replace injective function with its single argument
uniq_function_argument_node = std::move(uniq_function_argument_node_argument_nodes[0]);
uniq_function_argument_node = uniq_function_argument_node_argument_nodes[0];
}
}
};

View File

@ -374,7 +374,7 @@ QueryPipeline InterpreterExplainQuery::executeImpl()
case ASTExplainQuery::QueryTree:
{
if (ast.getExplainedQuery()->as<ASTSelectWithUnionQuery>() == nullptr)
throw Exception(ErrorCodes::INCORRECT_QUERY, "Only SELECT is supported for EXPLAIN QUERYTREE query");
throw Exception(ErrorCodes::INCORRECT_QUERY, "Only SELECT is supported for EXPLAIN QUERY TREE query");
auto settings = checkAndGetSettings<QueryTreeSettings>(ast.getSettings());
auto query_tree = buildQueryTree(ast.getExplainedQuery(), getContext());

View File

@ -112,8 +112,11 @@ void ASTColumnsListMatcher::formatImpl(const FormatSettings & settings, FormatSt
settings.ostr << ")";
/// Format column transformers
for (const auto & child : children)
size_t children_size = children.size();
for (size_t i = 1; i < children_size; ++i)
{
const auto & child = children[i];
settings.ostr << ' ';
child->formatImpl(settings, state, frame);
}
@ -174,8 +177,11 @@ void ASTQualifiedColumnsRegexpMatcher::formatImpl(const FormatSettings & setting
settings.ostr << ")";
/// Format column transformers
for (const auto & child : children)
size_t children_size = children.size();
for (size_t i = 1; i < children_size; ++i)
{
const auto & child = children[i];
settings.ostr << ' ';
child->formatImpl(settings, state, frame);
}
@ -223,9 +229,8 @@ void ASTQualifiedColumnsListMatcher::formatImpl(const FormatSettings & settings,
for (ASTs::const_iterator it = column_list->children.begin(); it != column_list->children.end(); ++it)
{
if (it != column_list->children.begin())
{
settings.ostr << ", ";
}
(*it)->formatImpl(settings, state, frame);
}
settings.ostr << ")";

View File

@ -92,9 +92,7 @@ bool ParserExplainQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected
if (select_p.parse(pos, query, expected))
explain_query->setExplainedQuery(std::move(query));
else
{
return false;
}
}
else if (kind == ASTExplainQuery::ExplainKind::CurrentTransaction)
{

View File

@ -4,4 +4,4 @@ endif()
if (ENABLE_EXAMPLES)
add_subdirectory(examples)
endif()
endif()

View File

@ -111,29 +111,34 @@ QueryPlan buildQueryPlanForTableExpression(QueryTreeNodePtr table_expression,
auto & query_context = planner_context->getQueryContext();
auto from_stage = storage->getQueryProcessingStage(query_context, select_query_options.to_stage, storage_snapshot, table_expression_query_info);
const auto & columns_names = table_expression_data.getColumnsNames();
Names column_names(columns_names.begin(), columns_names.end());
const auto & columns_names_set = table_expression_data.getColumnsNames();
Names columns_names(columns_names_set.begin(), columns_names_set.end());
/** The current user must have the SELECT privilege.
* We do not check access rights for table functions because they have beein already checked in ITablefunction::execute().
* We do not check access rights for table functions because they have been already checked in ITableFunction::execute().
*/
if (table_node)
checkAccessRights(*table_node, column_names, planner_context->getQueryContext());
{
auto column_names_with_aliases = columns_names;
const auto & alias_columns_names = table_expression_data.getAliasColumnsNames();
column_names_with_aliases.insert(column_names_with_aliases.end(), alias_columns_names.begin(), alias_columns_names.end());
checkAccessRights(*table_node, column_names_with_aliases, planner_context->getQueryContext());
}
if (column_names.empty())
if (columns_names.empty())
{
auto column_names_and_types = storage_snapshot->getColumns(GetColumnsOptions(GetColumnsOptions::All).withSubcolumns());
auto additional_column_to_read = column_names_and_types.front();
const auto & column_identifier = planner_context->getGlobalPlannerContext()->createColumnIdentifier(additional_column_to_read, table_expression);
column_names.push_back(additional_column_to_read.name);
columns_names.push_back(additional_column_to_read.name);
table_expression_data.addColumn(additional_column_to_read, column_identifier);
}
size_t max_block_size = query_context->getSettingsRef().max_block_size;
size_t max_streams = query_context->getSettingsRef().max_threads;
bool need_rewrite_query_with_final = storage->needRewriteQueryWithFinal(column_names);
bool need_rewrite_query_with_final = storage->needRewriteQueryWithFinal(columns_names);
if (need_rewrite_query_with_final)
{
if (table_expression_query_info.table_expression_modifiers)
@ -154,12 +159,12 @@ QueryPlan buildQueryPlanForTableExpression(QueryTreeNodePtr table_expression,
}
}
storage->read(query_plan, column_names, storage_snapshot, table_expression_query_info, query_context, from_stage, max_block_size, max_streams);
storage->read(query_plan, columns_names, storage_snapshot, table_expression_query_info, query_context, from_stage, max_block_size, max_streams);
/// Create step which reads from empty source if storage has no data.
if (!query_plan.isInitialized())
{
auto source_header = storage_snapshot->getSampleBlockForColumns(column_names);
auto source_header = storage_snapshot->getSampleBlockForColumns(columns_names);
Pipe pipe(std::make_shared<NullSource>(source_header));
auto read_from_pipe = std::make_unique<ReadFromPreparedSource>(std::move(pipe));
read_from_pipe->setStepDescription("Read from NullSource");
@ -181,11 +186,15 @@ QueryPlan buildQueryPlanForTableExpression(QueryTreeNodePtr table_expression,
auto rename_actions_dag = std::make_shared<ActionsDAG>(query_plan.getCurrentDataStream().header.getColumnsWithTypeAndName());
for (const auto & [column_name, column_identifier] : table_expression_data.getColumnNameToIdentifier())
for (auto & output_node : rename_actions_dag->getOutputs())
{
auto position = query_plan.getCurrentDataStream().header.getPositionByName(column_name);
const auto * node_to_rename = rename_actions_dag->getOutputs()[position];
rename_actions_dag->getOutputs()[position] = &rename_actions_dag->addAlias(*node_to_rename, column_identifier);
const auto * column_identifier = table_expression_data.getColumnIdentifierOrNull(output_node->result_name);
if (!column_identifier)
continue;
const auto * node_to_rename = output_node;
output_node = &rename_actions_dag->addAlias(*node_to_rename, *column_identifier);
}
auto rename_step = std::make_unique<ExpressionStep>(query_plan.getCurrentDataStream(), rename_actions_dag);
@ -267,13 +276,13 @@ QueryPlan buildQueryPlanForJoinNode(QueryTreeNodePtr join_tree_node,
const auto & join_node_using_column_node_type = join_node_using_column_node.getColumnType();
if (!left_inner_column.getColumnType()->equals(*join_node_using_column_node_type))
{
auto left_inner_column_identifier = planner_context->getColumnNodeIdentifierOrThrow(left_inner_column_node);
const auto & left_inner_column_identifier = planner_context->getColumnNodeIdentifierOrThrow(left_inner_column_node);
left_plan_column_name_to_cast_type.emplace(left_inner_column_identifier, join_node_using_column_node_type);
}
if (!right_inner_column.getColumnType()->equals(*join_node_using_column_node_type))
{
auto right_inner_column_identifier = planner_context->getColumnNodeIdentifierOrThrow(right_inner_column_node);
const auto & right_inner_column_identifier = planner_context->getColumnNodeIdentifierOrThrow(right_inner_column_node);
right_plan_column_name_to_cast_type.emplace(right_inner_column_identifier, join_node_using_column_node_type);
}
}
@ -468,18 +477,12 @@ QueryPlan buildQueryPlanForJoinNode(QueryTreeNodePtr join_tree_node,
for (auto & join_using_node : using_list.getNodes())
{
auto & join_using_column_node = join_using_node->as<ColumnNode &>();
if (!join_using_column_node.getExpression() ||
join_using_column_node.getExpression()->getNodeType() != QueryTreeNodeType::LIST)
throw Exception(ErrorCodes::LOGICAL_ERROR,
"JOIN {} column in USING does not have inner columns",
join_node.formatASTForErrorMessage());
auto & using_join_columns_list = join_using_column_node.getExpression()->as<ListNode &>();
auto & using_join_columns_list = join_using_column_node.getExpressionOrThrow()->as<ListNode &>();
auto & using_join_left_join_column_node = using_join_columns_list.getNodes().at(0);
auto & using_join_right_join_column_node = using_join_columns_list.getNodes().at(1);
auto left_column_identifier = planner_context->getColumnNodeIdentifierOrThrow(using_join_left_join_column_node);
auto right_column_identifier = planner_context->getColumnNodeIdentifierOrThrow(using_join_right_join_column_node);
const auto & left_column_identifier = planner_context->getColumnNodeIdentifierOrThrow(using_join_left_join_column_node);
const auto & right_column_identifier = planner_context->getColumnNodeIdentifierOrThrow(using_join_right_join_column_node);
table_join_clause.key_names_left.push_back(left_column_identifier);
table_join_clause.key_names_right.push_back(right_column_identifier);
@ -627,13 +630,13 @@ QueryPlan buildQueryPlanForArrayJoinNode(QueryTreeNodePtr table_expression,
{
auto & array_join_node = table_expression->as<ArrayJoinNode &>();
auto left_plan = buildQueryPlanForJoinTreeNode(array_join_node.getTableExpression(),
auto plan = buildQueryPlanForJoinTreeNode(array_join_node.getTableExpression(),
select_query_info,
select_query_options,
planner_context);
auto left_plan_output_columns = left_plan.getCurrentDataStream().header.getColumnsWithTypeAndName();
auto plan_output_columns = plan.getCurrentDataStream().header.getColumnsWithTypeAndName();
ActionsDAGPtr array_join_action_dag = std::make_shared<ActionsDAG>(left_plan_output_columns);
ActionsDAGPtr array_join_action_dag = std::make_shared<ActionsDAG>(plan_output_columns);
PlannerActionsVisitor actions_visitor(planner_context);
NameSet array_join_columns;
@ -652,16 +655,16 @@ QueryPlan buildQueryPlanForArrayJoinNode(QueryTreeNodePtr table_expression,
}
array_join_action_dag->projectInput();
auto array_join_actions = std::make_unique<ExpressionStep>(left_plan.getCurrentDataStream(), array_join_action_dag);
auto array_join_actions = std::make_unique<ExpressionStep>(plan.getCurrentDataStream(), array_join_action_dag);
array_join_actions->setStepDescription("ARRAY JOIN actions");
left_plan.addStep(std::move(array_join_actions));
plan.addStep(std::move(array_join_actions));
auto array_join_action = std::make_shared<ArrayJoinAction>(array_join_columns, array_join_node.isLeft(), planner_context->getQueryContext());
auto array_join_step = std::make_unique<ArrayJoinStep>(left_plan.getCurrentDataStream(), std::move(array_join_action));
auto array_join_step = std::make_unique<ArrayJoinStep>(plan.getCurrentDataStream(), std::move(array_join_action));
array_join_step->setStepDescription("ARRAY JOIN");
left_plan.addStep(std::move(array_join_step));
plan.addStep(std::move(array_join_step));
return left_plan;
return plan;
}
}
@ -675,13 +678,13 @@ QueryPlan buildQueryPlanForJoinTreeNode(QueryTreeNodePtr join_tree_node,
switch (join_tree_node_type)
{
case QueryTreeNodeType::QUERY:
[[fallthrough]];
case QueryTreeNodeType::UNION:
[[fallthrough]];
case QueryTreeNodeType::TABLE:
[[fallthrough]];
case QueryTreeNodeType::TABLE_FUNCTION:
[[fallthrough]];
case QueryTreeNodeType::QUERY:
[[fallthrough]];
case QueryTreeNodeType::UNION:
{
return buildQueryPlanForTableExpression(join_tree_node, select_query_info, select_query_options, planner_context);
}
@ -696,7 +699,7 @@ QueryPlan buildQueryPlanForJoinTreeNode(QueryTreeNodePtr join_tree_node,
default:
{
throw Exception(ErrorCodes::LOGICAL_ERROR,
"Expected query, table, table function, join or array join query node. Actual {}",
"Expected table, table function, query, union, join or array join query node. Actual {}",
join_tree_node->formatASTForErrorMessage());
}
}

View File

@ -75,7 +75,7 @@ void JoinClause::dump(WriteBuffer & buffer) const
buffer << " left_condition_nodes: " + dump_dag_nodes(left_filter_condition_nodes);
if (!right_filter_condition_nodes.empty())
buffer << " left_condition_nodes: " + dump_dag_nodes(right_filter_condition_nodes);
buffer << " right_condition_nodes: " + dump_dag_nodes(right_filter_condition_nodes);
}
String JoinClause::dump() const
@ -190,7 +190,7 @@ void buildJoinClause(ActionsDAGPtr join_expression_dag,
throw Exception(ErrorCodes::INVALID_JOIN_ON_EXPRESSION,
"JOIN {} ON expression {} with constants is not supported",
join_node.formatASTForErrorMessage(),
join_expressions_actions_node->function->getName());
join_expressions_actions_node->result_name);
}
else if (left_expression_side_optional && !right_expression_side_optional)
{
@ -210,7 +210,7 @@ void buildJoinClause(ActionsDAGPtr join_expression_dag,
const ActionsDAG::Node * left_key = left_child;
const ActionsDAG::Node * right_key = right_child;
if (left_expression_side_optional == JoinTableSide::Right)
if (left_expression_side == JoinTableSide::Right)
{
left_key = right_child;
right_key = left_child;
@ -254,7 +254,6 @@ void buildJoinClause(ActionsDAGPtr join_expression_dag,
join_node.formatASTForErrorMessage());
auto expression_side = *expression_side_optional;
join_clause.addCondition(expression_side, join_expressions_actions_node);
}
@ -269,7 +268,7 @@ JoinClausesAndActions buildJoinClausesAndActions(const ColumnsWithTypeAndName &
/** In ActionsDAG if input node has constant representation additional constant column is added.
* That way we cannot simply check that node has INPUT type during resolution of expression join table side.
* Put all nodes after actions dag initialization in set.
* To check if actions dag node is input column, we set contains node.
* To check if actions dag node is input column, we check if set contains it.
*/
const auto & join_expression_actions_nodes = join_expression_actions->getNodes();
@ -375,9 +374,7 @@ JoinClausesAndActions buildJoinClausesAndActions(const ColumnsWithTypeAndName &
else
dag_filter_condition_node = left_filter_condition_nodes[0];
join_clause.getLeftFilterConditionNodes().clear();
join_clause.addCondition(JoinTableSide::Left, dag_filter_condition_node);
join_clause.getLeftFilterConditionNodes() = {dag_filter_condition_node};
join_expression_actions->addOrReplaceInOutputs(*dag_filter_condition_node);
add_necessary_name_if_needed(JoinTableSide::Left, dag_filter_condition_node->result_name);
@ -393,9 +390,7 @@ JoinClausesAndActions buildJoinClausesAndActions(const ColumnsWithTypeAndName &
else
dag_filter_condition_node = right_filter_condition_nodes[0];
join_clause.getRightFilterConditionNodes().clear();
join_clause.addCondition(JoinTableSide::Right, dag_filter_condition_node);
join_clause.getRightFilterConditionNodes() = {dag_filter_condition_node};
join_expression_actions->addOrReplaceInOutputs(*dag_filter_condition_node);
add_necessary_name_if_needed(JoinTableSide::Right, dag_filter_condition_node->result_name);
@ -541,7 +536,8 @@ void trySetStorageInTableJoin(const QueryTreeNodePtr & table_expression, std::sh
else if (auto * table_function = table_expression->as<TableFunctionNode>())
storage = table_function->getStorage();
if (auto storage_join = std::dynamic_pointer_cast<StorageJoin>(storage); storage_join)
auto storage_join = std::dynamic_pointer_cast<StorageJoin>(storage);
if (storage_join)
{
table_join->setStorageJoin(storage_join);
return;
@ -581,7 +577,9 @@ std::shared_ptr<DirectKeyValueJoin> tryDirectJoin(const std::shared_ptr<TableJoi
clauses[0].key_names_left.size() == 1 &&
clauses[0].key_names_right.size() == 1 &&
!clauses[0].on_filter_condition_left &&
!clauses[0].on_filter_condition_right;
!clauses[0].on_filter_condition_right &&
clauses[0].analyzer_left_filter_condition_column_name.empty() &&
clauses[0].analyzer_right_filter_condition_column_name.empty();
if (!only_one_key)
return {};
@ -604,7 +602,7 @@ std::shared_ptr<DirectKeyValueJoin> tryDirectJoin(const std::shared_ptr<TableJoi
* CREATE DICTIONARY test_dictionary (id UInt64, value String) PRIMARY KEY id SOURCE(CLICKHOUSE(TABLE 'test_dictionary_table')) LIFETIME(0);
* SELECT t1.id FROM test_table AS t1 INNER JOIN test_dictionary AS t2 ON t1.id = t2.id;
*
* Unique execution name for `id` column in right table expression `test_dictionary AS t2` for example can be `t2.id_0`.
* Unique execution name for `id` column from right table expression `test_dictionary AS t2` for example can be `t2.id_0`.
* Storage column name is `id`.
*
* Here we create header for right table expression with original storage column names.
@ -652,7 +650,7 @@ std::shared_ptr<IJoin> chooseJoinAlgorithm(std::shared_ptr<TableJoin> & table_jo
if (!table_join->oneDisjunct() && !table_join->isEnabledAlgorithm(JoinAlgorithm::HASH))
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Only `hash` join supports multiple ORs for keys in JOIN ON section");
/// Direct JOIN with special storages that support key value access. For example JOIN with Dictionary.
/// Direct JOIN with special storages that support key value access. For example JOIN with Dictionary
if (table_join->isEnabledAlgorithm(JoinAlgorithm::DIRECT))
{
JoinPtr direct_join = tryDirectJoin(table_join, right_table_expression, right_table_expression_header, planner_context);

View File

@ -31,7 +31,7 @@ namespace DB
* Example: SELECT * FROM test_table_1 AS t1 INNER JOIN test_table_2 AS t2 ON toString(t1.id) = toString(t2.id).
* toString(t1.id) = toString(t2.id) is JOIN keys section. Where toString(t1.id) is left key, and toString(t2.id) is right key.
*
* During query planning JOIN ON section must be represented using join clause structure. It is important to split
* During query planning JOIN ON section represented using join clause structure. It is important to split
* keys and conditions. And for each action detect from which stream it can be performed.
*
* We have 2 streams, left stream and right stream.
@ -79,26 +79,16 @@ public:
return left_key_nodes;
}
/// Get right key nodes
const ActionsDAG::NodeRawConstPtrs & getRightKeyNodes() const
{
return right_key_nodes;
}
/// Get left key nodes
ActionsDAG::NodeRawConstPtrs & getLeftKeyNodes()
{
return left_key_nodes;
}
bool hasASOF() const
/// Get right key nodes
const ActionsDAG::NodeRawConstPtrs & getRightKeyNodes() const
{
return !asof_conditions.empty();
}
const std::vector<ASOFCondition> & getASOFConditions() const
{
return asof_conditions;
return right_key_nodes;
}
/// Get right key nodes
@ -107,23 +97,36 @@ public:
return right_key_nodes;
}
/// Returns true if JOIN clause has ASOF conditions, false otherwise
bool hasASOF() const
{
return !asof_conditions.empty();
}
/// Get ASOF conditions
const std::vector<ASOFCondition> & getASOFConditions() const
{
return asof_conditions;
}
/// Get left filter condition nodes
const ActionsDAG::NodeRawConstPtrs & getLeftFilterConditionNodes() const
{
return left_filter_condition_nodes;
}
/// Get left filter condition nodes
ActionsDAG::NodeRawConstPtrs & getLeftFilterConditionNodes()
{
return left_filter_condition_nodes;
}
/// Get right filter condition nodes
const ActionsDAG::NodeRawConstPtrs & getRightFilterConditionNodes() const
{
return right_filter_condition_nodes;
}
ActionsDAG::NodeRawConstPtrs & getLeftFilterConditionNodes()
{
return left_filter_condition_nodes;
}
/// Get right filter condition nodes
ActionsDAG::NodeRawConstPtrs & getRightFilterConditionNodes()
{
@ -183,7 +186,7 @@ std::optional<bool> tryExtractConstantFromJoinNode(const QueryTreeNodePtr & join
/** Choose JOIN algorithm for table join, right table expression, right table expression header and planner context.
* Table join structure can be modified during JOIN algorithm choosing for special JOIN algorithms.
* For example JOIN with Dictionary enigne, or JOIN with JOIN engine.
* For example JOIN with Dictionary engine, or JOIN with JOIN engine.
*/
std::shared_ptr<IJoin> chooseJoinAlgorithm(std::shared_ptr<TableJoin> & table_join,
const QueryTreeNodePtr & right_table_expression,

View File

@ -24,7 +24,7 @@ namespace
std::pair<Field, DataTypePtr> extractWithFillValue(const QueryTreeNodePtr & node)
{
auto constant_value = node->getConstantValue();
const auto & constant_value = node->getConstantValue();
std::pair<Field, DataTypePtr> result;
result.first = constant_value.getValue();
@ -38,7 +38,7 @@ std::pair<Field, DataTypePtr> extractWithFillValue(const QueryTreeNodePtr & node
std::pair<Field, std::optional<IntervalKind>> extractWithFillStepValue(const QueryTreeNodePtr & node)
{
auto constant_value = node->getConstantValue();
const auto & constant_value = node->getConstantValue();
const auto & constant_node_result_type = constant_value.getType();
if (const auto * type_interval = typeid_cast<const DataTypeInterval *>(constant_node_result_type.get()))
@ -76,7 +76,8 @@ FillColumnDescription extractWithFillDescription(const SortNode & sort_node)
}
else
{
fill_column_description.fill_step = Field(sort_node.getSortDirection() == SortDirection::ASCENDING ? 1 : -1);
auto direction_value = sort_node.getSortDirection() == SortDirection::ASCENDING ? static_cast<Int64>(1) : static_cast<Int64>(-1);
fill_column_description.fill_step = Field(direction_value);
}
if (applyVisitor(FieldVisitorAccurateEquals(), fill_column_description.fill_step, Field{0}))

View File

@ -85,8 +85,10 @@ std::vector<WindowDescription> extractWindowDescriptions(const QueryTreeNodes &
}
const auto & arguments_nodes = window_function_node_typed.getArguments().getNodes();
window_function.argument_names.reserve(arguments_nodes.size());
window_function.argument_types.reserve(arguments_nodes.size());
size_t arguments_nodes_size = arguments_nodes.size();
window_function.argument_names.reserve(arguments_nodes_size);
window_function.argument_types.reserve(arguments_nodes_size);
for (const auto & argument_node : arguments_nodes)
{

View File

@ -9,10 +9,10 @@
namespace DB
{
/// Extract and sort window description from query.
/// Extract window descriptions from window function nodes
std::vector<WindowDescription> extractWindowDescriptions(const QueryTreeNodes & window_function_nodes, const PlannerContext & planner_context);
/** Try to sort window description in such an order that the window with the longest
/** Try to sort window descriptions in such an order that the window with the longest
* sort description goes first, and all window that use its prefixes follow.
*/
void sortWindowDescriptions(std::vector<WindowDescription> & window_descriptions);

View File

@ -22,7 +22,7 @@ class TableExpressionData
public:
using ColumnNameToColumnIdentifier = std::unordered_map<std::string, ColumnIdentifier>;
using ColumnIdentifierToColumnName = std::unordered_map<std::string, ColumnIdentifier>;
using ColumnIdentifierToColumnName = std::unordered_map<ColumnIdentifier, std::string>;
/// Return true if column with name exists, false otherwise
bool hasColumn(const std::string & column_name) const
@ -32,6 +32,8 @@ public:
/** Add column in table expression data.
* Column identifier must be created using global planner context.
*
* Logical error exception is thrown if column already exists.
*/
void addColumn(const NameAndTypePair & column, const ColumnIdentifier & column_identifier)
{
@ -64,13 +66,13 @@ public:
alias_columns_names.insert(column_name);
}
/// Get alias column names
/// Get alias columns names
const NameSet & getAliasColumnsNames() const
{
return alias_columns_names;
}
/// Get column names
/// Get columns names
const NameSet & getColumnsNames() const
{
return columns_names;
@ -146,7 +148,7 @@ public:
return &it->second;
}
/** Cache value of storage is remote method call.
/** Returns true if storage is remote, false otherwise.
*
* Valid only for table and table function node.
*/
@ -155,29 +157,29 @@ public:
return is_remote;
}
/// Set is remote value
/// Set is storage remote value
void setIsRemote(bool is_remote_value)
{
is_remote = is_remote_value;
}
private:
/// Valid for table, table function, query table expression nodes
/// Valid for table, table function, query, union table expression nodes
NamesAndTypesList columns;
/// Valid for table, table function, query table expression nodes
/// Valid for table, table function, query, union table expression nodes
NameSet columns_names;
/// Valid only for table table expression node
NameSet alias_columns_names;
/// Valid for table, table function, query table expression nodes
/// Valid for table, table function, query, union table expression nodes
ColumnNameToColumnIdentifier column_name_to_column_identifier;
/// Valid for table, table function, query table expression nodes
/// Valid for table, table function, query, union table expression nodes
ColumnIdentifierToColumnName column_identifier_to_column_name;
/// Cached value if table expression receives data from remote server
/// Is storage remote
bool is_remote = false;
};

View File

@ -32,6 +32,7 @@ String dumpQueryPlan(QueryPlan & query_plan)
{
WriteBufferFromOwnString query_plan_buffer;
query_plan.explainPlan(query_plan_buffer, QueryPlan::ExplainPlanOptions{true, true, true, true});
return query_plan_buffer.str();
}
@ -40,6 +41,7 @@ String dumpQueryPipeline(QueryPlan & query_plan)
QueryPlan::ExplainPipelineOptions explain_pipeline;
WriteBufferFromOwnString query_pipeline_buffer;
query_plan.explainPipeline(query_pipeline_buffer, explain_pipeline);
return query_pipeline_buffer.str();
}
@ -47,26 +49,26 @@ Block buildCommonHeaderForUnion(const Blocks & queries_headers)
{
size_t num_selects = queries_headers.size();
Block common_header = queries_headers.front();
size_t num_columns = common_header.columns();
size_t columns_size = common_header.columns();
for (size_t query_num = 1; query_num < num_selects; ++query_num)
for (size_t query_number = 1; query_number < num_selects; ++query_number)
{
if (queries_headers.at(query_num).columns() != num_columns)
if (queries_headers.at(query_number).columns() != columns_size)
throw Exception(ErrorCodes::TYPE_MISMATCH,
"Different number of columns in UNION elements: {} and {}",
common_header.dumpNames(),
queries_headers[query_num].dumpNames());
queries_headers[query_number].dumpNames());
}
std::vector<const ColumnWithTypeAndName *> columns(num_selects);
for (size_t column_num = 0; column_num < num_columns; ++column_num)
for (size_t column_number = 0; column_number < columns_size; ++column_number)
{
for (size_t i = 0; i < num_selects; ++i)
columns[i] = &queries_headers[i].getByPosition(column_num);
columns[i] = &queries_headers[i].getByPosition(column_number);
ColumnWithTypeAndName & result_elem = common_header.getByPosition(column_num);
result_elem = getLeastSuperColumn(columns);
ColumnWithTypeAndName & result_element = common_header.getByPosition(column_number);
result_element = getLeastSuperColumn(columns);
}
return common_header;
@ -175,20 +177,18 @@ ActionsDAGPtr buildActionsDAGFromExpressionNode(const QueryTreeNodePtr & express
ActionsDAGPtr action_dag = std::make_shared<ActionsDAG>(input_columns);
PlannerActionsVisitor actions_visitor(planner_context);
auto expression_dag_index_nodes = actions_visitor.visit(action_dag, expression_node);
action_dag->getOutputs().clear();
for (auto & expression_dag_index_node : expression_dag_index_nodes)
action_dag->getOutputs().push_back(expression_dag_index_node);
action_dag->getOutputs() = std::move(expression_dag_index_nodes);
return action_dag;
}
bool sortDescriptionIsPrefix(const SortDescription & prefix, const SortDescription & full)
{
if (prefix.size() > full.size())
size_t prefix_size = prefix.size();
if (prefix_size > full.size())
return false;
for (size_t i = 0; i < prefix.size(); ++i)
for (size_t i = 0; i < prefix_size; ++i)
{
if (full[i] != prefix[i])
return false;
@ -204,7 +204,7 @@ bool queryHasArrayJoinInJoinTree(const QueryTreeNodePtr & query_node)
std::vector<QueryTreeNodePtr> join_tree_nodes_to_process;
join_tree_nodes_to_process.push_back(query_node_typed.getJoinTree());
while (join_tree_nodes_to_process.empty())
while (!join_tree_nodes_to_process.empty())
{
auto join_tree_node_to_process = join_tree_nodes_to_process.back();
join_tree_nodes_to_process.pop_back();
@ -253,7 +253,7 @@ bool queryHasWithTotalsInAnySubqueryInJoinTree(const QueryTreeNodePtr & query_no
std::vector<QueryTreeNodePtr> join_tree_nodes_to_process;
join_tree_nodes_to_process.push_back(query_node_typed.getJoinTree());
while (join_tree_nodes_to_process.empty())
while (!join_tree_nodes_to_process.empty())
{
auto join_tree_node_to_process = join_tree_nodes_to_process.back();
join_tree_nodes_to_process.pop_back();

View File

@ -48,7 +48,7 @@ ActionsDAGPtr buildActionsDAGFromExpressionNode(const QueryTreeNodePtr & express
/// Returns true if prefix sort description is prefix of full sort descriptor, false otherwise
bool sortDescriptionIsPrefix(const SortDescription & prefix, const SortDescription & full);
/// Returns true if query node JOIN TREE contains ARRAY JOIN node
/// Returns true if query node JOIN TREE contains ARRAY JOIN node, false otherwise
bool queryHasArrayJoinInJoinTree(const QueryTreeNodePtr & query_node);
/** Returns true if query node JOIN TREE contains QUERY node with WITH TOTALS, false otherwise.

View File

@ -490,12 +490,8 @@ MergeTreeDataSelectSamplingData MergeTreeDataSelectExecutor::getSampling(
{
const auto & table_expression_modifiers = *select_query_info.table_expression_modifiers;
final = table_expression_modifiers.hasFinal();
if (table_expression_modifiers.hasSampleSizeRatio())
sample_size_ratio = table_expression_modifiers.getSampleSizeRatio();
if (table_expression_modifiers.hasSampleOffsetRatio())
sample_offset_ratio = table_expression_modifiers.getSampleSizeRatio();
sample_size_ratio = table_expression_modifiers.getSampleSizeRatio();
sample_offset_ratio = table_expression_modifiers.getSampleOffsetRatio();
}
else
{