Skip to content

Commit

Permalink
fix: restrict number of elements in some syntax.
Browse files Browse the repository at this point in the history
  • Loading branch information
ashigeru committed Aug 20, 2024
1 parent 78f5c00 commit 2a41119
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 0 deletions.
3 changes: 3 additions & 0 deletions include/mizugaki/analyzer/sql_analyzer_code.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ enum class sql_analyzer_code {
ambiguous_type,
/// @brief the set of type input is inconsistent for this operation.
inconsistent_type,
/// @brief the declared elements exceeds the limit of count.
exceed_number_of_elements,
/// @brief the referring variable is not resolved (internal error).
unresolved_variable,
/// @brief the number of values is wrong (internal error).
Expand Down Expand Up @@ -188,6 +190,7 @@ inline constexpr std::string_view to_string_view(sql_analyzer_code value) noexce

case kind::ambiguous_type: return "ambiguous_type"sv;
case kind::inconsistent_type: return "inconsistent_type"sv;
case kind::exceed_number_of_elements: return "exceed_number_of_elements"sv;
case kind::unresolved_variable: return "unresolved_variable"sv;
case kind::inconsistent_elements: return "inconsistent_elements"sv;
}
Expand Down
102 changes: 102 additions & 0 deletions include/mizugaki/analyzer/sql_analyzer_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,37 @@ class sql_analyzer_options {
*/
static constexpr std::string_view default_advance_sequence_function_name { "nextval" };

/**
* @brief the default value of maximum number of columns in table definition.
* @see max_max_table_columns()
*/
static constexpr size_type default_max_table_columns = 1'000;

/**
* @brief the default value of maximum number of columns in the select list.
* @see max_select_elements()
*/
static constexpr size_type default_max_select_elements = 1'000;

/**
* @brief the default value of maximum number of rows in table value constructor.
* @see max_table_value_constructor_rows()
*/
static constexpr size_type default_max_table_value_constructor_rows = 1'000;

/**
* @brief the default value of maximum number of columns in row value constructor.
* @see max_row_value_constructor_columns()
*/
static constexpr size_type default_max_row_value_constructor_columns = 1'000;

/**
* @brief the default value of maximum number of alternatives in case expressions.
* @see max_case_alternatives()
* @note this also restricts the number of alternatives of `COALESCE`.
*/
static constexpr size_type default_max_case_alternatives = 300;

/**
* @brief the zone offset unit type.
*/
Expand Down Expand Up @@ -483,6 +514,71 @@ class sql_analyzer_options {
return system_zone_offset_;
}

/**
* @brief returns the maximum number of columns in table definition.
* @return the max number of columns in table definition
*/
[[nodiscard]] size_type& max_table_columns() noexcept {
return max_table_columns_;
}

/// @copydoc max_table_columns()
[[nodiscard]] size_type const& max_table_columns() const noexcept {
return max_table_columns_;
}

/**
* @brief returns the maximum number of columns in the select list.
* @return the max number of columns in the select list
*/
[[nodiscard]] size_type& max_select_elements() noexcept {
return max_select_elements_;
}

/// @copydoc max_select_elements()
[[nodiscard]] size_type const& max_select_elements() const noexcept {
return max_select_elements_;
}

/**
* @brief returns the maximum number of rows in table value constructor.
* @return the max number of rows in table value constructor
*/
[[nodiscard]] size_type& max_table_value_constructor_rows() noexcept {
return max_table_value_constructor_rows_;
}

/// @copydoc max_table_value_constructor_rows()
[[nodiscard]] size_type const& max_table_value_constructor_rows() const noexcept {
return max_table_value_constructor_rows_;
}

/**
* @brief returns the maximum number of columns in row value constructor.
* @return the max number of columns in row value constructor
*/
[[nodiscard]] size_type& max_row_value_constructor_columns() noexcept {
return max_row_value_constructor_columns_;
}

/// @copydoc max_row_value_constructor_columns()
[[nodiscard]] size_type const& max_row_value_constructor_columns() const noexcept {
return max_row_value_constructor_columns_;
}

/**
* @brief returns the maximum number of alternatives in case expressions.
* @return the max number of alternatives in case expressions
*/
[[nodiscard]] size_type& max_case_alternatives() noexcept {
return max_case_alternatives_;
}

/// @copydoc max_case_alternatives()
[[nodiscard]] size_type const& max_case_alternatives() const noexcept {
return max_case_alternatives_;
}

private:
::takatori::util::maybe_shared_ptr<::yugawara::schema::catalog const> catalog_;
::takatori::util::maybe_shared_ptr<::yugawara::schema::search_path const> schema_search_path_;
Expand Down Expand Up @@ -511,6 +607,12 @@ class sql_analyzer_options {

std::string_view advance_sequence_function_name_ { default_advance_sequence_function_name };
zone_offset_type system_zone_offset_ { default_system_zone_offset };

size_type max_table_columns_ { default_max_table_columns };
size_type max_select_elements_ { default_max_select_elements };
size_type max_table_value_constructor_rows_ { default_max_table_value_constructor_rows };
size_type max_row_value_constructor_columns_ { default_max_row_value_constructor_columns };
size_type max_case_alternatives_ { default_max_case_alternatives };
};

} // namespace mizugaki::analyzer
33 changes: 33 additions & 0 deletions src/mizugaki/analyzer/details/analyze_query_expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,16 @@ class engine {
relation_info info;
optional_ptr<trelation::project> select_columns {};
{
if (expr.elements().size() > context_.options()->max_select_elements()) {
context_.report(
sql_analyzer_code::exceed_number_of_elements,
string_builder {}
<< "too many select elements: "
<< "must be less than or equal to " << context_.options()->max_select_elements()
<< string_builder::to_string,
expr.region());
return {};
}
auto&& project = graph_.emplace<trelation::project>(std::vector<trelation::project::column> {});
project.columns().reserve(std::min(expr.elements().size(), std::size_t { 8 }));
if (!expr.elements().empty()) {
Expand Down Expand Up @@ -424,10 +434,33 @@ class engine {
ast::query::table_value_constructor const& expr,
optional_ptr<query_scope const> const& parent,
row_value_context const& val) {
if (expr.elements().size() > context_.options()->max_table_value_constructor_rows()) {
context_.report(
sql_analyzer_code::exceed_number_of_elements,
string_builder {}
<< "too many table value constructor elements: "
<< "must be less than or equal to "
<< context_.options()->max_table_value_constructor_rows()
<< string_builder::to_string,
expr.region());
return {};
}

// compute row values only which is a row value constructor
auto rows = create_vector<trelation::values::row>(expr.elements().size());
for (auto&& row_value : expr.elements()) {
if (auto row_ctor = as_row_value_constructor(*row_value)) {
if (row_ctor->elements().size() > context_.options()->max_row_value_constructor_columns()) {
context_.report(
sql_analyzer_code::exceed_number_of_elements,
string_builder {}
<< "too many row value constructor elements: "
<< "must be less than or equal to "
<< context_.options()->max_row_value_constructor_columns()
<< string_builder::to_string,
row_ctor->region());
return {};
}
auto row = create_ref_vector<tscalar::expression>(row_ctor->elements().size());
std::size_t index = 0;
for (auto&& elem_expr : row_ctor->elements()) {
Expand Down
31 changes: 31 additions & 0 deletions src/mizugaki/analyzer/details/analyze_scalar_expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,16 @@ class engine {
expr.region());
return {};
}
if (expr.when_clauses().size() > context_.options()->max_case_alternatives()) {
context_.report(
sql_analyzer_code::exceed_number_of_elements,
string_builder {}
<< "too many alternatives in CASE expression: "
<< "must be less than or equal to " << context_.options()->max_case_alternatives()
<< string_builder::to_string,
expr.region());
return {};
}

std::optional<tscalar::let::variable> variable {};
if (expr.operand()) {
Expand Down Expand Up @@ -610,6 +620,17 @@ class engine {
values.region());
return {};
}
if (values.elements().size() > context_.options()->max_table_value_constructor_rows()) {
context_.report(
sql_analyzer_code::exceed_number_of_elements,
string_builder {}
<< "IN predicate with too many elements: "
<< "must be less than or equal to "
<< context_.options()->max_table_value_constructor_rows()
<< string_builder::to_string,
values.region());
return {};
}

auto left = process(*expr.left(), {});
if (!left) {
Expand Down Expand Up @@ -822,6 +843,16 @@ class engine {
expr.region());
return {};
}
if (expr.arguments().size() > context_.options()->max_case_alternatives()) {
context_.report(
sql_analyzer_code::exceed_number_of_elements,
string_builder {}
<< "too many alternatives in COALESCE expression: "
<< "must be less than or equal to " << context_.options()->max_case_alternatives()
<< string_builder::to_string,
expr.region());
return {};
}

::takatori::util::reference_vector<tscalar::expression> operands {};
operands.reserve(expr.arguments().size());
Expand Down
11 changes: 11 additions & 0 deletions src/mizugaki/analyzer/details/analyze_statement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,17 @@ class engine {
}
}
table_columns.push_back(std::move(prototype));
if (table_columns.size() > context_.options()->max_table_columns()) {
context_.report(
sql_analyzer_code::exceed_number_of_elements,
string_builder {}
<< "too many columns in table definition: "
<< table_name << " - "
<< "must be less than or equal to " << context_.options()->max_table_columns()
<< string_builder::to_string,
stmt.region());
return {};
}
}
if (table_columns.empty()) {
context_.report(
Expand Down

0 comments on commit 2a41119

Please sign in to comment.