LCOV - code coverage report
Current view: top level - src/parse - Sema.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1011 1242 81.4 %
Date: 2025-03-25 01:19:55 Functions: 33 94 35.1 %
Branches: 1309 2587 50.6 %

           Branch data     Line data    Source code
       1                 :            : #include "parse/Sema.hpp"
       2                 :            : 
       3                 :            : #include <cstdint>
       4                 :            : #include <mutable/catalog/Catalog.hpp>
       5                 :            : #include <mutable/io/Reader.hpp>
       6                 :            : #include <mutable/Options.hpp>
       7                 :            : #include <sstream>
       8                 :            : #include <unordered_map>
       9                 :            : 
      10                 :            : 
      11                 :            : using namespace m;
      12                 :            : using namespace m::ast;
      13                 :            : 
      14                 :            : 
      15                 :          0 : std::unique_ptr<DatabaseCommand> Sema::analyze(std::unique_ptr<ast::Command> ast)
      16                 :            : {
      17                 :          0 :     (*this)(*ast); // perform semantic analysis
      18         [ #  # ]:          0 :     if (command_)
      19         [ #  # ]:          0 :         command_->ast(std::move(ast)); // move AST into DatabaseCommand instance
      20                 :          0 :     return std::move(command_);
      21                 :          0 : }
      22                 :            : 
      23                 :       1365 : bool Sema::is_nested() const
      24                 :            : {
      25                 :       1365 :     return contexts_.size() > 1;
      26                 :            : }
      27                 :            : 
      28                 :            : /*----------------------------------------------------------------------------------------------------------------------
      29                 :            :  * Sema Designator Helpers
      30                 :            :  *--------------------------------------------------------------------------------------------------------------------*/
      31                 :            : 
      32                 :         30 : std::unique_ptr<Designator> Sema::create_designator(ThreadSafePooledString name, Token tok, const Expr &target)
      33                 :            : {
      34         [ +  - ]:         60 :     auto new_designator = std::make_unique<Designator>(tok, Token::CreateArtificial(),
      35   [ +  -  +  - ]:         30 :                                                        Token(tok.pos, std::move(name), TK_IDENTIFIER));
      36         [ +  - ]:         30 :     new_designator->type_ = target.type();
      37                 :         30 :     new_designator->target_ = &target;
      38                 :         30 :     return new_designator;
      39         [ +  - ]:         30 : }
      40                 :            : 
      41                 :          0 : std::unique_ptr<Designator> Sema::create_designator(const Expr &name, const Expr &target, bool drop_table_name)
      42                 :            : {
      43                 :          0 :     auto &C = Catalog::Get();
      44                 :            : 
      45                 :          0 :     std::unique_ptr<Designator> new_designator;
      46   [ #  #  #  # ]:          0 :     if (auto d = cast<const Designator>(&name)) {
      47   [ #  #  #  #  :          0 :         Token table_name = drop_table_name ? Token::CreateArtificial() : d->table_name; // possibly drop table name
                   #  # ]
      48         [ #  # ]:          0 :         new_designator = std::make_unique<Designator>(d->tok, std::move(table_name), d->attr_name); // copy of `name`
      49                 :          0 :     } else {
      50   [ #  #  #  # ]:          0 :         oss.str("");
      51         [ #  # ]:          0 :         oss << name; // stringify `name`
      52   [ #  #  #  #  :          0 :         Token tok(target.tok.pos, C.pool(oss.str().c_str()), TK_DEC_INT); // fresh identifier
                   #  # ]
      53         [ #  # ]:          0 :         new_designator = std::make_unique<Designator>(std::move(tok));
      54                 :          0 :     }
      55                 :            : 
      56         [ #  # ]:          0 :     new_designator->type_ = target.type();
      57                 :          0 :     new_designator->target_ = &target;
      58                 :          0 :     return new_designator;
      59         [ #  # ]:          0 : }
      60                 :            : 
      61                 :          0 : void Sema::replace_by_fresh_designator_to(std::unique_ptr<Expr> &to_replace, const Expr &target)
      62                 :            : {
      63                 :          0 :     auto new_designator = create_designator(*to_replace, target);
      64                 :          0 :     to_replace = std::move(new_designator);
      65                 :          0 : }
      66                 :            : 
      67                 :          0 : 
      68                 :            : /*----------------------------------------------------------------------------------------------------------------------
      69                 :            :  * Other Sema Helpers
      70                 :            :  *--------------------------------------------------------------------------------------------------------------------*/
      71                 :            : 
      72                 :       2252 : ThreadSafePooledOptionalString Sema::make_unique_id_from_binding_path(context_stack_t::reverse_iterator current_ctx,
      73                 :            :                                                                       context_stack_t::reverse_iterator binding_ctx)
      74                 :          1 : {
      75         [ +  + ]:       2252 :     if (current_ctx == binding_ctx) return {};
      76                 :            : 
      77   [ +  -  +  - ]:         28 :     oss.str("");
      78         [ +  + ]:         56 :     for (auto it = current_ctx; it != binding_ctx; ++it) {
      79         [ +  - ]:         28 :         if (it != current_ctx) oss << '.';
      80                 :         28 :         M_insist((*it)->alias.has_value(), "nested queries must have an alias");
      81                 :         28 :         oss << (*it)->alias;
      82                 :         28 :     }
      83                 :            : 
      84                 :         28 :     auto &C = Catalog::Get();
      85   [ +  -  -  + ]:         28 :     return C.pool(oss.str().c_str());
      86                 :       2252 : }
      87                 :            : 
      88                 :          0 : bool Sema::is_composable_of(const ast::Expr &expr,
      89                 :            :                             const std::vector<std::reference_wrapper<ast::Expr>> components)
      90                 :            : {
      91                 :          0 :     auto recurse = overloaded {
      92                 :          0 :         [&](const ast::Designator &d) -> bool {
      93         [ #  # ]:          0 :             return d.contains_free_variables() or d.is_identifier(); // identifiers are implicitly composable, as they never refer into a table XXX do we have to check the target?
      94                 :            :         },
      95                 :          0 :         [&](const ast::FnApplicationExpr &e) -> bool {
      96   [ #  #  #  # ]:          0 :             if (not is_composable_of(*e.fn, components)) return false;
      97         [ #  # ]:          0 :             for (auto &arg : e.args) {
      98   [ #  #  #  # ]:          0 :                 if (not is_composable_of(*arg, components))
      99                 :          0 :                     return false;
     100                 :            :             }
     101                 :          0 :             return true;
     102                 :          0 :         },
     103         [ #  # ]:          0 :         [&](const ast::UnaryExpr &e) -> bool { return is_composable_of(*e.expr, components); },
     104                 :          0 :         [&](const ast::BinaryExpr &e) -> bool {
     105   [ #  #  #  #  :          0 :             return is_composable_of(*e.lhs, components) and is_composable_of(*e.rhs, components);
          #  #  #  #  #  
                #  #  # ]
     106                 :          0 :         },
     107                 :          0 :         [](auto&) -> bool { return true; },
     108                 :            :     };
     109                 :            : 
     110         [ #  # ]:          0 :     for (auto c : components)
     111         [ #  # ]:          0 :         if (expr == c.get()) return true; // syntactically equivalent to a component
     112                 :          0 :     return visit(recurse, expr, m::tag<m::ast::ConstASTExprVisitor>()); // attempt to recursively compose expr
     113                 :          0 : }
     114                 :            : 
     115                 :          0 : void Sema::compose_of(std::unique_ptr<ast::Expr> &ptr, const std::vector<std::reference_wrapper<ast::Expr>> components)
     116                 :            : {
     117                 :          0 :     auto recurse = overloaded {
     118                 :          0 :         [&](const ast::Designator &d) -> bool {
     119         [ #  # ]:          0 :             return d.contains_free_variables() or d.is_identifier(); // identifiers are implicitly composable, as they never refer into a table XXX do we have to check the target?
     120                 :            :         },
     121                 :          0 :         [&](const ast::FnApplicationExpr &e) -> bool {
     122   [ #  #  #  # ]:          0 :             if (not is_composable_of(*e.fn, components)) return false;
     123         [ #  # ]:          0 :             for (auto &arg : e.args) {
     124   [ #  #  #  # ]:          0 :                 if (not is_composable_of(*arg, components))
     125                 :          0 :                     return false;
     126                 :            :             }
     127                 :          0 :             return true;
     128                 :          0 :         },
     129         [ #  # ]:          0 :         [&](const ast::UnaryExpr &e) -> bool { return is_composable_of(*e.expr, components); },
     130                 :          0 :         [&](const ast::BinaryExpr &e) -> bool {
     131   [ #  #  #  #  :          0 :             return is_composable_of(*e.lhs, components) and is_composable_of(*e.rhs, components);
          #  #  #  #  #  
                #  #  # ]
     132                 :          0 :         },
     133                 :          0 :         [](auto&) -> bool { return true; },
     134                 :            :     };
     135                 :            : 
     136         [ #  # ]:          0 :     for (auto c : components) {
     137         [ #  # ]:          0 :         if (*ptr == c.get())
     138                 :          0 :             replace_by_fresh_designator_to(/* to_replace= */ ptr, /* target= */ c.get());
     139                 :            :     }
     140                 :          0 :     visit(recurse, *ptr, m::tag<m::ast::ConstASTExprVisitor>()); // attempt to recursively compose expr
     141                 :          0 : }
     142                 :            : 
     143                 :            : /*===== Expr =========================================================================================================*/
     144                 :            : 
     145                 :          0 : void Sema::operator()(ErrorExpr &e)
     146                 :            : {
     147         [ #  # ]:          0 :     e.type_ = Type::Get_Error();
     148                 :          0 : }
     149                 :            : 
     150                 :       2376 : void Sema::operator()(Designator &e)
     151                 :            : {
     152                 :       2376 :     Catalog &C = Catalog::Get();
     153                 :       2376 :     SemaContext *current_ctx = &get_context();
     154                 :       2376 :     auto attr_name = e.attr_name.text.assert_not_none();
     155                 :            : 
     156   [ +  -  +  - ]:       2376 :     oss.str("");
     157         [ +  - ]:       2376 :     oss << e;
     158   [ +  -  +  - ]:       2376 :     auto pooled_name = C.pool(oss.str().c_str());
     159                 :            : 
     160                 :            :     /*----- In a stage after SELECT, check whether the `Designator` refers to a value produced by SELECT. -----*/
     161         [ +  + ]:       2376 :     if (current_ctx->stage > SemaContext::S_Select) {
     162         [ +  - ]:        127 :         auto [begin, end] = current_ctx->results.equal_range(pooled_name);
     163   [ +  -  +  -  :         82 :         if (std::distance(begin, end) > 1) {
                   +  + ]
     164   [ +  -  +  -  :          7 :             diag.e(e.tok.pos) << "Designator " << e << " is ambiguous, multiple occurrences in SELECT clause.\n";
             +  -  +  - ]
     165   [ +  -  +  - ]:          7 :             e.type_ = Type::Get_Error();
     166                 :          7 :             return;
     167   [ +  -  +  -  :         68 :         } else if (std::distance(begin, end) == 1) {
                   +  + ]
     168                 :         18 :             SemaContext::result_t &result = begin->second;
     169   [ +  -  +  -  :         18 :             if (auto d = cast<Designator>(&result.expr()); d and not result.alias.has_value()) // target is a designator
          +  -  +  -  +  
                      + ]
     170   [ +  -  +  - ]:          3 :                 e.table_name.text = d->table_name.text;                                        // w/o explicit alias
     171   [ +  -  +  - ]:         18 :             e.type_ = result.expr().type();
     172         [ +  - ]:         18 :             e.target_ = &result.expr();
     173                 :         18 :             return;
     174                 :            :         }
     175                 :         16 :     }
     176                 :            : 
     177                 :            :     /*----- In a stage after GROUP BY, check whether the entire expression refers to a grouping key. -----*/
     178   [ +  +  +  + ]:       2351 :     if (current_ctx->stage > SemaContext::S_GroupBy and not current_ctx->grouping_keys.empty()) {
     179         [ +  - ]:        339 :         auto [begin, end] = current_ctx->grouping_keys.equal_range(pooled_name);
     180   [ +  -  +  -  :        180 :         if (std::distance(begin, end) > 1) {
                   -  + ]
     181   [ #  #  #  #  :          0 :             diag.e(e.tok.pos) << "Designator " << e << " is ambiguous, multiple occurrences in GROUP BY clause.\n";
             #  #  #  # ]
     182   [ #  #  #  # ]:          0 :             e.type_ = Type::Get_Error();
     183                 :          0 :             return;
     184   [ +  -  +  -  :        180 :         } else if (std::distance(begin, end) == 1) {
                   +  + ]
     185                 :         69 :             auto &referenced_expr = begin->second.get();
     186         [ +  - ]:         69 :             e.type_ = referenced_expr.type();
     187   [ +  -  +  -  :         69 :             if (auto pt = cast<const PrimitiveType>(e.type()))
                   +  - ]
     188         [ +  - ]:         69 :                 e.type_ = pt->as_scalar();
     189                 :            :             else
     190   [ #  #  #  #  :          0 :                 M_insist(e.type()->is_error(), "grouping expression must be of primitive type");
                   #  # ]
     191                 :         69 :             e.target_ = &referenced_expr;
     192                 :         69 :             return;
     193                 :            :         }
     194                 :         21 :     }
     195                 :            : 
     196                 :            :     /*----- Designator was neither a reference to a SELECT or GROUP BY expression. -----*/
     197                 :       2282 :     decltype(contexts_)::reverse_iterator found_ctx; // the context where the designator is found
     198                 :       2282 :     bool is_result = false;
     199                 :            : 
     200                 :            :     /* If the designator references an attribute of a table, search for it. */
     201   [ +  -  +  + ]:       2282 :     if (e.table_name) {
     202                 :            :         /* Find the source table first and then locate the target inside this table. */
     203                 :       1974 :         SemaContext::source_type src;
     204                 :            : 
     205                 :            :         /* Search all contexts, starting with the innermost and advancing outwards. */
     206                 :       1974 :         auto it = contexts_.rbegin();
     207   [ +  +  +  - ]:       2013 :         for (auto end = contexts_.rend(); it != end; ++it) {
     208                 :            :             try {
     209   [ +  -  +  -  :       2005 :                 src = (*it)->sources.at(e.table_name.text.assert_not_none()).first;
             +  +  +  - ]
     210                 :       1966 :                 break;
     211         [ -  + ]:         39 :             } catch (std::out_of_range) {
     212                 :            :                 /* The source is not found in this context so iterate over the entire stack. */
     213         [ +  - ]:         39 :             }
     214                 :         39 :         }
     215                 :            : 
     216   [ +  +  +  + ]:       1966 :         if (it == contexts_.rend()) {
     217   [ +  -  +  -  :          8 :             diag.e(e.table_name.pos) << "Source table " << e.table_name.text
                   +  - ]
     218         [ +  - ]:          8 :                                      << " not found. Maybe you forgot to specify it in the FROM clause?\n";
     219   [ +  -  +  - ]:          8 :             e.type_ = Type::Get_Error();
     220                 :          8 :             return;
     221                 :            :         }
     222                 :       1966 :         found_ctx = it;
     223                 :            : 
     224                 :            :         /* Find the target inside the source table. */
     225                 :       1966 :         Designator::target_type target;
     226         [ +  + ]:       1966 :         if (auto ref = std::get_if<std::reference_wrapper<const Table>>(&src)) {
     227                 :       1946 :             const Table &tbl = ref->get();
     228                 :            :             /* Find the attribute inside the table. */
     229                 :            :             try {
     230         [ +  + ]:       1946 :                 target = &tbl.at(attr_name); // we found an attribute of that name in the source tables
     231         [ -  + ]:       1946 :             } catch (std::out_of_range) {
     232   [ +  -  +  -  :          2 :                 diag.e(e.attr_name.pos) << "Table " << e.table_name.text << " has no attribute " << attr_name << ".\n";
          +  -  +  -  +  
                -  +  - ]
     233   [ +  -  +  - ]:          2 :                 e.type_ = Type::Get_Error();
     234                 :            :                 return;
     235   [ +  -  #  # ]:          2 :             }
     236         [ +  - ]:       1964 :         } else if (auto T = std::get_if<SemaContext::named_expr_table>(&src)) {
     237                 :         20 :             const SemaContext::named_expr_table &tbl = *T;
     238                 :            :             /* Find expression inside named expression table. */
     239         [ +  - ]:         32 :             auto [begin, end] = tbl.equal_range(attr_name);
     240         [ +  + ]:         20 :             if (begin == end) {
     241   [ +  -  +  -  :          1 :                 diag.e(e.attr_name.pos) << "Source " << e.table_name.text << " has no attribute " << attr_name << ".\n";
          +  -  +  -  +  
                -  +  - ]
     242   [ +  -  +  - ]:          1 :                 e.type_ = Type::Get_Error();
     243                 :          1 :                 return;
     244   [ +  -  +  -  :         38 :             } else if (std::distance(begin, end) > 1) {
                   +  + ]
     245   [ +  -  +  -  :          7 :                 diag.e(e.attr_name.pos) << "Source " << e.table_name.text << " has multiple attributes " << attr_name
          +  -  +  -  +  
                      - ]
     246         [ +  - ]:          7 :                                         << ".\n";
     247   [ +  -  +  - ]:          7 :                 e.type_ = Type::Get_Error();
     248                 :          7 :                 return;
     249                 :            :             } else {
     250                 :         12 :                 target = &begin->second.first.get();
     251                 :            :             }
     252                 :         12 :         } else {
     253         [ #  # ]:          0 :             M_unreachable("invalid variant");
     254                 :            :         }
     255                 :       1956 :         e.target_ = target;
     256   [ +  -  +  - ]:       1956 :         e.set_binding_depth(std::distance(contexts_.rbegin(), found_ctx));
     257   [ +  -  -  + ]:       1956 :         e.unique_id_ = make_unique_id_from_binding_path(contexts_.rbegin(), found_ctx);
     258         [ +  + ]:       1974 :     } else {
     259                 :            :         /* No table name was specified.  The designator references either a result or a named expression.  Search the
     260                 :            :          * named expressions first, because they overrule attribute names. */
     261   [ +  -  -  + ]:        308 :         if (auto [begin, end] = current_ctx->results.equal_range(attr_name);
     262   [ +  +  +  -  :        308 :             current_ctx->stage > SemaContext::S_Select and std::distance(begin, end) >= 1)
                   +  - ]
     263                 :            :         {
     264                 :            :             /* Found a named expression. */
     265   [ #  #  #  #  :          0 :             if (std::distance(begin, end) > 1) {
                   #  # ]
     266   [ #  #  #  #  :          0 :                 diag.e(e.attr_name.pos) << "Attribute specifier " << attr_name << " is ambiguous.\n";
             #  #  #  # ]
     267   [ #  #  #  # ]:          0 :                 e.type_ = Type::Get_Error();
     268                 :          0 :                 return;
     269                 :            :             } else {
     270   [ #  #  #  #  :          0 :                 M_insist(std::distance(begin, end) == 1);
                   #  # ]
     271                 :          0 :                 SemaContext::result_t &result = begin->second;
     272         [ #  # ]:          0 :                 e.target_ = &result.expr();
     273   [ #  #  #  #  :          0 :                 if (auto d = cast<Designator>(&result.expr()); d and d->attr_name.text == attr_name)
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     274   [ #  #  #  # ]:          0 :                     e.table_name.text = d->table_name.text;
     275         [ #  # ]:          0 :                 e.set_binding_depth(0); // bound by the current (innermost) context ⇒ bound variable
     276                 :          0 :                 is_result = true;
     277                 :          0 :                 found_ctx = contexts_.rbegin(); // iterator to the current context
     278                 :            :             }
     279                 :          0 :         } else {
     280                 :            :             /* Since no table was explicitly specified, we must search *all* sources for the attribute. */
     281                 :        308 :             Designator::target_type target;
     282                 :        308 :             ThreadSafePooledOptionalString alias;
     283                 :            : 
     284                 :            :             /* Search all contexts, starting with the innermost and advancing outwards. */
     285   [ +  -  +  +  :        318 :             for (auto it = contexts_.rbegin(), end = contexts_.rend(); it != end; ++it) {
                   +  - ]
     286   [ +  -  +  + ]:        621 :                 for (auto &src : (*it)->sources) {
     287         [ +  + ]:        315 :                     if (auto ref = std::get_if<std::reference_wrapper<const Table>>(&src.second.first)) {
     288                 :        306 :                         const Table &tbl = ref->get();
     289                 :            :                         try {
     290         [ +  + ]:        306 :                             const Attribute &A = tbl.at(attr_name);
     291         [ +  + ]:        296 :                             if (not std::holds_alternative<std::monostate>(target)) {
     292                 :            :                                 /* ambiguous attribute name */
     293   [ +  -  +  -  :          3 :                                 diag.e(e.attr_name.pos) << "Attribute specifier " << attr_name << " is ambiguous.\n";
             +  -  +  - ]
     294                 :            :                                 // TODO print names of conflicting tables
     295   [ +  -  +  - ]:          3 :                                 e.type_ = Type::Get_Error();
     296                 :          3 :                                 return;
     297                 :            :                             } else {
     298                 :        293 :                                 target = &A; // we found an attribute of that name in the source tables
     299   [ +  -  +  - ]:        293 :                                 alias = src.first;
     300                 :        293 :                                 found_ctx = it;
     301                 :            :                             }
     302         [ -  + ]:        303 :                         } catch (std::out_of_range) {
     303                 :            :                             /* This source table has no attribute of that name.  OK, continue. */
     304         [ +  - ]:         10 :                         }
     305         [ -  + ]:        312 :                     } else if (auto T = std::get_if<SemaContext::named_expr_table>(&src.second.first)) {
     306                 :          9 :                         const SemaContext::named_expr_table &tbl = *T;
     307         [ +  - ]:         32 :                         auto [begin, end] = tbl.equal_range(attr_name);
     308         [ -  + ]:          9 :                         if (begin == end) {
     309                 :            :                             /* This source table has no attribute of that name.  OK, continue. */
     310   [ +  -  +  -  :         18 :                         } else if (std::distance(begin, end) > 1) {
                   +  + ]
     311   [ +  -  +  -  :          1 :                             diag.e(e.attr_name.pos) << "Attribute specifier " << attr_name << " is ambiguous.\n";
             +  -  +  - ]
     312   [ +  -  +  - ]:          1 :                             e.type_ = Type::Get_Error();
     313                 :          1 :                             return;
     314                 :            :                         } else {
     315   [ +  -  +  -  :         16 :                             M_insist(std::distance(begin, end) == 1);
                   +  - ]
     316         [ +  + ]:          8 :                             if (not std::holds_alternative<std::monostate>(target)) {
     317                 :            :                                 /* ambiguous attribute name */
     318   [ +  -  +  -  :          1 :                                 diag.e(e.attr_name.pos) << "Attribute specifier " << attr_name << " is ambiguous.\n";
             +  -  +  - ]
     319                 :            :                                 // TODO print names of conflicting tables
     320   [ +  -  +  - ]:          1 :                                 e.type_ = Type::Get_Error();
     321                 :          1 :                                 return;
     322                 :            :                             } else {
     323                 :          7 :                                 target = &begin->second.first.get(); // we found an attribute of that name in the source tables
     324   [ +  -  +  - ]:          7 :                                 alias = src.first;
     325                 :          7 :                                 found_ctx = it;
     326                 :            :                             }
     327                 :            :                         }
     328                 :          7 :                     } else {
     329         [ #  # ]:          0 :                         M_unreachable("invalid variant");
     330                 :            :                     }
     331                 :            :                 }
     332                 :            :                 /* If we found target of the designator, abort searching contexts further outside. */
     333         [ +  + ]:        306 :                 if (not std::holds_alternative<std::monostate>(target))
     334                 :        296 :                     break;
     335                 :         10 :             }
     336                 :            : 
     337                 :            :             /* If this designator could not be resolved, emit an error and abort further semantic analysis. */
     338         [ +  + ]:        303 :             if (std::holds_alternative<std::monostate>(target)) {
     339   [ +  -  +  -  :          7 :                 diag.e(e.attr_name.pos) << "Attribute " << attr_name << " not found.\n";
             +  -  +  - ]
     340   [ +  -  +  - ]:          7 :                 e.type_ = Type::Get_Error();
     341                 :          7 :                 return;
     342                 :            :             }
     343                 :            : 
     344                 :        296 :             e.target_ = target;
     345   [ +  -  +  - ]:        296 :             e.table_name.text = alias; // set the deduced table name of this designator
     346   [ +  -  +  - ]:        296 :             e.set_binding_depth(std::distance(contexts_.rbegin(), found_ctx));
     347   [ +  -  +  - ]:        296 :             e.unique_id_ = make_unique_id_from_binding_path(contexts_.rbegin(), found_ctx);
     348         [ +  + ]:        308 :         }
     349                 :            :     }
     350                 :            : 
     351                 :            :     /* Compute the type of this designator based on the referenced source. */
     352         [ +  - ]:       2252 :     M_insist(e.target_.index() != 0);
     353                 :            :     struct get_type {
     354                 :          0 :         const Type * operator()(std::monostate&) const { M_unreachable("target not set"); }
     355                 :       2234 :         const Type * operator()(const Attribute *attr) const { return attr->type; }
     356                 :         18 :         const Type * operator()(const Expr *expr) const { return expr->type_; }
     357                 :            :     };
     358   [ +  -  +  - ]:       2252 :     const PrimitiveType *pt = cast<const PrimitiveType>(std::visit(get_type(), e.target_));
     359                 :       2252 :     e.type_ = pt;
     360                 :            : 
     361         [ +  - ]:       2252 :     if (not is_result)
     362         [ +  - ]:       2252 :         e.type_ = pt->as_vectorial();
     363                 :            : 
     364                 :            :     /* Check if any context between current context and found context is in stage `S_FROM`. */
     365   [ +  -  +  +  :       2280 :     for (auto it = contexts_.rbegin(); it != found_ctx; ++it) {
                   +  - ]
     366   [ +  -  -  + ]:         28 :         if ((*it)->stage == SemaContext::S_From) {
     367                 :            :             /* The designator is correlated and occurs in a nested query in the FROM. Emit an error. */
     368   [ #  #  #  # ]:          0 :             diag.e(e.attr_name.pos) << "Correlated attributes are not allowed in the FROM clause.\n";
     369   [ #  #  #  # ]:          0 :             e.type_ = Type::Get_Error();
     370                 :          0 :             return;
     371                 :            :         }
     372                 :         28 :     }
     373                 :            : 
     374   [ +  -  -  +  :       2252 :     switch ((*found_ctx)->stage) {
                      + ]
     375                 :            :         default:
     376         [ #  # ]:          0 :             M_unreachable("designator not allowed in this stage");
     377                 :            : 
     378                 :            :         case SemaContext::S_From:
     379                 :            :             /* The designator is correlated and occurs in a nested query in the FROM. Emit an error. */
     380   [ +  -  +  - ]:          1 :             diag.e(e.attr_name.pos) << "Correlated attributes are not allowed in the FROM clause.\n";
     381   [ +  -  +  - ]:          1 :             e.type_ = Type::Get_Error();
     382                 :          1 :             return;
     383                 :            : 
     384                 :            :         case SemaContext::S_Where:
     385                 :            :         case SemaContext::S_GroupBy:
     386                 :            :         case SemaContext::S_Having:
     387                 :            :         case SemaContext::S_Select:
     388                 :            :         case SemaContext::S_OrderBy:
     389                 :            :             /* The type of the attribute remains unchanged.  Nothing to be done. */
     390                 :       2251 :             break;
     391                 :            :     }
     392         [ -  + ]:       2443 : }
     393                 :            : 
     394                 :       3671 : void Sema::operator()(Constant &e)
     395                 :            : {
     396                 :       3671 :     int base = 8; // for integers
     397   [ +  +  +  +  :       3671 :     switch (e.tok.type) {
          +  +  +  +  +  
                      - ]
     398                 :            :         default:
     399                 :          0 :             M_unreachable("a constant must be one of the types below");
     400                 :            : 
     401                 :            :         case TK_Null:
     402         [ +  - ]:         40 :             e.type_ = Type::Get_None();
     403                 :         40 :             break;
     404                 :            : 
     405                 :            :         case TK_STRING_LITERAL:
     406   [ +  -  +  -  :         95 :             e.type_ = Type::Get_Char(Type::TY_Scalar, interpret(*e.tok.text).length());
             +  -  +  - ]
     407                 :         95 :             break;
     408                 :            : 
     409                 :            :         case TK_DATE: {
     410                 :            :             int year, month, day;
     411                 :         20 :             sscanf(*e.tok.text, "d'%d-%d-%d'", &year, &month, &day);
     412         [ +  + ]:         20 :             if (year == 0) {
     413                 :          1 :                 diag.e(e.tok.pos) << e << " has invalid year (after year -1 (1 BC) follows year 1 (1 AD)).\n";
     414         [ +  - ]:          1 :                 e.type_ = Type::Get_Error();
     415                 :          1 :                 return;
     416                 :            :             }
     417   [ +  +  +  + ]:         19 :             if (month < 1 or month > 12) {
     418                 :          4 :                 diag.e(e.tok.pos) << e << " has invalid month.\n";
     419         [ +  - ]:          4 :                 e.type_ = Type::Get_Error();
     420                 :          2 :                 return;
     421                 :            :             }
     422   [ +  +  +  + ]:         34 :             if (day < 1 or (month == 2 and day > 29)
     423   [ +  +  +  -  :         16 :                 or ((month == 4 or month == 6 or month == 9 or month == 11) and day > 30)
                   +  - ]
     424   [ +  +  +  +  :         16 :                 or ((month == 1 or month == 3 or month == 5 or month == 7 or month == 8 or month == 10 or month == 12)
          +  +  +  -  +  
             -  +  -  +  
                      - ]
     425                 :         13 :                     and day > 31)) {
     426                 :         14 :                 diag.e(e.tok.pos) << e << " has invalid day.\n";
     427         [ +  - ]:         14 :                 e.type_ = Type::Get_Error();
     428                 :          4 :                 return;
     429                 :            :             }
     430         [ +  - ]:         13 :             e.type_ = Type::Get_Date(Type::TY_Scalar);
     431                 :         13 :             break;
     432                 :            :         }
     433                 :            : 
     434                 :            :         case TK_DATE_TIME: {
     435                 :            :             int year, month, day, hour, minute, second;
     436                 :         21 :             sscanf(*e.tok.text, "d'%d-%d-%d %d:%d:%d'", &year, &month, &day, &hour, &minute, &second);
     437         [ +  + ]:         21 :             if (year == 0) {
     438                 :          1 :                 diag.e(e.tok.pos) << e << " has invalid year (after year -1 (1 BC) follows year 1 (1 AD)).\n";
     439         [ +  - ]:          1 :                 e.type_ = Type::Get_Error();
     440                 :          1 :                 return;
     441                 :            :             }
     442   [ +  +  +  + ]:         20 :             if (month < 1 or month > 12) {
     443                 :          2 :                 diag.e(e.tok.pos) << e << " has invalid month.\n";
     444         [ +  - ]:          2 :                 e.type_ = Type::Get_Error();
     445                 :          2 :                 return;
     446                 :            :             }
     447   [ +  +  +  + ]:         32 :             if (day < 1 or (month == 2 and day > 29)
     448   [ +  +  +  -  :         17 :                 or ((month == 4 or month == 6 or month == 9 or month == 11) and day > 30)
                   +  - ]
     449   [ +  +  +  +  :         16 :                 or ((month == 1 or month == 3 or month == 5 or month == 7 or month == 8 or month == 10 or month == 12)
          +  +  +  -  +  
             -  +  -  +  
                      - ]
     450                 :         14 :                     and day > 31)) {
     451                 :          4 :                 diag.e(e.tok.pos) << e << " has invalid day.\n";
     452         [ +  - ]:          4 :                 e.type_ = Type::Get_Error();
     453                 :          4 :                 return;
     454                 :            :             }
     455                 :         14 :             M_insist(hour >= 0);
     456         [ +  + ]:         14 :             if (hour > 23) {
     457                 :          1 :                 diag.e(e.tok.pos) << e << " has invalid hour.\n";
     458         [ +  - ]:          1 :                 e.type_ = Type::Get_Error();
     459                 :          1 :                 return;
     460                 :            :             }
     461                 :         13 :             M_insist(minute >= 0);
     462         [ +  + ]:         13 :             if (minute > 59) {
     463                 :          1 :                 diag.e(e.tok.pos) << e << " has invalid minute.\n";
     464         [ +  - ]:          1 :                 e.type_ = Type::Get_Error();
     465                 :          1 :                 return;
     466                 :            :             }
     467                 :         12 :             M_insist(second >= 0);
     468         [ +  + ]:         12 :             if (second > 59) {
     469                 :          1 :                 diag.e(e.tok.pos) << e << " has invalid second.\n";
     470         [ -  + ]:          1 :                 e.type_ = Type::Get_Error();
     471                 :          1 :                 return;
     472                 :            :             }
     473         [ -  + ]:         11 :             e.type_ = Type::Get_Datetime(Type::TY_Scalar);
     474                 :         11 :             break;
     475                 :            :         }
     476                 :            : 
     477                 :            :         case TK_True:
     478                 :            :         case TK_False:
     479         [ +  - ]:        222 :             e.type_ = Type::Get_Boolean(Type::TY_Scalar);
     480                 :        222 :             break;
     481                 :            : 
     482                 :            :         case TK_HEX_INT:
     483                 :         20 :             base += 6;
     484                 :            :         case TK_DEC_INT:
     485                 :       3122 :             base += 2;
     486                 :            :         case TK_OCT_INT: {
     487                 :       3196 :             int64_t value = strtol(*e.tok.text, nullptr, base);
     488         [ +  + ]:       3196 :             if (value == int32_t(value))
     489         [ +  - ]:       3177 :                 e.type_ = Type::Get_Integer(Type::TY_Scalar, 4);
     490                 :            :             else
     491         [ +  - ]:         19 :                 e.type_ = Type::Get_Integer(Type::TY_Scalar, 8);
     492                 :       3196 :             break;
     493                 :            :         }
     494                 :            : 
     495                 :            :         case TK_DEC_FLOAT:
     496                 :            :         case TK_HEX_FLOAT:
     497         [ +  - ]:         77 :             e.type_ = Type::Get_Double(Type::TY_Scalar); // TODO: 32-bit floating-point constants
     498                 :         77 :             break;
     499                 :            :     }
     500                 :       3671 : }
     501                 :            : 
     502                 :         78 : void Sema::operator()(FnApplicationExpr &e)
     503                 :            : {
     504                 :         78 :     SemaContext &Ctx = get_context();
     505                 :         78 :     Catalog &C = Catalog::Get();
     506                 :            : 
     507                 :            :     /* Analyze function name. */
     508                 :         78 :     auto d = cast<Designator>(e.fn.get());
     509   [ +  -  -  + ]:         78 :     if (not d or not d->is_identifier()) {
     510                 :          0 :         diag.e(d->attr_name.pos) << *d << " is not a valid function.\n";
     511         [ #  # ]:          0 :         d->type_ = e.type_ = Type::Get_Error();
     512                 :          0 :         return;
     513                 :            :     }
     514                 :         78 :     M_insist(bool(d));
     515                 :         78 :     M_insist(not d->type_, "This identifier has already been analyzed.");
     516                 :            : 
     517                 :            :     /* Analyze arguments. */
     518         [ +  + ]:        156 :     for (auto &arg : e.args)
     519                 :         78 :         (*this)(*arg);
     520                 :            : 
     521                 :            :     /* Lookup the function. */
     522         [ +  - ]:         78 :     if (C.has_database_in_use()) {
     523                 :         78 :         const auto &DB = C.get_database_in_use();
     524                 :            :         try {
     525   [ +  -  +  + ]:         78 :             e.func_ = DB.get_function(d->attr_name.text.assert_not_none());
     526         [ -  + ]:         78 :         } catch (std::out_of_range) {
     527   [ +  -  +  -  :          1 :             diag.e(d->attr_name.pos) << "Function " << d->attr_name.text << " is not defined in database " << DB.name
          +  -  +  -  +  
                      - ]
     528         [ +  - ]:          1 :                 << ".\n";
     529   [ +  -  +  - ]:          1 :             e.type_ = Type::Get_Error();
     530                 :            :             return;
     531         [ #  # ]:          1 :         }
     532                 :         77 :     } else {
     533                 :            :         try {
     534   [ #  #  #  # ]:          0 :             e.func_ = C.get_function(d->attr_name.text.assert_not_none());
     535         [ #  # ]:          0 :         } catch (std::out_of_range) {
     536   [ #  #  #  #  :          0 :             diag.e(d->attr_name.pos) << "Function " << d->attr_name.text << " is not defined.\n";
             #  #  #  # ]
     537   [ #  #  #  # ]:          0 :             e.type_ = Type::Get_Error();
     538                 :            :             return;
     539         [ #  # ]:          0 :         }
     540                 :            :     }
     541                 :         77 :     M_insist(e.func_);
     542                 :            : 
     543                 :            :     /* Infer the type of the function.  Functions are defined in an abstract way, where the type of the parameters is
     544                 :            :      * not specified.  We must infer the parameter types and the return type of the function. */
     545   [ -  +  -  +  :         77 :     switch (e.func_->fnid) {
                      + ]
     546                 :            :         default:
     547                 :          0 :             M_unreachable("Function not implemented");
     548                 :            : 
     549                 :            :         case Function::FN_UDF:
     550                 :          0 :             diag.e(d->attr_name.pos) << "User-defined functions are not yet supported.\n";
     551         [ #  # ]:          0 :             d->type_ = e.type_ = Type::Get_Error();
     552                 :          0 :             return;
     553                 :            : 
     554                 :            :         case Function::FN_MIN:
     555                 :            :         case Function::FN_MAX:
     556                 :            :         case Function::FN_SUM:
     557                 :            :         case Function::FN_AVG: {
     558         [ +  + ]:         67 :             if (e.args.size() == 0) {
     559                 :          1 :                 diag.e(d->attr_name.pos) << "Missing argument for aggregate " << *d << ".\n";
     560         [ +  - ]:          1 :                 d->type_ = e.type_ = Type::Get_Error();
     561                 :          1 :                 return;
     562                 :            :             }
     563         [ +  + ]:         66 :             if (e.args.size() > 1) {
     564                 :          1 :                 diag.e(d->attr_name.pos) << "Too many arguments for aggregate " << *d << ".\n";
     565         [ +  - ]:          1 :                 d->type_ = e.type_ = Type::Get_Error();
     566                 :          1 :                 return;
     567                 :            :             }
     568                 :         65 :             M_insist(e.args.size() == 1);
     569                 :         65 :             auto &arg = *e.args[0];
     570         [ +  + ]:         65 :             if (arg.type()->is_error()) {
     571                 :            :                 /* skip argument of error type */
     572         [ +  - ]:          1 :                 d->type_ = e.type_ = Type::Get_Error();
     573                 :          1 :                 return;
     574                 :            :             }
     575         [ +  + ]:         64 :             if (not arg.type()->is_numeric()) {
     576                 :            :                 /* invalid argument type */
     577                 :          1 :                 diag.e(d->attr_name.pos) << "Argument of aggregate function must be of numeric type.\n";
     578         [ +  - ]:          1 :                 d->type_ = e.type_ = Type::Get_Error();
     579                 :          1 :                 return;
     580                 :            :             }
     581                 :         63 :             M_insist(arg.type()->is_numeric());
     582                 :         63 :             const Numeric *arg_type = cast<const Numeric>(arg.type());
     583         [ +  + ]:         63 :             if (not arg_type->is_vectorial()) {
     584                 :          3 :                 diag.w(d->attr_name.pos) << "Argument of aggregate is not of vectorial type.  "
     585                 :            :                                             "(Aggregates over scalars are discouraged.)\n";
     586                 :          3 :             }
     587                 :            : 
     588   [ -  +  +  + ]:         63 :             switch (e.func_->fnid) {
     589                 :            :                 default:
     590                 :          0 :                     M_unreachable("Invalid function");
     591                 :            : 
     592                 :            :                 case Function::FN_MIN:
     593                 :            :                 case Function::FN_MAX: {
     594                 :            :                     /* MIN/MAX maintain type */
     595                 :         50 :                     e.type_ = arg_type->as_scalar();
     596   [ +  -  +  -  :         50 :                     d->type_ = Type::Get_Function(e.type_, { arg.type() });
                   +  - ]
     597                 :         50 :                     break;
     598                 :            :                 }
     599                 :            : 
     600                 :            :                 case Function::FN_AVG: {
     601                 :            :                     /* AVG always uses double precision floating-point */
     602         [ +  - ]:          6 :                     e.type_ = Type::Get_Double(Type::TY_Scalar);
     603   [ +  -  +  -  :          6 :                     d->type_ = Type::Get_Function(e.type_, { arg.type() });
                   +  - ]
     604                 :          6 :                     break;
     605                 :            :                 }
     606                 :            : 
     607                 :            :                 case Function::FN_SUM: {
     608                 :            :                     /* SUM can overflow.  Always assume type of highest precision. */
     609   [ -  +  +  + ]:          7 :                     switch (arg_type->kind) {
     610                 :            :                         case Numeric::N_Int:
     611         [ +  - ]:          5 :                             e.type_ = Type::Get_Integer(Type::TY_Scalar, 8);
     612                 :          5 :                             break;
     613                 :            : 
     614                 :            :                         case Numeric::N_Float:
     615         [ +  - ]:          1 :                             e.type_ = Type::Get_Double(Type::TY_Scalar);
     616                 :          1 :                             break;
     617                 :            : 
     618                 :            :                         case Numeric::N_Decimal:
     619         [ +  - ]:          1 :                             e.type_ = Type::Get_Decimal(Type::TY_Scalar, Numeric::MAX_DECIMAL_PRECISION,
     620                 :          1 :                                                         arg_type->scale);
     621                 :          1 :                             break;
     622                 :            :                     }
     623   [ +  -  +  -  :          7 :                     d->type_ = Type::Get_Function(e.type(), { e.type() });
                   +  - ]
     624                 :          7 :                     break;
     625                 :            :                 }
     626                 :            :             }
     627                 :         63 :             break;
     628                 :            :         }
     629                 :            : 
     630                 :            :         case Function::FN_COUNT: {
     631         [ +  + ]:          2 :             if (e.args.size() > 1) {
     632                 :          1 :                 diag.e(d->attr_name.pos) << "Too many arguments for aggregate " << *d << ".\n";
     633         [ +  - ]:          1 :                 e.type_ = Type::Get_Error();
     634                 :          1 :                 return;
     635                 :            :             }
     636                 :            : 
     637                 :            :             /* TODO If argument is given, check whether it can be NULL.  If not, COUNT(arg) == COUNT(*) */
     638                 :            : 
     639         [ +  - ]:          1 :             e.type_ = Type::Get_Integer(Type::TY_Scalar, 8);
     640   [ +  -  +  - ]:          1 :             d->type_ = Type::Get_Function(e.type_, {});
     641                 :          1 :             break;
     642                 :            :         }
     643                 :            : 
     644                 :            :         case Function::FN_ISNULL: {
     645         [ +  + ]:          8 :             if (e.args.size() == 0) {
     646                 :          1 :                 diag.e(d->attr_name.pos) << "Missing argument for aggregate " << *d << ".\n";
     647         [ +  - ]:          1 :                 e.type_ = Type::Get_Error();
     648                 :          1 :                 return;
     649                 :            :             }
     650         [ +  + ]:          7 :             if (e.args.size() > 1) {
     651                 :          1 :                 diag.e(d->attr_name.pos) << "Too many arguments for aggregate " << *d << ".\n";
     652         [ +  - ]:          1 :                 e.type_ = Type::Get_Error();
     653                 :          1 :                 return;
     654                 :            :             }
     655                 :          6 :             M_insist(e.args.size() == 1);
     656                 :          6 :             auto &arg = *e.args[0];
     657                 :            : 
     658         [ +  + ]:          6 :             if (arg.type()->is_error()) {
     659         [ +  - ]:          1 :                 e.type_ = Type::Get_Error();
     660                 :          1 :                 return;
     661                 :            :             }
     662                 :          5 :             const PrimitiveType *arg_type = cast<const PrimitiveType>(arg.type());
     663         [ +  + ]:          5 :             if (not arg_type) {
     664                 :          1 :                 diag.e(d->attr_name.pos) << "Function ISNULL can only be applied to expressions of primitive type.\n";
     665         [ +  - ]:          1 :                 e.type_ = Type::Get_Error();
     666                 :          1 :                 return;
     667                 :            :             }
     668                 :            : 
     669   [ +  -  +  -  :          4 :             d->type_ = Type::Get_Function(Type::Get_Boolean(arg_type->category), { arg.type() });
          +  -  +  -  +  
                      - ]
     670         [ +  - ]:          4 :             e.type_= Type::Get_Boolean(arg_type->category);
     671                 :          4 :             break;
     672                 :            :         }
     673                 :            :     }
     674                 :            : 
     675                 :         68 :     M_insist(d->type_);
     676         [ -  + ]:         68 :     M_insist(d->type()->is_error() or cast<const FnType>(d->type()));
     677                 :         68 :     M_insist(e.type_);
     678                 :         68 :     M_insist(not e.type()->is_error());
     679                 :         68 :     M_insist(e.type()->is_primitive());
     680                 :            : 
     681   [ -  +  +  +  :         68 :     switch (Ctx.stage) {
             -  +  +  - ]
     682                 :            :         case SemaContext::S_From:
     683                 :          0 :             M_unreachable("Function application in FROM clause is impossible");
     684                 :            : 
     685                 :            :         case SemaContext::S_Where:
     686         [ +  + ]:          3 :             if (e.func_->is_aggregate()) {
     687                 :          1 :                 diag.e(d->attr_name.pos) << "Aggregate functions are not allowed in WHERE clause.\n";
     688                 :          1 :                 return;
     689                 :            :             }
     690                 :          2 :             break;
     691                 :            : 
     692                 :            :         case SemaContext::S_GroupBy:
     693         [ +  + ]:          3 :             if (e.func_->is_aggregate()) {
     694                 :          2 :                 diag.e(d->attr_name.pos) << "Aggregate functions are not allowed in GROUP BY clause.\n";
     695                 :          2 :                 return;
     696                 :            :             }
     697                 :          1 :             break;
     698                 :            : 
     699                 :            :         case SemaContext::S_Having:
     700                 :            :             /* nothing to be done */
     701                 :         11 :             break;
     702                 :            : 
     703                 :            :         case SemaContext::S_OrderBy:
     704                 :            :             /* TODO */
     705                 :          3 :             break;
     706                 :            : 
     707                 :            :         case SemaContext::S_Select:
     708                 :            :             /* TODO */
     709                 :         48 :             break;
     710                 :            : 
     711                 :            :         case SemaContext::S_Limit:
     712                 :            :             /* TODO */
     713                 :          0 :             break;
     714                 :            :     }
     715                 :         79 : }
     716                 :            : 
     717                 :         88 : void Sema::operator()(UnaryExpr &e)
     718                 :            : {
     719                 :            :     /* Analyze sub-expression. */
     720                 :         88 :     (*this)(*e.expr);
     721                 :            : 
     722                 :            :     /* If the sub-expression is erroneous, so is this expression. */
     723         [ +  + ]:         88 :     if (e.expr->type()->is_error()) {
     724         [ +  - ]:          1 :         e.type_ = Type::Get_Error();
     725                 :          1 :         return;
     726                 :            :     }
     727                 :            : 
     728      [ +  +  - ]:         87 :     switch (e.op().type) {
     729                 :            :         default:
     730                 :          0 :             M_unreachable("invalid unary expression");
     731                 :            : 
     732                 :            :         case TK_Not:
     733         [ +  + ]:         17 :             if (not e.expr->type()->is_boolean()) {
     734   [ +  -  +  -  :          1 :                 diag.e(e.op().pos) << "Invalid expression " << e << " must be boolean.\n";
             +  -  +  - ]
     735         [ -  + ]:          1 :                 e.type_ = Type::Get_Error();
     736                 :          1 :                 return;
     737                 :            :             }
     738                 :         16 :             break;
     739                 :            : 
     740                 :            :         case TK_PLUS:
     741                 :            :         case TK_MINUS:
     742                 :            :         case TK_TILDE:
     743         [ +  + ]:         70 :             if (not e.expr->type()->is_numeric()) {
     744   [ +  -  +  -  :          6 :                 diag.e(e.op().pos) << "Invalid expression " << e << " must be numeric.\n";
             +  -  +  - ]
     745         [ -  + ]:          6 :                 e.type_ = Type::Get_Error();
     746                 :          6 :                 return;
     747                 :            :             }
     748                 :         64 :             break;
     749                 :            :     }
     750                 :            : 
     751                 :         80 :     e.type_ = e.expr->type();
     752                 :         88 : }
     753                 :            : 
     754                 :        812 : void Sema::operator()(BinaryExpr &e)
     755                 :            : {
     756                 :            :     /* Analyze sub-expressions. */
     757                 :        812 :     (*this)(*e.lhs);
     758                 :        812 :     (*this)(*e.rhs);
     759                 :            : 
     760                 :            :     /* If at least one of the sub-expressions is erroneous, so is this expression. */
     761   [ +  +  +  + ]:        812 :     if (e.lhs->type()->is_error() or e.rhs->type()->is_error()) {
     762         [ +  - ]:         21 :         e.type_ = Type::Get_Error();
     763                 :         21 :         return;
     764                 :            :     }
     765                 :            : 
     766                 :            :     /* Validate that lhs and rhs are compatible with binary operator. */
     767   [ +  +  +  +  :        791 :     switch (e.op().type) {
                +  +  - ]
     768                 :            :         default:
     769                 :          0 :             M_unreachable("Invalid binary operator.");
     770                 :            : 
     771                 :            :         /* Arithmetic operations are only valid for numeric types.  Compute the type of the binary expression that is
     772                 :            :          * precise enough.  */
     773                 :            :         case TK_PLUS:
     774                 :            :         case TK_MINUS:
     775                 :            :         case TK_ASTERISK:
     776                 :            :         case TK_SLASH:
     777                 :            :         case TK_PERCENT: {
     778                 :            :             /* Verify that both operands are of numeric type. */
     779                 :         85 :             const Numeric *ty_lhs = cast<const Numeric>(e.lhs->type());
     780                 :         85 :             const Numeric *ty_rhs = cast<const Numeric>(e.rhs->type());
     781   [ +  +  +  + ]:         85 :             if (not ty_lhs or not ty_rhs) {
     782   [ +  -  +  -  :         21 :                 diag.e(e.op().pos) << "Invalid expression " << e << ", operands must be of numeric type.\n";
             +  -  +  - ]
     783         [ +  - ]:         21 :                 e.type_ = Type::Get_Error();
     784                 :         21 :                 return;
     785                 :            :             }
     786                 :         64 :             M_insist(ty_lhs);
     787                 :         64 :             M_insist(ty_rhs);
     788                 :            : 
     789                 :            :             /* Compute type of the binary expression. */
     790                 :         64 :             e.type_ = e.common_operand_type = arithmetic_join(ty_lhs, ty_rhs);
     791                 :         64 :             break;
     792                 :            :         }
     793                 :            : 
     794                 :            :         case TK_DOTDOT: {
     795                 :            :             /* Concatenation of two strings. */
     796                 :         14 :             auto ty_lhs = cast<const CharacterSequence>(e.lhs->type());
     797                 :         14 :             auto ty_rhs = cast<const CharacterSequence>(e.rhs->type());
     798   [ +  +  +  + ]:         14 :             if (not ty_lhs or not ty_rhs) {
     799   [ +  -  +  -  :         11 :                 diag.e(e.op().pos) << "Invalid expression " << e << ", concatenation requires string operands.\n";
             +  -  +  - ]
     800         [ +  - ]:         11 :                 e.type_ = Type::Get_Error();
     801                 :         11 :                 return;
     802                 :            :             }
     803                 :          3 :             M_insist(ty_lhs);
     804                 :          3 :             M_insist(ty_rhs);
     805                 :            : 
     806                 :            :             /* Scalar and scalar yield a scalar.  Otherwise, expression yields a vectorial. */
     807                 :          3 :             Type::category_t c = std::max(ty_lhs->category, ty_rhs->category);
     808                 :            : 
     809         [ +  - ]:          3 :             e.type_ = Type::Get_Char(c, ty_lhs->length + ty_rhs->length);
     810                 :          3 :             break;
     811                 :            :         }
     812                 :            : 
     813                 :            :         case TK_LESS:
     814                 :            :         case TK_LESS_EQUAL:
     815                 :            :         case TK_GREATER:
     816                 :            :         case TK_GREATER_EQUAL: {
     817         [ +  + ]:         74 :             if (auto ty_lhs = cast<const Numeric>(e.lhs->type())) {
     818                 :            :                 /* Verify that both operands are of numeric type. */
     819                 :         59 :                 auto ty_rhs = cast<const Numeric>(e.rhs->type());
     820   [ +  -  +  + ]:         59 :                 if (not ty_lhs or not ty_rhs) {
     821   [ +  -  +  -  :          6 :                     diag.e(e.op().pos) << "Invalid expression " << e << ", both operands must be of numeric type.\n";
             +  -  +  - ]
     822         [ +  - ]:          6 :                     e.type_ = Type::Get_Error();
     823                 :          6 :                     return;
     824                 :            :                 }
     825                 :         53 :                 M_insist(ty_lhs);
     826                 :         53 :                 M_insist(ty_rhs);
     827                 :            : 
     828                 :            :                 /* Scalar and scalar yield a scalar.  Otherwise, expression yields a vectorial. */
     829                 :         53 :                 Type::category_t c = std::max(ty_lhs->category, ty_rhs->category);
     830                 :            : 
     831                 :            :                 /* Comparisons always have boolean type. */
     832         [ +  - ]:         53 :                 e.type_ = Type::Get_Boolean(c);
     833                 :         53 :                 e.common_operand_type = arithmetic_join(ty_lhs, ty_rhs);
     834         [ +  + ]:         68 :             } else if (auto ty_lhs = cast<const CharacterSequence>(e.lhs->type())) {
     835                 :            :                 /* Verify that both operands are character sequences. */
     836                 :          6 :                 auto ty_rhs = cast<const CharacterSequence>(e.rhs->type());
     837   [ +  -  +  + ]:          6 :                 if (not ty_lhs or not ty_rhs) {
     838   [ +  -  +  -  :          2 :                     diag.e(e.op().pos) << "Invalid expression " << e << ", both operands must be strings.\n";
             +  -  +  - ]
     839         [ +  - ]:          2 :                     e.type_ = Type::Get_Error();
     840                 :          2 :                     return;
     841                 :            :                 }
     842                 :          4 :                 M_insist(ty_lhs);
     843                 :          4 :                 M_insist(ty_rhs);
     844                 :            : 
     845                 :            :                 /* Scalar and scalar yield a scalar.  Otherwise, expression yields a vectorial. */
     846                 :          4 :                 Type::category_t c = std::max(ty_lhs->category, ty_rhs->category);
     847                 :            : 
     848                 :            :                 /* Comparisons always have boolean type. */
     849         [ +  - ]:          4 :                 e.type_ = Type::Get_Boolean(c);
     850         [ +  + ]:         13 :             } else if (auto ty_lhs = cast<const Date>(e.lhs->type())) {
     851                 :            :                 /* Verify that both operands are dates. */
     852                 :          2 :                 auto ty_rhs = cast<const Date>(e.rhs->type());
     853   [ +  -  +  + ]:          2 :                 if (not ty_lhs or not ty_rhs) {
     854   [ +  -  +  -  :          1 :                     diag.e(e.op().pos) << "Invalid expression " << e << ", both operands must be dates.\n";
             +  -  +  - ]
     855         [ +  - ]:          1 :                     e.type_ = Type::Get_Error();
     856                 :          1 :                     return;
     857                 :            :                 }
     858                 :          1 :                 M_insist(ty_lhs);
     859                 :          1 :                 M_insist(ty_rhs);
     860                 :            : 
     861                 :            :                 /* Scalar and scalar yield a scalar.  Otherwise, expression yields a vectorial. */
     862                 :          1 :                 Type::category_t c = std::max(ty_lhs->category, ty_rhs->category);
     863                 :            : 
     864                 :            :                 /* Comparisons always have boolean type. */
     865         [ +  - ]:          1 :                 e.type_ = Type::Get_Boolean(c);
     866         [ -  + ]:          8 :             } else if (auto ty_lhs = cast<const DateTime>(e.lhs->type())) {
     867                 :            :                 /* Verify that both operands are datetimes. */
     868                 :          0 :                 auto ty_rhs = cast<const DateTime>(e.rhs->type());
     869   [ #  #  #  # ]:          0 :                 if (not ty_lhs or not ty_rhs) {
     870   [ #  #  #  #  :          0 :                     diag.e(e.op().pos) << "Invalid expression " << e << ", both operands must be datetimes.\n";
             #  #  #  # ]
     871         [ #  # ]:          0 :                     e.type_ = Type::Get_Error();
     872                 :          0 :                     return;
     873                 :            :                 }
     874                 :          0 :                 M_insist(ty_lhs);
     875                 :          0 :                 M_insist(ty_rhs);
     876                 :            : 
     877                 :            :                 /* Scalar and scalar yield a scalar.  Otherwise, expression yields a vectorial. */
     878                 :          0 :                 Type::category_t c = std::max(ty_lhs->category, ty_rhs->category);
     879                 :            : 
     880                 :            :                 /* Comparisons always have boolean type. */
     881         [ #  # ]:          0 :                 e.type_ = Type::Get_Boolean(c);
     882                 :          0 :             } else {
     883   [ +  -  +  -  :          7 :                 diag.e(e.op().pos) << "Invalid expression " << e << ", operator not supported for given operands.\n";
             +  -  +  - ]
     884         [ +  - ]:          7 :                 e.type_ = Type::Get_Error();
     885                 :          7 :                 return;
     886                 :            :             }
     887                 :         58 :             break;
     888                 :            :         }
     889                 :            : 
     890                 :            :         case TK_EQUAL:
     891                 :            :         case TK_BANG_EQUAL: {
     892         [ +  + ]:        386 :             if (not is_comparable(e.lhs->type(), e.rhs->type())) {
     893   [ +  -  +  -  :         11 :                 diag.e(e.op().pos) << "Invalid expression " << e << ", operands are incomparable.\n";
             +  -  +  - ]
     894         [ +  - ]:         11 :                 e.type_ = Type::Get_Error();
     895                 :         11 :                 return;
     896                 :            :             }
     897                 :        375 :             const PrimitiveType *ty_lhs = as<const PrimitiveType>(e.lhs->type());
     898                 :        375 :             const PrimitiveType *ty_rhs = as<const PrimitiveType>(e.rhs->type());
     899                 :            : 
     900                 :            :             /* Scalar and scalar yield a scalar.  Otherwise, expression yields a vectorial. */
     901                 :        375 :             Type::category_t c = std::max(ty_lhs->category, ty_rhs->category);
     902                 :            : 
     903                 :            :             /* Comparisons always have boolean type. */
     904         [ +  - ]:        375 :             e.type_ = Type::Get_Boolean(c);
     905         [ +  + ]:        375 :             if (auto ty_lhs = cast<const Numeric>(e.lhs->type()))
     906                 :        366 :                 e.common_operand_type = arithmetic_join(ty_lhs, as<const Numeric>(e.rhs->type()));
     907                 :        375 :             break;
     908                 :            :         }
     909                 :            : 
     910                 :            :         case TK_Like: {
     911                 :         14 :             auto ty_lhs = cast<const CharacterSequence>(e.lhs->type());
     912                 :         14 :             auto ty_rhs = cast<const CharacterSequence>(e.rhs->type());
     913   [ +  +  +  + ]:         14 :             if (not ty_lhs or not ty_rhs) {
     914   [ +  -  +  -  :          9 :                 diag.e(e.op().pos) << "Invalid expression " << e << ", operands must be character sequences.\n";
             +  -  +  - ]
     915         [ +  - ]:          9 :                 e.type_ = Type::Get_Error();
     916                 :          9 :                 return;
     917                 :            :             }
     918                 :          5 :             M_insist(ty_lhs);
     919                 :          5 :             M_insist(ty_rhs);
     920                 :            : 
     921                 :            :             /* Scalar and scalar yield a scalar.  Otherwise, expression yields a vectorial. */
     922                 :          5 :             Type::category_t c = std::max(ty_lhs->category, ty_rhs->category);
     923                 :            : 
     924                 :            :             /* Comparisons always have boolean type. */
     925         [ +  - ]:          5 :             e.type_ = Type::Get_Boolean(c);
     926                 :          5 :             break;
     927                 :            :         }
     928                 :            : 
     929                 :            :         case TK_And:
     930                 :            :         case TK_Or: {
     931                 :        218 :             const Boolean *ty_lhs = cast<const Boolean>(e.lhs->type());
     932                 :        218 :             const Boolean *ty_rhs = cast<const Boolean>(e.rhs->type());
     933                 :            : 
     934                 :            :             /* Both operands must be of boolean type. */
     935   [ +  +  +  + ]:        218 :             if (not ty_lhs or not ty_rhs) {
     936   [ +  -  +  -  :         10 :                 diag.e(e.op().pos) << "Invalid expression " << e << ", operands must be of boolean type.\n";
             +  -  +  - ]
     937         [ +  - ]:         10 :                 e.type_ = Type::Get_Error();
     938                 :         10 :                 return;
     939                 :            :             }
     940                 :            : 
     941                 :            :             /* Scalar and scalar yield a scalar.  Otherwise, expression yields a vectorial. */
     942                 :        208 :             Type::category_t c = std::max(ty_lhs->category, ty_rhs->category);
     943                 :            : 
     944                 :            :             /* Logical operators always have boolean type. */
     945         [ +  - ]:        208 :             e.type_ = Type::Get_Boolean(c);
     946                 :        208 :             break;
     947                 :            :         }
     948                 :            :     }
     949                 :        812 : }
     950                 :            : 
     951                 :         97 : void Sema::operator()(QueryExpr &e)
     952                 :            : {
     953                 :         97 :     M_insist(is<SelectStmt>(*e.query), "nested statements are always select statements");
     954                 :            : 
     955                 :         97 :     SemaContext &Ctx = get_context();
     956                 :            : 
     957                 :            :     /* Evaluate the nested statement in a fresh sema context. */
     958         [ +  - ]:         97 :     push_context(*e.query, e.alias());
     959                 :         97 :     (*this)(*e.query);
     960                 :         97 :     M_insist(not contexts_.empty());
     961                 :         97 :     SemaContext inner_ctx = pop_context();
     962                 :            : 
     963                 :            :     /* TODO an EXISTS operator allows multiple results */
     964         [ +  + ]:         97 :     if (1 != inner_ctx.results.size()) {
     965   [ +  +  +  -  :         53 :         diag.e(e.tok.pos) << "Invalid expression:\n" << e << ",\nnested statement must return a single column.\n";
             +  -  +  - ]
     966   [ +  -  +  - ]:          3 :         e.type_ = Type::Get_Error();
     967                 :          3 :         return;
     968                 :            :     }
     969         [ +  - ]:         44 :     M_insist(1 == inner_ctx.results.size());
     970         [ +  - ]:         44 :     Expr &res = inner_ctx.results.begin()->second.expr();
     971                 :            : 
     972   [ +  -  +  -  :         44 :     if (not res.type()->is_primitive()) {
                   -  + ]
     973   [ #  #  #  #  :          0 :         diag.e(e.tok.pos) << "Invalid expression:\n" << e << ",\nnested statement must return a primitive value.\n";
             #  #  #  # ]
     974   [ #  #  #  # ]:          0 :         e.type_ = Type::Get_Error();
     975                 :          0 :         return;
     976                 :            :     }
     977         [ +  - ]:         44 :     auto *pt = as<const PrimitiveType>(res.type_);
     978                 :         44 :     e.type_ = pt;
     979                 :            : 
     980         [ +  + ]:         44 :     switch (Ctx.stage) {
     981                 :            :         default: {
     982   [ +  -  +  - ]:          2 :             diag.e(e.tok.pos) << "Nested statements are not allowed in this stage.\n";
     983   [ +  -  +  - ]:          2 :             e.type_ = Type::Get_Error();
     984                 :          2 :             return;
     985                 :            :         }
     986                 :            :         case SemaContext::S_Where:
     987                 :            :         case SemaContext::S_Having:
     988                 :            :             /* TODO The result must not be a single scalar value in general. */
     989                 :            : 
     990                 :            :         case SemaContext::S_Select: {
     991                 :            :             /* The result of the nested query must be a single scalar value. */
     992                 :            : 
     993   [ +  -  +  + ]:         42 :             if (not pt->is_scalar()) {
     994   [ +  -  +  -  :          6 :                 diag.e(e.tok.pos) << "Invalid expression:\n" << e
                   +  - ]
     995         [ +  - ]:          6 :                                    << ",\nnested statement must return a scalar value.\n";
     996   [ +  -  +  - ]:          6 :                 e.type_ = Type::Get_Error();
     997                 :          6 :                 return;
     998                 :            :             }
     999                 :            : 
    1000         [ +  - ]:         36 :             auto is_fn = is<FnApplicationExpr>(res);
    1001         [ +  - ]:         36 :             auto is_const = res.is_constant();
    1002         [ +  - ]:         36 :             auto &q = as<const SelectStmt>(*e.query);
    1003                 :            :             /* The result is a single value iff it is a constant and there is no from clause or
    1004                 :            :              * iff it is an aggregate and there is no group_by clause. */
    1005   [ +  +  +  +  :         36 :             if (not(is_const and not q.from) and not(is_fn and not q.group_by)) {
                   +  + ]
    1006   [ +  +  +  -  :         57 :                 diag.e(e.tok.pos) << "Invalid expression:\n" << e
                   +  - ]
    1007         [ +  - ]:          7 :                                    << ",\nnested statement must return a single value.\n";
    1008   [ +  -  +  - ]:          7 :                 e.type_ = Type::Get_Error();
    1009                 :          7 :                 return;
    1010                 :            :             }
    1011                 :         29 :             break;
    1012                 :            :         }
    1013                 :            :     }
    1014         [ -  + ]:        147 : }
    1015                 :            : 
    1016                 :            : /*===== Clause =======================================================================================================*/
    1017                 :            : 
    1018                 :          0 : void Sema::operator()(ErrorClause&)
    1019                 :            : {
    1020                 :            :     /* nothing to be done */
    1021                 :          0 : }
    1022                 :            : 
    1023                 :        574 : void Sema::operator()(SelectClause &c)
    1024                 :            : {
    1025                 :        574 :     SemaContext &Ctx = get_context();
    1026                 :        574 :     Ctx.stage = SemaContext::S_Select;
    1027                 :        574 :     Catalog &C = Catalog::Get();
    1028                 :            : 
    1029                 :        574 :     bool has_vectorial = false;
    1030                 :        574 :     bool has_scalar = false;
    1031                 :        574 :     uint64_t const_counter = 0;
    1032                 :        574 :     M_insist(Ctx.results.empty());
    1033                 :        574 :     unsigned result_counter = 0;
    1034                 :            : 
    1035         [ +  + ]:        574 :     if (c.select_all) {
    1036                 :            :         /* Expand the `SELECT *` by creating dummy expressions for all accessible values of all sources. */
    1037                 :        265 :         auto &stmt = as<const SelectStmt>(Ctx.stmt);
    1038                 :            : 
    1039         [ +  + ]:        265 :         if (stmt.group_by) {
    1040                 :            :             /* If the statement contains a GROUP BY clause, we must include all grouping keys in the result. */
    1041                 :         30 :             auto &group_by = as<const GroupByClause>(*stmt.group_by);
    1042         [ -  + ]:         30 :             has_scalar = has_scalar or not group_by.group_by.empty();
    1043         [ +  + ]:        130 :             for (auto &[expr, alias] : group_by.group_by) {
    1044                 :         30 :                 std::unique_ptr<Designator> d;
    1045   [ +  -  +  + ]:         30 :                 if (alias) { // alias was given
    1046   [ +  -  +  -  :          7 :                     d = create_designator(alias.text.assert_not_none(), expr->tok, *expr);
                   +  - ]
    1047   [ +  -  +  + ]:         30 :                 } else if (auto D = cast<const ast::Designator>(expr.get())) { // no alias, but designator -> keep name
    1048   [ +  -  +  -  :         18 :                     d = create_designator(D->attr_name.text.assert_not_none(), D->tok, *D);
                   +  - ]
    1049                 :         18 :                 } else { // no designator, no alias -> derive name
    1050         [ +  - ]:          5 :                     std::ostringstream oss;
    1051         [ +  - ]:          5 :                     oss << *expr;
    1052   [ +  -  +  -  :          5 :                     d = create_designator(C.pool(oss.str().c_str()), expr->tok, *expr);
             +  -  +  - ]
    1053                 :          5 :                 }
    1054   [ +  -  +  -  :         30 :                 if (auto ty = cast<const PrimitiveType>(d->type()))
                   +  + ]
    1055         [ +  - ]:         29 :                     d->type_ = ty->as_scalar();
    1056                 :            :                 else
    1057   [ +  -  +  -  :          1 :                     M_insist(d->type()->is_error(), "grouping key must be of primitive type");
                   +  - ]
    1058         [ +  - ]:         30 :                 auto attr_name = d->attr_name.text.assert_not_none();
    1059         [ +  - ]:         30 :                 auto &ref = c.expanded_select_all.emplace_back(std::move(d));
    1060         [ +  - ]:         30 :                 (*this)(*ref);
    1061   [ +  -  +  -  :         60 :                 Ctx.results.emplace(std::move(attr_name), SemaContext::result_t(*ref, result_counter++, alias.text));
             +  -  +  - ]
    1062                 :         30 :             }
    1063         [ +  + ]:        265 :         } else if (stmt.having) {
    1064                 :            :             /* A statement with a HAVING clause but without a GROUP BY clause may only have literals in its SELECT
    1065                 :            :              * clause.  Therefore, '*' has no meaning and we should emit a warning. */
    1066                 :          4 :             diag.w(c.select_all.pos) << "The '*' has no meaning in this query.  Did you forget the GROUP BY clause?.\n";
    1067                 :          4 :         } else {
    1068                 :            :             /* The '*' in the SELECT clause selects all attributes of all sources. */
    1069         [ +  + ]:       1862 :             for (auto &[src_name, src] : Ctx.sources) {
    1070         [ +  + ]:        459 :                 if (auto ref = std::get_if<std::reference_wrapper<const Table>>(&src.first)) {
    1071                 :            :                     /* The source is a database table. */
    1072                 :        452 :                     auto &tbl = ref->get();
    1073         [ +  + ]:       1614 :                     for (auto &attr : tbl) {
    1074         [ +  - ]:       1162 :                         auto d = create_designator(
    1075                 :       1162 :                             /* pos=        */ c.select_all.pos,
    1076                 :       1162 :                             /* table_name= */ src_name,
    1077         [ +  - ]:       1162 :                             /* attr_name=  */ attr.name,
    1078                 :       1162 :                             /* target=     */ &attr,
    1079                 :       1162 :                             /* type=       */ attr.type
    1080                 :            :                         );
    1081         [ +  - ]:       1162 :                         auto &ref = c.expanded_select_all.emplace_back(std::move(d));
    1082         [ +  - ]:       1162 :                         (*this)(*ref);
    1083   [ +  -  +  - ]:       1162 :                         Ctx.results.emplace(attr.name, SemaContext::result_t(*ref, result_counter++));
    1084                 :       1162 :                     }
    1085                 :        452 :                     has_vectorial = true;
    1086                 :        452 :                 } else {
    1087                 :            :                     /* The source is a nested query. */
    1088                 :          7 :                     auto &named_exprs = std::get<SemaContext::named_expr_table>(src.first);
    1089         [ +  - ]:          7 :                     std::vector<std::unique_ptr<Expr>> expanded_select_all(named_exprs.size());
    1090         [ +  + ]:         27 :                     for (auto &[name, expr_w_pos] : named_exprs) {
    1091                 :         50 :                         auto &[expr, pos] = expr_w_pos;
    1092         [ +  - ]:         10 :                         auto d = create_designator(
    1093                 :         10 :                             /* pos=        */ c.select_all.pos,
    1094         [ +  - ]:         10 :                             /* table_name= */ src_name,
    1095         [ +  - ]:         10 :                             /* attr_name=  */ name,
    1096                 :         10 :                             /* target=     */ &expr.get(),
    1097         [ +  - ]:         10 :                             /* type=       */ expr.get().type()
    1098                 :            :                         );
    1099                 :         10 :                         auto &ref = (expanded_select_all[pos] = std::move(d));
    1100         [ +  - ]:         10 :                         (*this)(*ref);
    1101   [ +  -  +  -  :         10 :                         if (auto pt = cast<const PrimitiveType>(ref->type())) {
                   +  + ]
    1102   [ +  -  +  - ]:          6 :                             has_scalar = has_scalar or pt->is_scalar();
    1103   [ +  +  +  - ]:          6 :                             has_vectorial = has_vectorial or pt->is_vectorial();
    1104                 :          6 :                         } else {
    1105   [ +  -  +  -  :          4 :                             M_insist(ref->type()->is_error(), "result of nested query must be of primitive type");
                   +  - ]
    1106                 :            :                         }
    1107   [ +  -  +  -  :         30 :                         Ctx.results.emplace(name, SemaContext::result_t(*ref, result_counter + pos));
             +  -  +  - ]
    1108                 :         10 :                     }
    1109                 :          7 :                     result_counter += named_exprs.size();
    1110   [ +  +  +  - ]:         17 :                     for (auto &e : expanded_select_all) c.expanded_select_all.emplace_back(std::move(e));
    1111                 :          7 :                 }
    1112                 :            :             }
    1113                 :            :         }
    1114                 :        265 :     }
    1115                 :            : 
    1116         [ +  + ]:        937 :     for (auto it = c.select.begin(), end = c.select.end(); it != end; ++it) {
    1117                 :        363 :         auto &select_expr = *it->first;
    1118                 :        363 :         auto alias = it->second;
    1119                 :            : 
    1120         [ +  - ]:        363 :         (*this)(select_expr); // recursively analyze select expression
    1121   [ +  -  +  +  :        363 :         if (select_expr.contains_free_variables() and not is<QueryExpr>(select_expr))
             +  -  +  - ]
    1122   [ +  -  +  -  :          6 :             diag.e(select_expr.tok.pos) << select_expr << " contains free variables (not yet supported).\n";
                   +  - ]
    1123                 :            : 
    1124   [ +  -  +  -  :        363 :         if (select_expr.type()->is_error()) continue;
                   +  + ]
    1125                 :            : 
    1126                 :            :         /* Expressions *must* be scalar when we have grouping. */
    1127   [ +  -  +  -  :        342 :         if (auto pt = cast<const PrimitiveType>(select_expr.type()); Ctx.needs_grouping and pt and pt->is_vectorial()) {
          +  +  +  -  +  
                -  +  + ]
    1128   [ +  -  +  -  :          2 :             diag.e(select_expr.tok.pos) << select_expr << " is not scalar.\n";
                   +  - ]
    1129                 :          2 :             continue;
    1130                 :            :         }
    1131                 :            : 
    1132                 :            :         /* Constants and scalar values of nested queries can be broadcast from scalar to vectorial.  We collect the
    1133                 :            :          * scalar/vector-ness information of each expression in the SELECT clause. */
    1134   [ +  -  +  +  :        340 :         if (not select_expr.is_constant() and not is<QueryExpr>(select_expr)) {
             +  -  +  + ]
    1135   [ +  -  +  - ]:        286 :             auto pt = as<const PrimitiveType>(select_expr.type());
    1136   [ +  +  +  - ]:        286 :             has_vectorial = has_vectorial or pt->is_vectorial();
    1137   [ +  +  +  - ]:        286 :             has_scalar = has_scalar or pt->is_scalar();
    1138                 :        286 :         }
    1139                 :            : 
    1140   [ +  -  +  + ]:        340 :         if (alias) { // SELECT expression has alias?
    1141                 :            :             /* Expression with alias. */
    1142   [ +  -  +  -  :         51 :             Ctx.results.emplace(alias.text, SemaContext::result_t(select_expr, result_counter++, alias.text));
                   +  - ]
    1143                 :         80 :             auto pred = [&](const std::pair<std::unique_ptr<Expr>, Token> &sel) {
    1144         [ +  - ]:         29 :                 return sel.second.text == alias.text;
    1145                 :          0 :             };
    1146   [ +  -  +  + ]:         51 :             if (auto num = std::count_if(c.select.begin(), it, pred)) {
    1147                 :            :                 /* Found ambiguous alias which is only allowed without accessing it. This is checked via the `Ctx`
    1148                 :            :                  * in which the ambiguous alias is contained. However, make alias unique for later accessing steps. */
    1149   [ -  +  +  - ]:          8 :                 oss.str("");
    1150   [ +  -  +  -  :          8 :                 oss << alias.text << "$" << num;
                   +  - ]
    1151   [ +  -  -  +  :          8 :                 alias.text = C.pool(oss.str().c_str());
             -  +  -  + ]
    1152                 :          8 :             }
    1153   [ +  -  +  + ]:        340 :         } else if (auto d = cast<Designator>(&select_expr)) {
    1154                 :            :             /* Expression is a designator.  Simply reuse the name without table prefix. */
    1155   [ +  -  -  + ]:         76 :             Ctx.results.emplace(d->attr_name.text, SemaContext::result_t(*d, result_counter++));
    1156                 :         76 :         } else {
    1157   [ +  -  +  - ]:        213 :             M_insist(not is<Designator>(select_expr));
    1158                 :            :             /* Expression without alias.  Print expression as string to get a name.  Use '$const' as prefix for
    1159                 :            :              * constants. */
    1160   [ +  -  +  - ]:        213 :             oss.str("");
    1161   [ +  -  +  + ]:        213 :             if (select_expr.is_constant())
    1162   [ +  -  +  - ]:         46 :                 oss << "$const" << const_counter++;
    1163                 :            :             else
    1164         [ +  - ]:        167 :                 oss << select_expr;
    1165   [ +  -  +  -  :        213 :             Ctx.results.emplace(C.pool(oss.str().c_str()), SemaContext::result_t(select_expr, result_counter++));
             +  -  +  - ]
    1166                 :            :         }
    1167      [ -  +  + ]:        363 :     }
    1168                 :            : 
    1169   [ +  +  +  - ]:        574 :     if (has_vectorial and has_scalar)
    1170                 :          0 :         diag.e(c.tok.pos) << "SELECT clause with mixed scalar and vectorial values is forbidden.\n";
    1171                 :        574 : }
    1172                 :            : 
    1173                 :        546 : void Sema::operator()(FromClause &c)
    1174                 :            : {
    1175                 :        546 :     SemaContext &Ctx = get_context();
    1176                 :        546 :     Ctx.stage = SemaContext::S_From;
    1177                 :            : 
    1178                 :        546 :     Catalog &C = Catalog::Get();
    1179                 :        546 :     const auto &DB = C.get_database_in_use();
    1180                 :            : 
    1181                 :        546 :     M_insist(Ctx.sources.empty());
    1182                 :        546 :     unsigned source_counter = 0;
    1183                 :            : 
    1184                 :            :     /* Check whether the source tables in the FROM clause exist in the database.  Add the source tables to the current
    1185                 :            :      * context, using their alias if provided (e.g. FROM src AS alias). */
    1186         [ +  + ]:       1342 :     for (auto &src: c.from) {
    1187         [ +  + ]:       2372 :         if (auto name = std::get_if<Token>(&src.source)) {
    1188                 :            :             try {
    1189   [ +  +  +  + ]:       2314 :                 const Table &T = DB.get_table(name->text.assert_not_none());
    1190   [ +  -  +  +  :        777 :                 Token table_name = src.alias ? src.alias : *name; // FROM name AS alias ?
                   +  + ]
    1191   [ +  +  +  - ]:       2311 :                 auto res = Ctx.sources.emplace(table_name.text, std::make_pair(std::ref(T), source_counter++));
    1192                 :            :                 /* Check if the table name is already in use in other contexts. */
    1193                 :        777 :                 bool unique = true;
    1194                 :        836 :                 for (std::size_t i = 0; i < contexts_.size() - 1; ++i) {
    1195         [ +  + ]:        836 :                     if (contexts_[i]->stage == SemaContext::S_From) continue;
    1196   [ +  +  -  +  :        805 :                     if (contexts_[i]->sources.contains(table_name.text.assert_not_none())) {
                   +  + ]
    1197                 :          8 :                         unique = false;
    1198                 :          8 :                         break;
    1199                 :            :                     }
    1200                 :         28 :                 }
    1201   [ +  +  +  + ]:          8 :                 if (not res.second or not unique)
    1202   [ +  +  +  -  :        775 :                     diag.e(table_name.pos) << "Table name " << table_name.text << " already in use.\n";
             +  -  +  - ]
    1203                 :        777 :                 src.table_ = &T;
    1204         [ -  + ]:        780 :             } catch (std::out_of_range) {
    1205   [ +  -  +  -  :          3 :                 diag.e(name->pos) << "No table " << name->text << " in database " << DB.name << ".\n";
          +  -  +  -  +  
                -  +  - ]
    1206                 :            :                 return;
    1207         [ #  # ]:          3 :             }
    1208         [ -  + ]:        835 :         } else if (auto stmt = std::get_if<Stmt*>(&src.source)) {
    1209                 :         58 :             M_insist(is<SelectStmt>(*stmt), "nested statements are always select statements");
    1210                 :            : 
    1211                 :            :             /* Evaluate the nested statement in a fresh sema context. */
    1212         [ -  + ]:         58 :             push_context(**stmt, src.alias.text);
    1213                 :         58 :             (*this)(**stmt);
    1214                 :         58 :             M_insist(not contexts_.empty());
    1215                 :         58 :             SemaContext inner_ctx = pop_context();
    1216                 :            : 
    1217                 :         58 :             SemaContext::named_expr_table results;
    1218         [ +  + ]:         90 :             for (auto &[name, res] : inner_ctx.results)
    1219   [ +  +  +  -  :         70 :                 results.emplace(name, std::make_pair(std::ref(res.expr()), res.order));
             +  -  +  - ]
    1220                 :            : 
    1221                 :            :             /* Add the results of the nested statement to the list of sources. */
    1222   [ +  -  +  - ]:         20 :             auto res = Ctx.sources.emplace(src.alias.text, std::make_pair(std::move(results), source_counter++));
    1223                 :            :             /* Convert scalar results to vectorials. */
    1224         [ +  + ]:         52 :             for (auto &[_, result] : inner_ctx.results)
    1225   [ +  -  +  -  :         32 :                 result.expr().type_ = as<const PrimitiveType>(result.expr().type())->as_vectorial();
          +  -  +  -  +  
                      - ]
    1226                 :            :             /* Check if the table name is already in use in other contexts. */
    1227                 :         20 :             bool unique = true;
    1228                 :         20 :             for (std::size_t i = 0; i < contexts_.size() - 1; ++i) {
    1229         [ +  - ]:         20 :                 if (contexts_[i]->stage == SemaContext::S_From) continue;
    1230   [ -  +  #  #  :         20 :                 if (contexts_[i]->sources.contains(src.alias.text.assert_not_none())) {
                   #  # ]
    1231                 :          0 :                     unique = false;
    1232                 :          0 :                     break;
    1233                 :            :                 }
    1234                 :          0 :             }
    1235   [ +  +  -  + ]:          0 :             if (not res.second or not unique) {
    1236   [ +  +  +  -  :         19 :                 diag.e(src.alias.pos) << "Table name " << src.alias.text << " already in use.\n";
             +  -  +  - ]
    1237                 :          1 :                 return;
    1238                 :            :             }
    1239      [ -  +  + ]:         20 :         } else {
    1240                 :          0 :             M_unreachable("invalid variant");
    1241                 :            :         }
    1242                 :            :     }
    1243                 :       7249 : }
    1244                 :            : 
    1245                 :        197 : void Sema::operator()(WhereClause &c)
    1246                 :            : {
    1247                 :        197 :     SemaContext &Ctx = get_context();
    1248                 :        197 :     Ctx.stage = SemaContext::S_Where;
    1249                 :            : 
    1250                 :            :     /* Analyze expression. */
    1251                 :        197 :     (*this)(*c.where);
    1252                 :            : 
    1253         [ +  + ]:        197 :     if (c.where->type()->is_error())
    1254                 :         16 :         return; /* nothing to be done */
    1255                 :            : 
    1256                 :        181 :     const Boolean *ty = cast<const Boolean>(c.where->type());
    1257                 :            : 
    1258                 :            :     /* WHERE condition must be of boolean type. */
    1259         [ +  + ]:        181 :     if (not ty) {
    1260                 :          1 :         diag.e(c.tok.pos) << "The expression in the WHERE clause must be of boolean type.\n";
    1261                 :          1 :         return;
    1262                 :            :     }
    1263                 :        197 : }
    1264                 :            : 
    1265                 :         58 : void Sema::operator()(GroupByClause &c)
    1266                 :            : {
    1267                 :         58 :     Catalog &C = Catalog::Get();
    1268                 :         58 :     SemaContext &Ctx = get_context();
    1269                 :         58 :     Ctx.stage = SemaContext::S_GroupBy;
    1270                 :            : 
    1271                 :         58 :     Ctx.needs_grouping = true;
    1272         [ +  + ]:        162 :     for (auto &[expr, alias] : c.group_by) {
    1273                 :         58 :         (*this)(*expr);
    1274                 :            : 
    1275                 :            :         /* Skip errors. */
    1276         [ +  + ]:         58 :         if (expr->type()->is_error())
    1277                 :          2 :             continue;
    1278                 :            : 
    1279         [ +  + ]:         56 :         if (expr->contains_free_variables())
    1280                 :          9 :             diag.e(expr->tok.pos) << *expr << " contains free variable(s) (not yet supported).\n";
    1281                 :            : 
    1282                 :         56 :         const PrimitiveType *pt = cast<const PrimitiveType>(expr->type());
    1283                 :            : 
    1284                 :            :         /* Can only group by expressions of primitive type. */
    1285         [ +  - ]:         56 :         if (not pt) {
    1286                 :          0 :             diag.e(c.tok.pos) << "Cannot group by " << *expr << ", has invalid type.\n";
    1287                 :          0 :             continue;
    1288                 :            :         }
    1289                 :            : 
    1290                 :            :         /* Can only group by vectorials.  The expression in the GROUP BY clause must be evaluated per tuple. */
    1291         [ +  + ]:         56 :         if (not pt->is_vectorial()) {
    1292                 :          6 :             diag.e(c.tok.pos) << "Cannot group by " << *expr << ".  Expressions in the GROUP BY clause must be "
    1293                 :            :                                  "vectorial, i.e. they must depend on each row separately.\n";
    1294                 :          3 :             continue;
    1295                 :            :         }
    1296                 :            : 
    1297                 :            :         /* Add expression to list of grouping keys. */
    1298         [ +  + ]:         53 :         if (alias) {
    1299                 :         24 :             Ctx.grouping_keys.emplace(alias.text, *expr);
    1300         [ +  + ]:         53 :         } else if (auto d = cast<Designator>(expr.get())) {
    1301                 :         88 :             Ctx.grouping_keys.emplace(d->attr_name.text, *expr);
    1302                 :         44 :         } else {
    1303   [ -  +  -  + ]:          1 :             oss.str("");
    1304                 :          2 :             oss << *expr;
    1305   [ -  +  -  + ]:          1 :             Ctx.grouping_keys.emplace(C.pool(oss.str().c_str()), *expr);
    1306                 :            :         }
    1307                 :            :     }
    1308                 :         58 : }
    1309                 :            : 
    1310                 :         40 : void Sema::operator()(HavingClause &c)
    1311                 :            : {
    1312                 :         40 :     SemaContext &Ctx = get_context();
    1313                 :         40 :     Ctx.stage = SemaContext::S_Having;
    1314                 :         40 :     Ctx.needs_grouping = true;
    1315                 :            : 
    1316                 :         40 :     (*this)(*c.having);
    1317                 :            : 
    1318                 :            :     /* Skip errors. */
    1319         [ +  + ]:         40 :     if (c.having->type()->is_error())
    1320                 :         12 :         return;
    1321                 :            : 
    1322                 :         28 :     const Boolean *ty = cast<const Boolean>(c.having->type());
    1323                 :            : 
    1324                 :            :     /* HAVING condition must be of boolean type. */
    1325         [ +  + ]:         28 :     if (not ty) {
    1326                 :          1 :         diag.e(c.tok.pos) << "The expression in the HAVING clause must be of boolean type.\n";
    1327                 :          1 :         return;
    1328                 :            :     }
    1329                 :            : 
    1330         [ +  + ]:         27 :     if (not ty->is_scalar()) {
    1331                 :          1 :         diag.e(c.tok.pos) << "The expression in the HAVING clause must be scalar.\n";
    1332                 :          1 :         return;
    1333                 :            :     }
    1334                 :            : 
    1335                 :            :     /* TODO The HAVING clause must be a conjunction or disjunction of aggregates or comparisons of grouping keys. */
    1336                 :         40 : }
    1337                 :            : 
    1338                 :         33 : void Sema::operator()(OrderByClause &c)
    1339                 :            : {
    1340                 :         33 :     SemaContext &Ctx = get_context();
    1341                 :         33 :     Ctx.stage = SemaContext::S_OrderBy;
    1342                 :            : 
    1343                 :            :     /* Analyze all ordering expressions. */
    1344         [ +  + ]:         76 :     for (auto &o : c.order_by) {
    1345                 :         43 :         auto &e = o.first;
    1346                 :         43 :         (*this)(*e);
    1347                 :            : 
    1348         [ +  + ]:         43 :         if (e->type()->is_error()) continue;
    1349         [ +  + ]:         33 :         if (e->contains_free_variables())
    1350                 :          3 :             diag.e(e->tok.pos) << *e << " contains free variable(s) (not yet supported).\n";
    1351                 :            : 
    1352                 :         33 :         auto pt = as<const PrimitiveType>(e->type());
    1353                 :            : 
    1354         [ +  + ]:         33 :         if (Ctx.needs_grouping) { // w/ grouping
    1355                 :            :             /* If we grouped, the grouping keys now have scalar type. */
    1356         [ +  + ]:          7 :             if (pt->is_vectorial())
    1357                 :          2 :                 diag.e(c.tok.pos) << "Cannot order by " << *e << ", expression must be scalar.\n";
    1358                 :          7 :         } else { // w/o grouping
    1359                 :            :             /* If we did not group, the ordering expressions must be vectorial. */
    1360         [ +  + ]:         26 :             if (pt->is_scalar())
    1361                 :          1 :                 diag.e(c.tok.pos) << "Cannot order by " << *e << ", expression must be vectorial.\n";
    1362                 :            :         }
    1363                 :            :     }
    1364                 :         33 : }
    1365                 :            : 
    1366                 :          4 : void Sema::operator()(LimitClause &c)
    1367                 :            : {
    1368                 :          4 :     SemaContext &Ctx = get_context();
    1369                 :          4 :     Ctx.stage = SemaContext::S_Limit;
    1370                 :            : 
    1371                 :            :     /* TODO limit only makes sense when SELECT is vectorial and not scalar */
    1372                 :            : 
    1373                 :          4 :     errno = 0;
    1374                 :          4 :     strtoull(*c.limit.text, nullptr, 0);
    1375         [ -  + ]:          4 :     if (errno == EINVAL)
    1376                 :          0 :         diag.e(c.limit.pos) << "Invalid value for LIMIT.\n";
    1377         [ -  + ]:          4 :     else if (errno == ERANGE)
    1378                 :          0 :         diag.e(c.limit.pos) << "Value of LIMIT out of range.\n";
    1379         [ -  + ]:          4 :     else if (errno != 0)
    1380                 :          0 :         diag.e(c.limit.pos) << "Invalid LIMIT.\n";
    1381                 :            : 
    1382         [ +  + ]:          4 :     if (c.offset) {
    1383                 :          2 :         errno = 0;
    1384                 :          2 :         strtoull(*c.offset.text, nullptr, 0);
    1385         [ -  + ]:          2 :         if (errno == EINVAL)
    1386                 :          0 :             diag.e(c.offset.pos) << "Invalid value for OFFSET.\n";
    1387         [ -  + ]:          2 :         else if (errno == ERANGE)
    1388                 :          0 :             diag.e(c.offset.pos) << "Value of OFFSET out of range.\n";
    1389         [ -  + ]:          2 :         else if (errno != 0)
    1390                 :          0 :             diag.e(c.offset.pos) << "Invalid OFFSET.\n";
    1391                 :          2 :     }
    1392                 :          4 : }
    1393                 :            : 
    1394                 :            : 
    1395                 :            : /*===== Instruction ==================================================================================================*/
    1396                 :            : 
    1397                 :          0 : void Sema::operator()(Instruction &I) {
    1398                 :          0 :     Catalog &C = Catalog::Get();
    1399                 :            :     try {
    1400         [ #  # ]:          0 :         command_ = C.create_instruction(I.name, I.args);
    1401         [ #  # ]:          0 :     } catch (std::invalid_argument) {
    1402   [ #  #  #  #  :          0 :         diag.e(I.tok.pos) << "Instruction " << I.name << " unknown\n";
             #  #  #  # ]
    1403         [ #  # ]:          0 :     }
    1404                 :          0 : }
    1405                 :            : 
    1406                 :            : 
    1407                 :            : /*===== Stmt =========================================================================================================*/
    1408                 :            : 
    1409                 :          0 : void Sema::operator()(ErrorStmt&)
    1410                 :            : {
    1411                 :            :     /* nothing to be done */
    1412                 :          0 : }
    1413                 :            : 
    1414                 :          0 : void Sema::operator()(EmptyStmt&)
    1415                 :            : {
    1416                 :          0 :     command_ = std::make_unique<EmptyCommand>();
    1417                 :          0 : }
    1418                 :            : 
    1419                 :          2 : void Sema::operator()(CreateDatabaseStmt &s)
    1420                 :            : {
    1421                 :          2 :     RequireContext RCtx(this, s);
    1422         [ +  - ]:          2 :     Catalog &C = Catalog::Get();
    1423         [ +  - ]:          2 :     auto db_name = s.database_name.text.assert_not_none();
    1424                 :            : 
    1425   [ +  -  +  + ]:          2 :     if (not C.has_database(db_name))
    1426         [ +  - ]:          1 :         command_ = std::make_unique<CreateDatabase>(std::move(db_name));
    1427                 :            :     else
    1428   [ +  -  +  -  :          1 :         diag.e(s.database_name.pos) << "Database " << db_name << " already exists.\n";
             +  -  +  - ]
    1429                 :          2 : }
    1430                 :            : 
    1431                 :          3 : void Sema::operator()(DropDatabaseStmt &s)
    1432                 :            : {
    1433                 :          3 :     RequireContext RCtx(this, s);
    1434         [ +  - ]:          3 :     Catalog &C = Catalog::Get();
    1435         [ +  - ]:          3 :     auto db_name = s.database_name.text.assert_not_none();
    1436                 :            : 
    1437   [ +  -  -  + ]:          3 :     if (C.has_database_in_use()) {
    1438   [ #  #  #  #  :          0 :         if (C.get_database_in_use().name == db_name) {
             #  #  #  # ]
    1439   [ #  #  #  #  :          0 :             diag.e(s.database_name.pos) << "Database " << db_name << " is in use.\n";
             #  #  #  # ]
    1440                 :          0 :             return;
    1441                 :            :         }
    1442                 :          0 :     }
    1443                 :            : 
    1444   [ +  -  +  + ]:          3 :     if (C.has_database(db_name))
    1445         [ +  - ]:          1 :         command_ = std::make_unique<DropDatabase>(std::move(db_name));
    1446                 :            :     else {
    1447         [ +  + ]:          2 :         if (s.has_if_exists)
    1448         [ +  - ]:          1 :             command_ = std::make_unique<EmptyCommand>();
    1449                 :            :         else
    1450   [ +  -  +  -  :          1 :             diag.e(s.database_name.pos) << "Database " << db_name << " does not exist.\n";
             +  -  +  - ]
    1451                 :            :     }
    1452         [ -  + ]:          3 : }
    1453                 :            : 
    1454                 :          3 : void Sema::operator()(UseDatabaseStmt &s)
    1455                 :            : {
    1456                 :          3 :     RequireContext RCtx(this, s);
    1457         [ +  - ]:          3 :     Catalog &C = Catalog::Get();
    1458         [ +  - ]:          3 :     auto db_name = s.database_name.text.assert_not_none();
    1459                 :            : 
    1460   [ +  -  +  + ]:          3 :     if (C.has_database(db_name))
    1461         [ +  - ]:          1 :         command_ = std::make_unique<UseDatabase>(std::move(db_name));
    1462                 :            :     else
    1463   [ +  -  +  -  :          2 :         diag.e(s.database_name.pos) << "Database " << db_name << " does not exist.\n";
             +  -  +  - ]
    1464                 :          3 : }
    1465                 :            : 
    1466                 :         26 : void Sema::operator()(CreateTableStmt &s)
    1467                 :            : {
    1468                 :         26 :     RequireContext RCtx(this, s);
    1469         [ +  - ]:         26 :     Catalog &C = Catalog::Get();
    1470                 :            : 
    1471   [ +  -  +  + ]:         26 :     if (not C.has_database_in_use()) {
    1472   [ +  -  +  - ]:          1 :         diag.err() << "No database selected.\n";
    1473                 :          1 :         return;
    1474                 :            :     }
    1475         [ +  - ]:         25 :     auto &DB = C.get_database_in_use();
    1476         [ +  - ]:         25 :     auto table_name = s.table_name.text.assert_not_none();
    1477   [ +  -  +  -  :         25 :     std::unique_ptr<Table> T = C.table_factory().make(table_name);
                   +  - ]
    1478                 :            : 
    1479                 :            :     /* Add the newly declared table to the list of sources of the sema context.  We need to add the table to the sema
    1480                 :            :      * context so that semantic analysis of `CHECK` expressions can resolve references to attributes of the same table.
    1481                 :            :      * */
    1482   [ +  -  +  -  :         25 :     get_context().sources.emplace(table_name, std::make_pair(SemaContext::source_type(*T), 0U));
                   +  - ]
    1483                 :            : 
    1484                 :            :     /* Verify table does not yet exist. */
    1485                 :            :     try {
    1486         [ +  + ]:         25 :         DB.get_table(table_name);
    1487   [ +  -  +  -  :          1 :         diag.e(s.table_name.pos) << "Table " << table_name << " already exists in database " << DB.name << ".\n";
          +  -  +  -  +  
                -  +  - ]
    1488         [ +  - ]:         25 :     } catch (std::out_of_range) {
    1489                 :            :         /* nothing to be done */
    1490         [ +  - ]:         24 :     }
    1491                 :            : 
    1492                 :            :     /* Analyze attributes and add them to the new table. */
    1493                 :         25 :     bool has_primary_key = false;
    1494         [ +  + ]:         93 :     for (auto &attr : s.attributes) {
    1495         [ +  - ]:         68 :         auto attribute_name = attr->name.text.assert_not_none();
    1496         [ +  - ]:         68 :         const PrimitiveType *ty = cast<const PrimitiveType>(attr->type);
    1497         [ -  + ]:         68 :         if (not ty) {
    1498   [ #  #  #  #  :          0 :             diag.e(attr->name.pos) << "Attribute " << attr->name.text << " cannot be defined with type " << *attr->type
          #  #  #  #  #  
                      # ]
    1499         [ #  # ]:          0 :                                    << ".\n";
    1500                 :          0 :             return;
    1501                 :            :         }
    1502         [ +  - ]:         68 :         attr->type = ty->as_vectorial(); // convert potentially scalar type to vectorial
    1503                 :            : 
    1504                 :            :         /* Before we check the constraints, we must add this newly declared attribute to its table, and hence to the
    1505                 :            :          * sema context. */
    1506                 :            :         try {
    1507   [ +  -  +  -  :         68 :             T->push_back(attribute_name, ty->as_vectorial());
                   +  + ]
    1508         [ +  - ]:         68 :         } catch (std::invalid_argument) {
    1509                 :            :             /* attribute name is a duplicate */
    1510   [ +  -  +  -  :          1 :             diag.e(attr->name.pos) << "Attribute " << attr->name.text << " occurs multiple times in defintion of table "
             +  -  +  - ]
    1511   [ +  -  +  - ]:          1 :                                    << table_name << ".\n";
    1512   [ +  -  #  # ]:          1 :         }
    1513                 :            : 
    1514                 :            :         /* Check constraint definitions. */
    1515                 :         68 :         bool has_reference = false; ///< at most one reference allowed per attribute
    1516                 :         68 :         bool is_unique = false, is_not_null = false;
    1517         [ +  - ]:         68 :         get_context().stage = SemaContext::S_Where;
    1518         [ +  + ]:        100 :         for (auto &c : attr->constraints) {
    1519   [ +  -  +  + ]:         32 :             if (is<PrimaryKeyConstraint>(c)) {
    1520         [ +  + ]:         16 :                 if (has_primary_key)
    1521   [ +  -  +  -  :          2 :                     diag.e(attr->name.pos) << "Duplicate definition of primary key as attribute " << attr->name.text
                   +  - ]
    1522         [ +  - ]:          1 :                                           << ".\n";
    1523                 :         16 :                 has_primary_key = true;
    1524         [ +  - ]:         16 :                 T->add_primary_key(attribute_name);
    1525                 :         16 :             }
    1526                 :            : 
    1527   [ +  -  +  + ]:         32 :             if (is<UniqueConstraint>(c)) {
    1528         [ +  + ]:          4 :                 if (is_unique)
    1529   [ +  -  +  -  :          1 :                     diag.w(c->tok.pos) << "Duplicate definition of attribute " << attr->name.text << " as UNIQUE.\n";
             +  -  +  - ]
    1530                 :          4 :                 is_unique = true;
    1531         [ +  - ]:          4 :                 T->at(attribute_name).unique = true;
    1532                 :          4 :             }
    1533                 :            : 
    1534   [ +  -  +  + ]:         32 :             if (is<NotNullConstraint>(c)) {
    1535         [ +  + ]:          4 :                 if (is_not_null)
    1536   [ +  -  +  -  :          1 :                     diag.w(c->tok.pos) << "Duplicate definition of attribute " << attr->name.text << " as NOT NULL.\n";
             +  -  +  - ]
    1537                 :          4 :                 is_not_null = true;
    1538         [ +  - ]:          4 :                 T->at(attribute_name).not_nullable = true;
    1539                 :          4 :             }
    1540                 :            : 
    1541   [ +  -  +  + ]:         35 :             if (auto check = cast<CheckConditionConstraint>(c)) {
    1542                 :            :                 /* Verify that the type of the condition is boolean. */
    1543                 :            :                 /* TODO if the condition uses already mentioned attributes, we must add them to the sema context before
    1544                 :            :                  * invoking semantic analysis of the condition! */
    1545         [ +  - ]:          3 :                 (*this)(*check->cond);
    1546         [ +  - ]:          3 :                 auto ty = check->cond->type();
    1547   [ +  -  +  + ]:          3 :                 if (not ty->is_boolean())
    1548   [ +  -  +  -  :          1 :                     diag.e(check->tok.pos) << "Condition " << *check->cond << " is an invalid CHECK constraint.\n";
             +  -  +  - ]
    1549                 :          3 :             }
    1550                 :            : 
    1551   [ +  -  +  + ]:         37 :             if (auto ref = cast<ReferenceConstraint>(c)) {
    1552         [ +  + ]:          5 :                 if (has_reference)
    1553   [ +  -  +  -  :          1 :                     diag.e(ref->tok.pos) << "Attribute " << attr->name.text << " must not have multiple references.\n";
             +  -  +  - ]
    1554                 :          5 :                 has_reference = true;
    1555                 :            : 
    1556                 :            :                 /* Check that the referenced attribute exists. */
    1557                 :            :                 try {
    1558   [ +  -  +  + ]:          5 :                     auto &ref_table = DB.get_table(ref->table_name.text.assert_not_none());
    1559                 :            :                     try {
    1560   [ +  -  +  + ]:          4 :                         auto &ref_attr = ref_table.at(ref->attr_name.text.assert_not_none());
    1561         [ +  + ]:          3 :                         if (attr->type != ref_attr.type)
    1562   [ +  -  +  - ]:          1 :                             diag.e(ref->attr_name.pos) << "Referenced attribute has different type.\n";
    1563   [ +  -  +  - ]:          3 :                         T->at(attr->name.text.assert_not_none()).reference = &ref_attr;
    1564         [ +  - ]:          4 :                     } catch (std::out_of_range) {
    1565   [ +  -  +  -  :          1 :                         diag.e(ref->attr_name.pos) << "Invalid reference, attribute " << ref->attr_name.text
                   +  - ]
    1566   [ +  -  +  -  :          1 :                                                   << " not found in table " << ref->table_name.text << ".\n";
                   +  - ]
    1567   [ +  -  #  # ]:          1 :                     }
    1568         [ -  + ]:          5 :                 } catch (std::out_of_range) {
    1569   [ +  -  +  -  :          1 :                     diag.e(ref->table_name.pos) << "Invalid reference, table " << ref->table_name.text
                   +  - ]
    1570         [ +  - ]:          1 :                                                 << " not found.\n";
    1571   [ +  -  #  # ]:          1 :                 }
    1572                 :          5 :             }
    1573                 :            :         }
    1574         [ -  + ]:         68 :     }
    1575                 :            : 
    1576   [ +  -  +  -  :         25 :     if (not is_nested() and not diag.num_errors())
                   +  + ]
    1577         [ +  - ]:         17 :         command_ = std::make_unique<CreateTable>(std::move(T));
    1578         [ -  + ]:         53 : }
    1579                 :            : 
    1580                 :          4 : void Sema::operator()(DropTableStmt &s)
    1581                 :            : {
    1582                 :          4 :     RequireContext RCtx(this, s);
    1583         [ +  - ]:          4 :     Catalog &C = Catalog::Get();
    1584                 :            : 
    1585   [ +  -  -  + ]:          4 :     if (not C.has_database_in_use()) {
    1586   [ #  #  #  # ]:          0 :         diag.err() << "No database selected.\n";
    1587                 :          0 :         return;
    1588                 :            :     }
    1589         [ +  - ]:          4 :     auto &DB = C.get_database_in_use();
    1590                 :            : 
    1591                 :          4 :     bool ok = true;
    1592                 :          4 :     std::vector<ThreadSafePooledString> table_names;
    1593         [ +  + ]:          9 :     for (auto &tok : s.table_names) {
    1594         [ +  - ]:          5 :         auto table_name = tok->text.assert_not_none();
    1595   [ +  -  +  + ]:          5 :         if (DB.has_table(table_name))
    1596         [ +  - ]:          2 :             table_names.emplace_back(std::move(table_name));
    1597                 :            :         else {
    1598         [ +  + ]:          3 :             if (not s.has_if_exists) {
    1599   [ +  -  +  -  :          1 :                 diag.e(tok->pos) << "Table " << table_name << " does not exist in database " << DB.name << ".\n";
          +  -  +  -  +  
                -  +  - ]
    1600                 :          1 :                 ok = false;
    1601                 :          1 :             } else {
    1602   [ +  -  +  -  :          2 :                 diag.n(tok->pos) << "Table " << table_name << " does not exist in database " << DB.name << ". "
          +  -  +  -  +  
                -  +  - ]
    1603         [ +  - ]:          2 :                                  << "Skipping.\n";
    1604                 :            :             }
    1605                 :            :         }
    1606                 :          5 :     }
    1607         [ +  + ]:          4 :     if (ok)
    1608         [ +  - ]:          3 :         command_ = std::make_unique<DropTable>(std::move(table_names));
    1609         [ -  + ]:          4 : }
    1610                 :            : 
    1611                 :          7 : void Sema::operator()(CreateIndexStmt &s)
    1612                 :            : {
    1613                 :          7 :     RequireContext RCtx(this, s);
    1614         [ +  - ]:          7 :     Catalog &C = Catalog::Get();
    1615                 :            : 
    1616   [ +  -  -  + ]:          7 :     if (not C.has_database_in_use()) {
    1617   [ #  #  #  # ]:          0 :         diag.err() << "No database selected.\n";
    1618                 :          0 :         return;
    1619                 :            :     }
    1620         [ +  - ]:          7 :     auto &DB = C.get_database_in_use();
    1621                 :            : 
    1622                 :            :     /* Check if `UNIQUE` was present in statement. */
    1623   [ +  -  -  + ]:          7 :     if (s.has_unique) {
    1624   [ #  #  #  # ]:          0 :         diag.e(s.has_unique.pos) << "Keyword UNIQUE not supported.\n";
    1625                 :          0 :         return;
    1626                 :            :     }
    1627                 :            : 
    1628                 :            :     /* Check that an index name is set. */
    1629   [ +  -  +  + ]:          7 :     if (not s.index_name) {
    1630   [ +  -  +  - ]:          4 :         diag.err() << "Indexes without name not supported.\n";
    1631                 :          4 :         return;
    1632                 :            :     }
    1633                 :            : 
    1634                 :            :     /* Check that the index name does not yet exist. */
    1635         [ +  - ]:          3 :     auto index_name = s.index_name.text.assert_not_none();
    1636   [ +  -  +  + ]:          3 :     if (DB.has_index(index_name)) {
    1637         [ -  + ]:          1 :         if (s.has_if_not_exists) {
    1638   [ #  #  #  #  :          0 :             diag.w(s.index_name.pos) << "Index " << index_name << " already exists in database " << DB.name
          #  #  #  #  #  
                      # ]
    1639         [ #  # ]:          0 :                                      << ". Skipping.\n";
    1640         [ #  # ]:          0 :             command_ = std::make_unique<EmptyCommand>();
    1641                 :          0 :             return;
    1642                 :            :         } else {
    1643   [ +  -  +  -  :          1 :             diag.e(s.index_name.pos) << "Index " << index_name << " already exists in database " << DB.name << ".\n";
          +  -  +  -  +  
                -  +  - ]
    1644                 :          1 :             return;
    1645                 :            :         }
    1646                 :            :     }
    1647                 :            : 
    1648                 :            :     /* Check that the table exists. */
    1649         [ +  - ]:          2 :     auto table_name = s.table_name.text.assert_not_none();
    1650   [ +  -  -  + ]:          2 :     if (not DB.has_table(table_name)) {
    1651   [ #  #  #  #  :          0 :         diag.e(s.table_name.pos) << "Table " << table_name << " does not exist in database " << DB.name << "\n.";
          #  #  #  #  #  
                #  #  # ]
    1652                 :          0 :         return;
    1653                 :            :     }
    1654         [ +  - ]:          2 :     auto &table = DB.get_table(table_name);
    1655                 :            : 
    1656                 :            :     /* Check that the index method exists. */
    1657   [ +  -  +  - ]:          2 :     if (not s.method) { // if method is not set, set to default
    1658   [ +  -  -  + ]:          2 :         s.method = Token::CreateArtificial(TK_Default);
    1659                 :          2 :         s.method.pos = s.table_name.pos;
    1660                 :          2 :     }
    1661      [ -  -  + ]:          2 :     switch(s.method.type) {
    1662                 :            :         case TK_Default: // ok
    1663                 :          2 :             break;
    1664                 :            : 
    1665                 :            :         case TK_IDENTIFIER:
    1666   [ #  #  #  #  :          0 :             if (s.method.text.assert_not_none() == C.pool("array")) // ok
             #  #  #  # ]
    1667                 :          0 :                 break;
    1668   [ #  #  #  #  :          0 :             else if (s.method.text == C.pool("rmi")) // ok
                   #  # ]
    1669                 :          0 :                 break;
    1670                 :            :             else { // unknown method, not ok
    1671   [ #  #  #  #  :          0 :                 diag.e(s.method.pos) << "Index method " << s.method.text << " not supported.\n";
             #  #  #  # ]
    1672                 :          0 :                 return;
    1673                 :            :             }
    1674                 :            : 
    1675                 :            :         default: // unknown token type, not ok
    1676   [ #  #  #  #  :          0 :             diag.e(s.method.pos) << "Index method " << s.method.text << " not supported.\n";
             #  #  #  # ]
    1677                 :          0 :             return;
    1678                 :            :     }
    1679                 :            : 
    1680                 :            :     /* Check that at most one key field is set. */
    1681         [ -  + ]:          2 :     if (s.key_fields.size() > 1) {
    1682   [ #  #  #  # ]:          0 :         diag.err() << "More than one key field for indexes not supported.\n";
    1683                 :          0 :         return;
    1684                 :            :     }
    1685                 :            : 
    1686                 :            :     /* Compute attribute from key field. */
    1687         [ +  + ]:          4 :     for (auto it = s.key_fields.cbegin(), end = s.key_fields.cend(); it != end; ++it) {
    1688                 :          2 :         auto field = it->get();
    1689   [ +  -  +  - ]:          2 :         if (auto d = cast<Designator>(field)) {
    1690   [ +  -  -  +  :          2 :             if (not table.has_attribute(d->attr_name.text.assert_not_none())) {
                   -  + ]
    1691   [ #  #  #  #  :          0 :                 diag.e(d->tok.pos) << "Attribute " << d->attr_name.text << " does not exists in table "
             #  #  #  # ]
    1692   [ #  #  #  # ]:          0 :                                    << table_name << ".\n";
    1693                 :          0 :                 return;
    1694                 :            :             }
    1695                 :          2 :         } else {
    1696   [ #  #  #  # ]:          0 :             diag.e(field->tok.pos) << "Non-attribute key fields for indexes not supported.\n";
    1697                 :          0 :             return;
    1698                 :            :         }
    1699                 :          2 :     }
    1700   [ +  -  -  + ]:          2 :     auto attribute_name = cast<Designator>(s.key_fields.front())->attr_name.text.assert_not_none();
    1701         [ +  - ]:          2 :     auto &attribute = table.at(attribute_name);
    1702                 :            : 
    1703                 :            :     /* Build index based on selected method and key type. */
    1704                 :          2 :     std::unique_ptr<idx::IndexBase> index;
    1705                 :          4 :     auto make_index = [&]<template<typename> typename Index, typename Key>() {
    1706                 :            :         if constexpr(requires { typename Index<Key>; }) {
    1707                 :          2 :             return std::make_unique<Index<Key>>();
    1708                 :            :         } else {
    1709                 :          0 :             diag(s.method.pos) << "Index method not available for given key type.\n";
    1710                 :          0 :             return nullptr;
    1711                 :            :         }
    1712                 :            :     };
    1713                 :          4 :     auto set_index = [&]<template<typename> typename Index>() {
    1714                 :         10 :         visit(overloaded {
    1715                 :          4 :             [&](const Boolean&) { index = make_index.operator()<Index, bool>(); },
    1716                 :          2 :             [&](const Numeric &n) {
    1717   [ #  #  #  #  :          0 :                 switch (n.kind) {
                   #  # ]
    1718                 :            :                     case Numeric::N_Int:
    1719                 :            :                     case Numeric::N_Decimal:
    1720   [ #  #  #  #  :          0 :                         switch (n.size()) {
          #  #  #  #  #  
                      # ]
    1721                 :          0 :                             default: M_unreachable("invalid size");
    1722                 :          0 :                             case  8: index = make_index.operator()<Index, int8_t>(); break;
    1723                 :          0 :                             case 16: index = make_index.operator()<Index, int16_t>(); break;
    1724                 :          0 :                             case 32: index = make_index.operator()<Index, int32_t>(); break;
    1725                 :          0 :                             case 64: index = make_index.operator()<Index, int64_t>(); break;
    1726                 :            :                         }
    1727                 :          0 :                         break;
    1728                 :            :                     case Numeric::N_Float:
    1729   [ #  #  #  #  :          0 :                         switch (n.size()) {
                   #  # ]
    1730                 :          0 :                             default: M_unreachable("invalid size");
    1731                 :          0 :                             case 32: index = make_index.operator()<Index, float>(); break;
    1732                 :          0 :                             case 64: index = make_index.operator()<Index, double>(); break;
    1733                 :            :                     }
    1734                 :          0 :                 }
    1735                 :          0 :             },
    1736                 :          2 :             [&](const CharacterSequence&) { index = make_index.operator()<Index, const char*>(); },
    1737                 :          2 :             [&](const Date&) { index = std::make_unique<Index<int32_t>>(); },
    1738                 :          2 :             [&](const DateTime&) { index = std::make_unique<Index<int64_t>>(); },
    1739                 :          0 :             [](auto&&) { M_unreachable("invalid type"); },
    1740                 :          2 :         }, *attribute.type);
    1741                 :          2 :     };
    1742      [ -  -  + ]:          2 :     switch(s.method.type) {
    1743         [ +  - ]:          2 :         case TK_Default: set_index.operator()<idx::ArrayIndex>(); break;
    1744                 :            :         case TK_IDENTIFIER:
    1745   [ #  #  #  #  :          0 :             if (s.method.text.assert_not_none() == C.pool("array"))
             #  #  #  # ]
    1746         [ #  # ]:          0 :                 set_index.operator()<idx::ArrayIndex>();
    1747   [ #  #  #  #  :          0 :             else if (s.method.text == C.pool("rmi"))
                   #  # ]
    1748         [ #  # ]:          0 :                 set_index.operator()<idx::RecursiveModelIndex>();
    1749                 :          0 :             break;
    1750                 :            :         default:
    1751         [ #  # ]:          0 :             M_unreachable("invalid token type");
    1752                 :            :     }
    1753         [ -  + ]:          2 :     if (not index) // No index was set
    1754                 :          0 :         return;
    1755                 :            : 
    1756         [ +  - ]:          2 :     command_ = std::make_unique<CreateIndex>(std::move(index), std::move(table_name), std::move(attribute_name),
    1757                 :            :                                              std::move(index_name));
    1758         [ -  + ]:          7 : }
    1759                 :            : 
    1760                 :          4 : void Sema::operator()(DropIndexStmt &s)
    1761                 :            : {
    1762                 :          4 :     RequireContext RCtx(this, s);
    1763         [ +  - ]:          4 :     Catalog &C = Catalog::Get();
    1764                 :            : 
    1765   [ +  -  -  + ]:          4 :     if (not C.has_database_in_use()) {
    1766   [ #  #  #  # ]:          0 :         diag.err() << "No database selected.\n";
    1767                 :          0 :         return;
    1768                 :            :     }
    1769         [ +  - ]:          4 :     auto &DB = C.get_database_in_use();
    1770                 :            : 
    1771                 :          4 :     bool ok = true;
    1772                 :          4 :     std::vector<ThreadSafePooledString> index_names;
    1773         [ +  + ]:          9 :     for (auto &tok : s.index_names) {
    1774         [ +  - ]:          5 :         auto index_name = tok->text.assert_not_none();
    1775   [ +  -  +  + ]:          5 :         if (DB.has_index(index_name))
    1776         [ +  - ]:          2 :             index_names.emplace_back(index_name);
    1777                 :            :         else {
    1778         [ +  + ]:          3 :             if (not s.has_if_exists) {
    1779   [ +  -  +  -  :          1 :                 diag.e(tok->pos) << "Index " << index_name << " does not exist in database " << DB.name << ".\n";
          +  -  +  -  +  
                -  +  - ]
    1780                 :          1 :                 ok = false;
    1781                 :          1 :             } else {
    1782   [ +  -  +  -  :          2 :                 diag.w(tok->pos) << "Index " << index_name << " does not exist in database " << DB.name << ". "
          +  -  +  -  +  
                -  +  - ]
    1783         [ +  - ]:          2 :                                  << "Skipping.\n";
    1784                 :            :             }
    1785                 :            :         }
    1786                 :          5 :     }
    1787         [ +  + ]:          4 :     if (ok)
    1788         [ +  - ]:          3 :         command_ = std::make_unique<DropIndex>(std::move(index_names));
    1789         [ -  + ]:          4 : }
    1790                 :            : 
    1791                 :        575 : void Sema::operator()(SelectStmt &s)
    1792                 :            : {
    1793                 :        575 :     RequireContext RCtx(this, s);
    1794         [ +  - ]:        575 :     Catalog &C = Catalog::Get();
    1795                 :            : 
    1796         [ +  + ]:        575 :     if (s.from) {
    1797   [ +  -  +  + ]:        547 :         if (not C.has_database_in_use()) {
    1798   [ +  -  +  - ]:          1 :             diag.err() << "No database selected.\n";
    1799                 :          1 :             return;
    1800                 :            :         }
    1801         [ +  - ]:        546 :         (*this)(*s.from);
    1802                 :        546 :     }
    1803   [ +  +  +  - ]:        574 :     if (s.where) (*this)(*s.where);
    1804   [ +  +  +  - ]:        574 :     if (s.group_by) (*this)(*s.group_by);
    1805   [ +  +  +  - ]:        574 :     if (s.having) (*this)(*s.having);
    1806         [ +  - ]:        574 :     (*this)(*s.select);
    1807   [ +  +  +  - ]:        574 :     if (s.order_by) (*this)(*s.order_by);
    1808   [ +  +  +  - ]:        574 :     if (s.limit) (*this)(*s.limit);
    1809                 :            : 
    1810                 :            : 
    1811   [ +  +  +  -  :        574 :     if (not is_nested() and not diag.num_errors())
                   +  + ]
    1812         [ +  - ]:        421 :         command_ = std::make_unique<QueryDatabase>();
    1813         [ -  + ]:        575 : }
    1814                 :            : 
    1815                 :        768 : void Sema::operator()(InsertStmt &s)
    1816                 :            : {
    1817                 :        768 :     RequireContext RCtx(this, s);
    1818         [ +  - ]:        768 :     Catalog &C = Catalog::Get();
    1819                 :            : 
    1820   [ +  -  +  + ]:        768 :     if (not C.has_database_in_use()) {
    1821   [ +  -  +  - ]:          1 :         diag.e(s.table_name.pos) << "No database in use.\n";
    1822                 :          1 :         return;
    1823                 :            :     }
    1824         [ +  - ]:        767 :     auto &DB = C.get_database_in_use();
    1825                 :            : 
    1826                 :            :     const Table *tbl;
    1827                 :            :     try {
    1828   [ +  -  +  + ]:        767 :         tbl = &DB.get_table(s.table_name.text.assert_not_none());
    1829         [ -  + ]:        767 :     } catch (std::out_of_range) {
    1830   [ +  -  +  -  :          1 :         diag.e(s.table_name.pos) << "Table " << s.table_name.text << " does not exist in database " << DB.name << ".\n";
          +  -  +  -  +  
                -  +  - ]
    1831                 :            :         return;
    1832   [ +  -  #  # ]:          1 :     }
    1833                 :            : 
    1834                 :            :     /* Analyze values. */
    1835         [ +  + ]:       1610 :     for (std::size_t i = 0; i != s.tuples.size(); ++i) {
    1836                 :        844 :         auto &t = s.tuples[i];
    1837         [ -  + ]:        844 :         if (t.empty())
    1838                 :          0 :             continue; // syntax error, already reported
    1839   [ +  -  +  + ]:        844 :         if (t.size() != tbl->num_attrs()) {
    1840   [ +  -  +  -  :          2 :             diag.e(s.table_name.pos) << "Tuple " << (i + 1) << " has not enough values.\n";
             +  -  +  - ]
    1841                 :          2 :             continue;
    1842                 :            :         }
    1843   [ +  -  +  -  :       4238 :         for (auto [it, j] = std::tuple{tbl->begin(), 0}; it != tbl->end(); ++it, ++j) {
          +  -  +  +  +  
                      - ]
    1844                 :       6792 :             auto &v = t[j];
    1845         [ +  - ]:       3396 :             auto &attr = *it;
    1846      [ +  +  + ]:       3396 :             switch (v.first) {
    1847                 :            :                 case InsertStmt::I_Expr: {
    1848         [ +  - ]:       3210 :                     (*this)(*v.second);
    1849   [ +  -  +  -  :       3210 :                     if (v.second->type()->is_error()) continue;
                   -  + ]
    1850   [ +  -  +  - ]:       3210 :                     auto ty = as<const PrimitiveType>(v.second->type());
    1851   [ +  -  +  +  :       3210 :                     if (ty->is_boolean() and attr.type->is_boolean())
             +  -  +  + ]
    1852                 :        127 :                         break;
    1853   [ +  -  +  +  :       3083 :                     if (ty->is_character_sequence() and attr.type->is_character_sequence())
             +  -  +  - ]
    1854                 :         45 :                         break;
    1855   [ +  -  -  +  :       3038 :                     if (ty->is_date() and attr.type->is_date())
             #  #  #  # ]
    1856                 :          0 :                         break;
    1857   [ +  -  -  +  :       3038 :                     if (ty->is_date_time() and attr.type->is_date_time())
             #  #  #  # ]
    1858                 :          0 :                         break;
    1859   [ +  -  +  +  :       3038 :                     if (ty->is_numeric() and attr.type->is_numeric())
             +  -  +  + ]
    1860                 :       3034 :                         break;
    1861   [ +  -  +  -  :          4 :                     diag.e(s.table_name.pos) << "Value " << *v.second << " is not valid for attribute "
             +  -  +  - ]
    1862   [ +  -  +  - ]:          4 :                                              << attr.name << ".\n";
    1863                 :          4 :                     break;
    1864                 :            :                 }
    1865                 :            : 
    1866                 :            :                 case InsertStmt::I_Null: {
    1867         [ -  + ]:        183 :                     if (attr.not_nullable)
    1868   [ #  #  #  #  :          0 :                         diag.e(s.table_name.pos) << "Value NULL is not valid for attribute " << attr.name
                   #  # ]
    1869         [ #  # ]:          0 :                                                  << " declared as NOT NULL.\n";
    1870                 :        183 :                     break;
    1871                 :            :                 }
    1872                 :            : 
    1873                 :            :                 case InsertStmt::I_Default:
    1874                 :            :                     /* TODO has default? */
    1875                 :          3 :                     break;
    1876                 :            :             }
    1877                 :       3396 :         }
    1878                 :        842 :     }
    1879                 :            : 
    1880   [ +  -  +  -  :        766 :     if (not is_nested() and not diag.num_errors())
                   +  + ]
    1881         [ +  - ]:        761 :         command_ = std::make_unique<InsertRecords>();
    1882         [ -  + ]:        769 : }
    1883                 :            : 
    1884                 :          0 : void Sema::operator()(UpdateStmt &s)
    1885                 :            : {
    1886                 :          0 :     RequireContext RCtx(this, s);
    1887                 :            :     /* TODO */
    1888                 :          0 :     (void) s;
    1889         [ #  # ]:          0 :     M_unreachable("Not implemented.");
    1890                 :          0 : }
    1891                 :            : 
    1892                 :          0 : void Sema::operator()(DeleteStmt &s)
    1893                 :            : {
    1894                 :          0 :     RequireContext RCtx(this, s);
    1895                 :            :     /* TODO */
    1896                 :          0 :     (void) s;
    1897         [ #  # ]:          0 :     M_unreachable("Not implemented.");
    1898                 :          0 : }
    1899                 :            : 
    1900                 :          9 : void Sema::operator()(DSVImportStmt &s)
    1901                 :            : {
    1902                 :          9 :     RequireContext RCtx(this, s);
    1903         [ +  - ]:          9 :     auto &C = Catalog::Get();
    1904                 :            : 
    1905   [ +  -  +  + ]:          9 :     if (not C.has_database_in_use()) {
    1906   [ +  -  +  - ]:          1 :         diag.e(s.table_name.pos) << "No database selected\n";
    1907                 :          1 :         return;
    1908                 :            :     }
    1909         [ +  - ]:          8 :     auto &DB = C.get_database_in_use();
    1910                 :            : 
    1911                 :          8 :     const Table *table = nullptr;
    1912                 :            :     try {
    1913   [ +  -  +  + ]:          8 :         table = &DB.get_table(s.table_name.text.assert_not_none());
    1914         [ -  + ]:          8 :     } catch (std::out_of_range) {
    1915   [ +  -  +  -  :          1 :         diag.e(s.table_name.pos) << "Table " << s.table_name.text << " does not exist in database " << DB.name << ".\n";
          +  -  +  -  +  
                -  +  - ]
    1916   [ +  -  #  # ]:          1 :     }
    1917                 :            : 
    1918                 :          8 :     DSVReader::Config cfg;
    1919                 :          8 :     cfg.has_header = s.has_header;
    1920                 :          8 :     cfg.skip_header = s.skip_header;
    1921   [ +  -  +  + ]:          8 :     if (s.rows)
    1922         [ +  - ]:          1 :         cfg.num_rows = atoi(*s.rows.text);
    1923                 :            : 
    1924                 :            :     /* If character was provided by user, check that length is equal to 1. */
    1925                 :            : #define SET_CHAR(NAME) \
    1926                 :            :     if (s.NAME) { \
    1927                 :            :         std::string NAME = interpret(*s.NAME.text); \
    1928                 :            :         if (NAME.length() == 1) \
    1929                 :            :             cfg.NAME = NAME[0]; \
    1930                 :            :         else \
    1931                 :            :             diag.e(s.NAME.pos) << "Invalid " #NAME " character " << s.NAME.text << ". Must have length 1.\n"; \
    1932                 :            :     }
    1933   [ +  -  +  +  :          9 :     SET_CHAR(delimiter);
          +  -  +  -  +  
          -  +  +  +  -  
          +  -  +  -  +  
                -  +  - ]
    1934   [ +  -  +  +  :          9 :     SET_CHAR(quote);
          +  -  +  -  +  
          -  +  +  +  -  
          +  -  +  -  +  
                -  +  - ]
    1935   [ +  -  +  +  :          9 :     SET_CHAR(escape);
          +  -  +  -  +  
          -  +  +  +  -  
          +  -  +  -  +  
                -  +  - ]
    1936                 :            : #undef SET_CHAR
    1937                 :            : 
    1938                 :            :     /* Delimiter and quote character must be distinct. */
    1939         [ +  + ]:          8 :     if (cfg.delimiter == cfg.quote) {
    1940   [ +  -  +  - ]:          1 :         auto pos = s.delimiter ? s.delimiter.pos : s.quote.pos;
    1941   [ +  -  +  -  :          1 :         diag.e(pos) << "The delimiter (" << cfg.delimiter << ") must differ from the quote character (" << cfg.quote
          +  -  +  -  +  
                      - ]
    1942         [ +  - ]:          1 :                     << ").\n";
    1943                 :          1 :     }
    1944                 :            : 
    1945                 :            :     /* Sanity check for skip header. */
    1946   [ +  +  +  + ]:          8 :     if (cfg.skip_header and not cfg.has_header) {
    1947   [ +  -  -  + ]:          1 :         if (not Options::Get().quiet)
    1948   [ #  #  #  # ]:          0 :             diag.n(s.path.pos) << "I will assume the existence of a header so I can skip it.\n";
    1949                 :          1 :     }
    1950                 :            : 
    1951                 :            :     /* Get filesystem path from path token by removing surrounding quotation marks. */
    1952   [ +  -  +  -  :          8 :     std::filesystem::path path(std::string(*s.path.text, 1, strlen(*s.path.text) - 2));
             +  -  +  - ]
    1953                 :            : 
    1954   [ +  -  +  + ]:          8 :     if (not diag.num_errors())
    1955         [ +  - ]:          3 :         command_ = std::make_unique<ImportDSV>(*table, path, std::move(cfg));
    1956         [ -  + ]:         10 : }

Generated by: LCOV version 1.16