Branch data Line data Source code
1 : : #pragma once 2 : : 3 : : #include <mutable/catalog/Catalog.hpp> 4 : : #include <mutable/catalog/DatabaseCommand.hpp> 5 : : #include <mutable/parse/AST.hpp> 6 : : #include <mutable/util/Diagnostic.hpp> 7 : : #include <sstream> 8 : : #include <unordered_map> 9 : : #include <vector> 10 : : 11 : : 12 : : namespace m { 13 : : 14 : : namespace ast { 15 : : 16 : : struct M_EXPORT Sema : ASTVisitor 17 : : { 18 : : /** Holds context information used by semantic analysis of a single statement. */ 19 [ + - - + ]: 1401 : struct SemaContext 20 : : { 21 : : ///> if the statement that is being analyzed is a nested query, this is its alias in the outer statement 22 : : ThreadSafePooledOptionalString alias; 23 : : 24 : : ///> the statement that is currently being analyzed and for which this `SemaContext` is used 25 : : Stmt &stmt; 26 : : enum stage_t { 27 : : S_From, 28 : : S_Where, 29 : : S_GroupBy, 30 : : S_Having, 31 : : S_Select, 32 : : S_OrderBy, 33 : : S_Limit, 34 : 1401 : } stage = S_From; ///< current stage 35 : : 36 : 1401 : bool needs_grouping = false; 37 : : 38 : : struct result_t 39 : : { 40 : : std::reference_wrapper<Expr> expr_; 41 : : ///> the order of this result column in the result set 42 : : unsigned order; 43 : : ///> alias of the expression; may not have a value 44 : : ThreadSafePooledOptionalString alias; 45 : : 46 : 1461 : result_t(Expr &expr, unsigned order) : expr_(expr), order(order) { } 47 : 81 : result_t(Expr &expr, unsigned order, ThreadSafePooledOptionalString alias) 48 : 81 : : expr_(expr), order(order), alias(std::move(alias)) 49 : 81 : { } 50 : : 51 : 194 : Expr & expr() { return expr_.get(); } 52 : : const Expr & expr() const { return expr_.get(); } 53 : : }; 54 : : 55 : : ///> list of all computed expressions along with their order 56 : : using named_expr_table = std::unordered_multimap<ThreadSafePooledString, 57 : : std::pair<std::reference_wrapper<Expr>, unsigned>>; 58 : : ///> the type of a source of data: either a database table or a nested query with named results 59 : : using source_type = std::variant<std::monostate, std::reference_wrapper<const Table>, named_expr_table>; 60 : : ///> associative container mapping source name to data source and its order 61 : : using source_table = std::unordered_map<ThreadSafePooledString, std::pair<source_type, unsigned>>; 62 : : ///> list of all sources along with their order 63 : : source_table sources; 64 : : ///> list of all results computed by this statement along with their order 65 : : std::unordered_multimap<ThreadSafePooledString, result_t> results; 66 : : ///> list of grouping keys 67 : : std::unordered_multimap<ThreadSafePooledString, std::reference_wrapper<Expr>> grouping_keys; 68 : : 69 : 2802 : SemaContext(Stmt &stmt) : stmt(stmt) { } 70 : : }; 71 : : 72 : : private: 73 : : /** Helper class to create a context when one is required but does not yet exist. Automatically disposes of the 74 : : * context when the instance goes out of scope. */ 75 : : struct RequireContext 76 : : { 77 : : private: 78 : : Sema &sema_; 79 : : bool needs_context_ = false; 80 : : 81 : : public: 82 : 1401 : RequireContext(Sema *sema, Stmt &stmt) 83 : 1401 : : sema_(*M_notnull(sema)) 84 : 1401 : , needs_context_(sema_.contexts_.empty()) 85 : : { 86 [ + + ]: 1401 : if (needs_context_) 87 [ + - ]: 1334 : sema_.push_context(stmt); 88 : 1401 : } 89 : : 90 : 1401 : ~RequireContext() { 91 [ + + ]: 1401 : if (needs_context_) 92 [ + - ]: 1334 : sema_.pop_context(); 93 : 1401 : } 94 : : }; 95 : : 96 : : public: 97 : : Diagnostic &diag; 98 : : private: 99 : : ///> a stack of sema contexts; one per statement; grows by nesting statements 100 : : using context_stack_t = std::vector<SemaContext*>; 101 : : context_stack_t contexts_; 102 : : ///> used to create textual representation of complex AST objects, e.g. expressions 103 : : std::ostringstream oss; 104 : : ///> the command to execute when semantic analysis completes without errors 105 : : std::unique_ptr<DatabaseCommand> command_; 106 : : 107 : : public: 108 [ + - ]: 1501 : Sema(Diagnostic &diag) : diag(diag) { } 109 : : 110 : : /** Perform semantic analysis of an `ast::Command`. Returns an `m::DatabaseCommand` to execute when no semantic 111 : : * errors occurred, `nullptr` otherwise. */ 112 : : std::unique_ptr<DatabaseCommand> analyze(std::unique_ptr<ast::Command> ast); 113 : : 114 : : using ASTExprVisitor::operator(); 115 : : using ASTClauseVisitor::operator(); 116 : : using ASTCommandVisitor::operator(); 117 : : #define DECLARE(CLASS) void operator()(CLASS&) override; 118 : : M_AST_EXPR_LIST(DECLARE) 119 : : M_AST_CLAUSE_LIST(DECLARE) 120 : : M_AST_COMMAND_LIST(DECLARE) 121 : : #undef DECLARE 122 : : 123 : : private: 124 : 1401 : SemaContext & push_context(Stmt &stmt, ThreadSafePooledOptionalString alias = {}) { 125 [ + - ]: 1401 : auto &ref = contexts_.emplace_back(new SemaContext(stmt)); 126 [ - + ]: 1401 : ref->alias = std::move(alias); 127 : 1401 : return *ref; 128 : 0 : } 129 : 1401 : SemaContext pop_context() { 130 : 1401 : auto ctx = *contexts_.back(); 131 [ - + ]: 1401 : delete contexts_.back(); 132 : 1401 : contexts_.pop_back(); 133 : 1401 : return ctx; 134 [ + - ]: 1401 : } 135 : 4046 : SemaContext & get_context() { 136 : 4046 : M_insist(not contexts_.empty()); 137 : 4046 : return *contexts_.back(); 138 : : } 139 : : const SemaContext & get_context() const { 140 : : M_insist(not contexts_.empty()); 141 : : return *contexts_.back(); 142 : : } 143 : : 144 : : /** Returns true iff the current statement, that is being analyzed, is a nested statement. */ 145 : : bool is_nested() const; 146 : : 147 : : 148 : : /*------------------------------------------------------------------------------------------------------------------ 149 : : * Sema Designator Helpers 150 : : *----------------------------------------------------------------------------------------------------------------*/ 151 : : 152 : : /** Creates a fresh `Designator` with the given \p name at location \p tok and with target \p target. */ 153 : : std::unique_ptr<Designator> create_designator(ThreadSafePooledString name, Token tok, const Expr &target); 154 : : 155 : : /** Creates a fresh `Designator` with the same syntactical representation as \p name (except the table name if 156 : : * \p drop_table_name) and with target \p target. */ 157 : : std::unique_ptr<Designator> create_designator(const Expr &name, const Expr &target, bool drop_table_name = false); 158 : : 159 : : /** Creates an entirely new `Designator`. This method is used to introduce artificial `Designator`s to *expand* an 160 : : * anti-projection (`SELECT` with asterisk `*`). */ 161 : 1172 : std::unique_ptr<Designator> create_designator(Position pos, ThreadSafePooledString table_name, 162 : : ThreadSafePooledString attr_name, 163 : : typename Designator::target_type target, const Type *type) 164 : : { 165 : 1172 : auto &C = Catalog::Get(); 166 [ + - ]: 1172 : Token dot(pos, C.pool("."), TK_DOT); 167 [ + - + - ]: 1172 : Token table(pos, std::move(table_name), TK_IDENTIFIER); 168 [ + - + - ]: 1172 : Token attr(pos, std::move(attr_name), TK_IDENTIFIER); 169 [ + - ]: 1172 : auto d = std::make_unique<Designator>(std::move(dot), std::move(table), std::move(attr)); 170 : 1172 : d->type_ = type; 171 : 1172 : d->target_ = target; 172 : 1172 : return d; 173 [ + - ]: 1172 : } 174 : : 175 : : /** Replaces \p to_replace by a fresh `Designator`, that has the same syntactical representation as \p to_replace 176 : : * and targets \p target. */ 177 : : void replace_by_fresh_designator_to(std::unique_ptr<Expr> &to_replace, const Expr &target); 178 : : 179 : : 180 : : /*------------------------------------------------------------------------------------------------------------------ 181 : : * Other Sema Helpers 182 : : *----------------------------------------------------------------------------------------------------------------*/ 183 : : 184 : : /** Computes whether the bound parts of \p expr are composable of elements in \p components. */ 185 : : bool is_composable_of(const ast::Expr &expr, const std::vector<std::reference_wrapper<ast::Expr>> components); 186 : : 187 : : /** Recursively analyzes the `ast::Expr` referenced by \p ptr and replaces subexpressions that can be composed of 188 : : * the elements in \p components. */ 189 : : void compose_of(std::unique_ptr<ast::Expr> &ptr, const std::vector<std::reference_wrapper<ast::Expr>> components); 190 : : 191 : : /** Creates a unique ID from a sequence of `SemaContext`s by concatenating their aliases. */ 192 : : ThreadSafePooledOptionalString make_unique_id_from_binding_path(context_stack_t::reverse_iterator current_ctx, 193 : : context_stack_t::reverse_iterator binding_ctx); 194 : : }; 195 : : 196 : : } 197 : : 198 : : }