Skip to content

Commit

Permalink
feat: introduce limits for repeated elements in grammar.
Browse files Browse the repository at this point in the history
  • Loading branch information
ashigeru committed Aug 20, 2024
1 parent f5c9637 commit 23f6a28
Show file tree
Hide file tree
Showing 12 changed files with 833 additions and 39 deletions.
25 changes: 11 additions & 14 deletions include/mizugaki/parser/sql_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <takatori/util/maybe_shared_ptr.h>

#include "sql_parser_options.h"
#include "sql_parser_result.h"

namespace mizugaki::parser {
Expand All @@ -21,23 +22,20 @@ class sql_parser {
/// @brief the result type.
using result_type = sql_parser_result;

/// @brief default number of next token candidates to display on error.
static constexpr std::size_t default_max_expected_candidates = 5;

/**
* @brief sets the max number of next token candidates to display on error.
* @param count the max number of candidates, or 0 to disable to display
* @see default_max_expected_candidates
* @brief creates a new instance.
* @param options the parser options
*/
sql_parser max_expected_candidates(std::size_t count) noexcept;
explicit sql_parser(sql_parser_options options = {}) noexcept;

/**
* @brief sets the debug level.
* @param level the debug level
* @return this
* @note this feature is only available for debug configurations
* @brief returns the parser options.
* @return the parser options
*/
sql_parser& set_debug(int level) noexcept;
[[nodiscard]] sql_parser_options& options() noexcept;

/// @copydoc options()
[[nodiscard]] sql_parser_options const& options() const noexcept;

/**
* @brief parses the contents.
Expand All @@ -57,8 +55,7 @@ class sql_parser {
[[nodiscard]] result_type operator()(::takatori::util::maybe_shared_ptr<document_type const> document) const;

private:
std::size_t max_expected_candidates_ { default_max_expected_candidates };
int debug_ {};
sql_parser_options options_;
};

} // namespace mizugaki::parser
4 changes: 4 additions & 0 deletions include/mizugaki/parser/sql_parser_code.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ enum class sql_parser_code {

/// @brief the document includes unexpected EOF.
unexpected_eof,

/// @brief the declared elements exceeds the limit of count.
exceed_number_of_elements,
};

/**
Expand All @@ -52,6 +55,7 @@ inline constexpr std::string_view to_string_view(sql_parser_code value) noexcept
case kind::invalid_token: return "invalid_token"sv;
case kind::unexpected_token: return "unexpected_token"sv;
case kind::unexpected_eof: return "unexpected_eof"sv;
case kind::exceed_number_of_elements: return "exceed_number_of_elements"sv;
}
std::abort();
}
Expand Down
165 changes: 165 additions & 0 deletions include/mizugaki/parser/sql_parser_element_kind.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
#pragma once

#include <array>
#include <ostream>
#include <string>
#include <string_view>
#include <type_traits>

namespace mizugaki::parser {

/**
* @brief represents syntactic element kind of SQL.
*/
enum class sql_parser_element_kind : std::size_t {
/// @brief statements in compilation units.
statement = 0,

/// @brief elements in schema declarations.
schema_element_definition,

/// @brief elements in table declarations.
table_element_definition,

/// @brief constraint definitions in column declarations.
column_constraint_definition,

/// @brief column references in foreign key columns, join correlations, etc.
column_reference,

/// @brief storage parameters in table or index definitions.
storage_parameter,

/// @brief set clauses in `UPDATE` statement.
set_clause,

/// @brief `WITH` elements in `SELECT` statement.
with_element,

/// @brief elements in `SELECT` statement.
select_element,

/// @brief table references in `FROM` clause.
table_reference,

/// @brief column references in `GROUP BY` clause.
grouping_element,

/// @brief column references in `ORDER BY` clause.
ordering_element,

/// @brief field definitions in row type.
field_definition,

/// @brief expressions in table value constructor (e.g., `VALUES ...`).
row_expression,

/// @brief elements in expression list.
scalar_expression,

/// @brief elements in `CASE` expression.
when_clause,

/// @brief elements in string literal concatenations.
string_literal_concatenation,
};

/**
* @brief represents an associated list of sql_parser_element_map.
* @tparam V the value type
*/
template<class V>
class sql_parser_element_map {
public:
/// @brief the key type.
using key_type = sql_parser_element_kind;

/// @brief the value type.
using value_type = V;

/// @brief the reference type.
using reference = std::add_lvalue_reference_t<value_type>;

/// @brief the const reference type.
using const_reference = std::add_lvalue_reference_t<std::add_const_t<value_type>>;

/**
* @brief returns the value corresponding to the given kind.
* @tparam K the target kind
* @return the value
*/
template<key_type K>
[[nodiscard]] constexpr reference get() noexcept {
return entries_[static_cast<std::size_t>(K) - offset];
}

/// @copydoc get()
template<key_type K>
[[nodiscard]] constexpr const_reference get() const noexcept {
return entries_[static_cast<std::size_t>(K) - offset];
}

/**
* @brief returns the value corresponding to the given kind.
* @param kind the target kind
* @return the value
*/
[[nodiscard]] constexpr reference operator[](key_type kind) noexcept {
return entries_[static_cast<std::size_t>(kind) - offset];
}

/// @copydoc operator[]()
[[nodiscard]] constexpr const_reference operator[](key_type kind) const noexcept {
return entries_[static_cast<std::size_t>(kind) - offset];

Check warning on line 113 in include/mizugaki/parser/sql_parser_element_kind.h

View workflow job for this annotation

GitHub Actions / Clang-Tidy

cppcoreguidelines-pro-bounds-constant-array-index

do not use array subscript when the index is not an integer constant expression
}

private:
static constexpr std::size_t offset = static_cast<std::size_t>(key_type::statement);

static constexpr std::size_t capacity =
static_cast<std::size_t>(key_type::string_literal_concatenation) + 1ULL - offset;

std::array<V, capacity> entries_ {};
};

/**
* @brief returns string representation of the value.
* @param value the target value
* @return the corresponded string representation
*/
inline constexpr std::string_view to_string_view(sql_parser_element_kind value) noexcept {
using namespace std::string_view_literals;
using kind = sql_parser_element_kind;
switch (value) {
case kind::statement: return "statement"sv;
case kind::schema_element_definition: return "schema_element_definition"sv;
case kind::table_element_definition: return "table_element_definition"sv;
case kind::column_constraint_definition: return "column_constraint_definition"sv;
case kind::column_reference: return "column_reference"sv;
case kind::storage_parameter: return "storage_parameter"sv;
case kind::set_clause: return "set_clause"sv;
case kind::with_element: return "with_element"sv;
case kind::select_element: return "select_element"sv;
case kind::table_reference: return "table_reference"sv;
case kind::grouping_element: return "grouping_element"sv;
case kind::ordering_element: return "ordering_element"sv;
case kind::field_definition: return "field_definition"sv;
case kind::row_expression: return "row_expression"sv;
case kind::scalar_expression: return "scalar_expression"sv;
case kind::when_clause: return "when_clause"sv;
case kind::string_literal_concatenation: return "string_literal_concatenation"sv;
}
std::abort();
}

/**
* @brief appends string representation of the given value.
* @param out the target output
* @param value the target value
* @return the output
*/
inline std::ostream& operator<<(std::ostream& out, sql_parser_element_kind value) {
return out << to_string_view(value);
}

} // namespace mizugaki::parser
60 changes: 60 additions & 0 deletions include/mizugaki/parser/sql_parser_options.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#pragma once

#include <memory>
#include <utility>

#include <mizugaki/parser/sql_parser_element_kind.h>

namespace mizugaki::parser {

class sql_parser_options {
public:
/// @brief the size type.
using size_type = std::size_t;

/// @brief default number of next token candidates to display on error.
static constexpr std::size_t default_max_expected_candidates = 5;

/**
* @brief creates a new instance.
*/
sql_parser_options();

/**
* @brief sets the max number of next token candidates to display on error.
* @param count the max number of candidates, or 0 to disable to display
* @see default_max_expected_candidates
*/
[[nodiscard]] size_type& max_expected_candidates() noexcept;

/// @copydoc max_expected_candidates()
[[nodiscard]] size_type const& max_expected_candidates() const noexcept;

/**
* @brief returns limits of each syntactic element.
* @details the limits are used to restrict the number of elements in the AST.
* Each default limit is 0, which means no limit.
* @return the map of limit sof each syntactic element
*/
[[nodiscard]] sql_parser_element_map<size_type>& element_limits() noexcept;

/// @copydoc element_limits()
[[nodiscard]] sql_parser_element_map<size_type> const& element_limits() const noexcept;

/**
* @brief returns the debug level.
* @return the debug level
* @note this feature is only available for debug configurations
*/
[[nodiscard]] int& debug() noexcept;

/// @copydoc debug()
[[nodiscard]] int const& debug() const noexcept;

private:
int debug_ {};
size_type max_expected_candidates_ { default_max_expected_candidates };
std::unique_ptr<sql_parser_element_map<size_type>> element_limits_;
};

} // namespace mizugaki::parser
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ add_library(mizugaki

# SQL parser
mizugaki/parser/sql_parser.cpp
mizugaki/parser/sql_parser_options.cpp
mizugaki/parser/sql_parser_diagnostic.cpp
mizugaki/parser/sql_parser_result.cpp
mizugaki/parser/sql_scanner.cpp
Expand Down
25 changes: 24 additions & 1 deletion src/mizugaki/parser/sql_driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <takatori/util/assertion.h>
#include <takatori/util/downcast.h>
#include <takatori/util/string_builder.h>

#include <mizugaki/ast/name/qualified.h>
#include <mizugaki/ast/type/user_defined.h>
Expand Down Expand Up @@ -68,10 +69,14 @@ void sql_driver::add_comment(location_type location) {
comments_.emplace_back(location);
}

std::size_t &sql_driver::max_expected_candidates() noexcept {
std::size_t& sql_driver::max_expected_candidates() noexcept {
return max_expected_candidates_;
}

::takatori::util::optional_ptr<sql_parser_element_map<std::size_t> const>& sql_driver::element_limits() noexcept {
return element_limits_;
}

std::vector<sql_driver::location_type>& sql_driver::comments() noexcept {
return comments_;
}
Expand Down Expand Up @@ -232,5 +237,23 @@ sql_driver::try_fold_literal(node_ptr<ast::scalar::expression> expression) {
return std::move(unary.operand());
}

bool sql_driver::validate_count(location_type location, std::size_t size, sql_driver::element_kind kind) {
if (!element_limits_) {
return true;
}
auto limit = (*element_limits_)[kind];
if (limit != 0 && limit < size) {
using ::takatori::util::string_builder;
error(sql_parser_code::exceed_number_of_elements, location,
string_builder {}
<< "SQL parser has reached the max number of elements: "
<< kind
<< " must be less than or equal to " << limit
<< string_builder::to_string);
return false;
}
return true;
}

} // namespace mizugaki::parser

14 changes: 13 additions & 1 deletion src/mizugaki/parser/sql_driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

#include <mizugaki/parser/sql_parser_result.h>
#include <mizugaki/parser/sql_parser_code.h>

#include <mizugaki/parser/sql_parser_element_kind.h>

namespace mizugaki::parser {

Expand All @@ -24,6 +24,8 @@ class sql_driver {

using location_type = ast::node_region;

using element_kind = sql_parser_element_kind;

template<class T>
using node_ptr = std::unique_ptr<T>;

Expand All @@ -46,6 +48,8 @@ class sql_driver {

[[nodiscard]] std::size_t& max_expected_candidates() noexcept;

[[nodiscard]] ::takatori::util::optional_ptr<sql_parser_element_map<std::size_t> const>& element_limits() noexcept;

[[nodiscard]] std::vector<location_type>& comments() noexcept;

[[nodiscard]] std::vector<location_type> const& comments() const noexcept;
Expand Down Expand Up @@ -106,12 +110,20 @@ class sql_driver {

[[nodiscard]] node_ptr<ast::scalar::expression> try_fold_literal(node_ptr<ast::scalar::expression> expression);

template<class T>
[[nodiscard]] bool validate(location_type location, std::vector<T> const& elements, element_kind kind) {
return validate_count(location, elements.size(), kind);
}

[[nodiscard]] bool validate_count(location_type location, std::size_t size, element_kind kind);

private:
::takatori::util::maybe_shared_ptr<document_type const> document_;
std::vector<location_type> comments_;
result_type result_ {};

std::size_t max_expected_candidates_ {};
::takatori::util::optional_ptr<sql_parser_element_map<std::size_t> const> element_limits_;
};

} // namespace sandbox
Loading

0 comments on commit 23f6a28

Please sign in to comment.