LCOV - code coverage report
Current view: top level - src/backend - Interpreter.hpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 71 84 84.5 %
Date: 2025-03-25 01:19:55 Functions: 31 37 83.8 %
Branches: 33 60 55.0 %

           Branch data     Line data    Source code
       1                 :            : #pragma once
       2                 :            : 
       3                 :            : #include "backend/InterpreterOperator.hpp"
       4                 :            : #include "backend/StackMachine.hpp"
       5                 :            : #include <cerrno>
       6                 :            : #include <ctime>
       7                 :            : #include <mutable/backend/Backend.hpp>
       8                 :            : #include <mutable/catalog/Catalog.hpp>
       9                 :            : #include <mutable/IR/Operator.hpp>
      10                 :            : #include <mutable/IR/Tuple.hpp>
      11                 :            : #include <mutable/util/macro.hpp>
      12                 :            : #include <unordered_map>
      13                 :            : 
      14                 :            : 
      15                 :            : namespace m {
      16                 :            : 
      17                 :            : /** A block of size `N` contains `N` tuples.  */
      18                 :            : template<std::size_t N>
      19                 :            : struct Block
      20                 :            : {
      21                 :            :     static constexpr std::size_t CAPACITY = N; ///< the capacity of a block
      22                 :            : 
      23                 :            :     private:
      24                 :            :     template<bool C>
      25                 :            :     struct the_iterator
      26                 :            :     {
      27                 :            :         static constexpr bool IsConst = C;
      28                 :            : 
      29                 :            :         using block_t = std::conditional_t<IsConst, const Block, Block>;
      30                 :            :         using reference = std::conditional_t<IsConst, const Tuple&, Tuple&>;
      31                 :            :         using pointer = std::conditional_t<IsConst, const Tuple*, Tuple*>;
      32                 :            : 
      33                 :            :         private:
      34                 :            :         block_t &block_;
      35                 :            :         uint64_t mask_;
      36                 :            : 
      37                 :            :         public:
      38                 :       4790 :         the_iterator(block_t &vec, uint64_t mask) : block_(vec), mask_(mask) { }
      39                 :            : 
      40                 :       8920 :         bool operator==(the_iterator other) {
      41                 :       8920 :             M_insist(&this->block_ == &other.block_);
      42                 :       8920 :             return this->mask_ == other.mask_;
      43                 :            :         }
      44                 :       8920 :         bool operator!=(the_iterator other) { return not operator==(other); }
      45                 :            : 
      46                 :       8700 :         the_iterator & operator++() { mask_ = mask_ & (mask_ - 1UL); /* set lowest 1-bit to 0 */ return *this; }
      47                 :            :         the_iterator operator++(int) { the_iterator clone(*this); operator++(); return clone; }
      48                 :            : 
      49                 :      13050 :         std::size_t index() const { return __builtin_ctzl(mask_); }
      50                 :            : 
      51                 :       8700 :         reference operator*() const { return block_[index()]; }
      52                 :            :         pointer operator->() const { return &block_[index()]; }
      53                 :            :     };
      54                 :            : 
      55                 :            :     public:
      56                 :            :     using iterator = the_iterator<false>;
      57                 :            :     using const_iterator = the_iterator<true>;
      58                 :            : 
      59                 :            :     private:
      60                 :            :     std::array<Tuple, N> data_; ///< an array of the tuples of this `Block`; some slots may be unused
      61                 :        162 :     uint64_t mask_ = 0x0; ///< a mast identifying which slots of `data_` are in use
      62                 :            :     static_assert(N <= 64, "maximum block size exceeded");
      63                 :            :     Schema schema_;
      64                 :            : 
      65                 :            :     public:
      66                 :          0 :     Block() = default;
      67                 :            :     Block(const Block&) = delete;
      68                 :            :     Block(Block&&) = delete;
      69                 :            : 
      70                 :            :     /** Create a new `Block` with tuples of `Schema` `schema`. */
      71                 :        162 :     Block(Schema schema)
      72                 :        162 :         : schema_(std::move(schema))
      73                 :            :     {
      74         [ +  + ]:      10530 :         for (auto &t : data_)
      75   [ +  -  +  - ]:      10368 :             t = Tuple(schema_);
      76                 :        162 :     }
      77                 :            : 
      78                 :            :     /** Return a pointer to the underlying array of tuples. */
      79                 :            :     Tuple * data() { return data_.data(); }
      80                 :            :     /** Return a pointer to the underlying array of tuples. */
      81                 :            :     const Tuple * data() const { return data_.data(); }
      82                 :            : 
      83                 :         68 :     const Schema & schema() const { return schema_; }
      84                 :            : 
      85                 :            :     /** Return the capacity of this `Block`. */
      86                 :      39399 :     static constexpr std::size_t capacity() { return CAPACITY; }
      87                 :            :     /** Return the number of *alive* tuples in this `Block`. */
      88                 :         42 :     std::size_t size() const { return __builtin_popcountl(mask_); }
      89                 :            : 
      90                 :        220 :     iterator begin() { return iterator(*this, mask_); }
      91                 :       4570 :     iterator end()   { return iterator(*this, 0UL); }
      92                 :            :     const_iterator begin() const { return const_iterator(*this, mask_); }
      93                 :            :     const_iterator end()   const { return const_iterator(*this, 0UL); }
      94                 :            :     const_iterator cbegin() const { return const_iterator(*this, mask_); }
      95                 :            :     const_iterator cend()   const { return const_iterator(*this, 0UL); }
      96                 :            : 
      97                 :            :     /** Returns an iterator to the tuple at index `index`. */
      98                 :            :     iterator at(std::size_t index) {
      99                 :            :         M_insist(index < capacity());
     100                 :            :         return iterator(*this, mask_ & (-1UL << index));
     101                 :            :     }
     102                 :            :     /** Returns an iterator to the tuple at index `index`. */
     103                 :            :     const_iterator at(std::size_t index) const { return const_cast<Block>(this)->at(index); }
     104                 :            : 
     105                 :            :     /** Check whether the tuple at the given `index` is alive. */
     106                 :      17400 :     bool alive(std::size_t index) const {
     107                 :      17400 :         M_insist(index < capacity());
     108                 :      17400 :         return mask_ & (1UL << index);
     109                 :            :     }
     110                 :            : 
     111                 :            :     /** Returns `true` iff the block has no *alive* tuples, i.e.\ `size() == 0`. */
     112                 :          0 :     bool empty() const { return size() == 0; }
     113                 :            : 
     114                 :            :     /** Returns the bit mask that identifies which tuples of this `Block` are alive. */
     115                 :        110 :     uint64_t mask() const { return mask_; }
     116                 :            :     /** Returns the bit mask that identifies which tuples of this `Block` are alive. */
     117                 :        340 :     void mask(uint64_t new_mask) { mask_ = new_mask; }
     118                 :            : 
     119                 :            :     private:
     120                 :            :     /** Returns a bit vector with left-most `capacity()` many bits set to `1` and the others set to `0`.  */
     121                 :         42 :     static constexpr uint64_t AllOnes() { return -1UL >> (8 * sizeof(mask_) - capacity()); }
     122                 :            : 
     123                 :            :     public:
     124                 :            :     /** Returns the tuple at index `index`.  The tuple must be *alive*!  */
     125                 :      17400 :     Tuple & operator[](std::size_t index) {
     126                 :      17400 :         M_insist(index < capacity(), "index out of bounds");
     127                 :      17400 :         M_insist(alive(index), "cannot access a dead tuple directly");
     128                 :      17400 :         return data_[index];
     129                 :            :     }
     130                 :            :     /** Returns the tuple at index `index`.  The tuple must be *alive*!  */
     131                 :            :     const Tuple & operator[](std::size_t index) const { return const_cast<Block*>(this)->operator[](index); }
     132                 :            : 
     133                 :            :     /** Make all tuples in this `Block` *alive*. */
     134                 :         42 :     void fill() { mask_ = AllOnes(); M_insist(size() == capacity()); }
     135                 :            : 
     136                 :            :     /** Erase the tuple at the given `index` from this `Block`. */
     137                 :          0 :     void erase(std::size_t index) {
     138                 :          0 :         M_insist(index < capacity(), "index out of bounds");
     139                 :          0 :         setbit(&mask_, false, index);
     140                 :          0 :     }
     141                 :            :     /** Erase the tuple identified by `it` from this `Block`. */
     142                 :          0 :     void erase(iterator it) { erase(it.index()); }
     143                 :            :     /** Erase the tuple identified by `it` from this `Block`. */
     144                 :            :     void erase(const_iterator it) { erase(it.index()); }
     145                 :            : 
     146                 :            :     /** Renders all tuples *dead* and removes their attributes.. */
     147                 :        220 :     void clear() {
     148                 :        220 :         mask_ = 0;
     149         [ +  + ]:      14300 :         for (auto &t : data_)
     150                 :      14080 :             t.clear();
     151                 :        220 :     }
     152                 :            : 
     153                 :            : M_LCOV_EXCL_START
     154                 :            :     /** Print a textual representation of this `Block` to `out`. */
     155                 :            :     friend std::ostream & operator<<(std::ostream &out, const Block<N> &block) {
     156                 :            :         out << "Block<" << block.capacity() << "> with " << block.size() << " elements:\n";
     157                 :            :         for (std::size_t i = 0; i != block.capacity(); ++i) {
     158                 :            :             out << "    " << i << ": ";
     159                 :            :             if (block.alive(i))
     160                 :            :                 out << block[i];
     161                 :            :             else
     162                 :            :                 out << "[dead]";
     163                 :            :             out << '\n';
     164                 :            :         }
     165                 :            :         return out;
     166                 :            :     }
     167                 :            : 
     168                 :            :     void dump(std::ostream &out) const
     169                 :            :     {
     170                 :            :         out << *this;
     171                 :            :         out.flush();
     172                 :            :     }
     173                 :            :     void dump() const { dump(std::cerr); }
     174                 :            : M_LCOV_EXCL_STOP
     175                 :            : };
     176                 :            : 
     177                 :            : struct Interpreter;
     178                 :            : 
     179                 :            : /** Implements push-based evaluation of a pipeline in the plan. */
     180                 :            : struct Pipeline : ConstOperatorVisitor
     181                 :            : {
     182                 :            :     friend struct Interpreter;
     183                 :            : 
     184                 :            :     private:
     185                 :            :     Block<64> block_;
     186                 :            : 
     187                 :            :     public:
     188         [ #  # ]:          0 :     Pipeline() { }
     189                 :            : 
     190                 :        162 :     Pipeline(const Schema &schema)
     191   [ +  -  +  - ]:        162 :         : block_(schema)
     192                 :        162 :     {
     193         [ -  + ]:        162 :         block_.mask(1UL); // create one empty tuple in the block
     194                 :        162 :     }
     195                 :            : 
     196                 :            :     Pipeline(Tuple &&t)
     197                 :            :     {
     198                 :            :         block_.mask(1UL);
     199                 :            :         block_[0] = std::move(t);
     200                 :            :     }
     201                 :            : 
     202                 :        191 :     void push(const Operator &pipeline_start) { (*this)(pipeline_start); }
     203                 :            : 
     204                 :        110 :     void clear() { block_.clear(); }
     205                 :            : 
     206                 :         68 :     const Schema & schema() const { return block_.schema(); }
     207                 :            : 
     208                 :            :     using ConstOperatorVisitor::operator();
     209                 :            : #define DECLARE(CLASS) void operator()(Const<CLASS> &op) override;
     210                 :            :     M_OPERATOR_LIST(DECLARE)
     211                 :            : #undef DECLARE
     212                 :            : };
     213                 :            : 
     214                 :            : /** Evaluates SQL operator trees on the database. */
     215                 :            : struct Interpreter : Backend, ConstOperatorVisitor
     216                 :            : {
     217                 :            :     public:
     218         [ +  - ]:          7 :     Interpreter() = default;
     219                 :            : 
     220                 :         81 :     void register_operators(PhysicalOptimizer &phys_opt) const override { register_interpreter_operators(phys_opt); }
     221                 :            : 
     222                 :         81 :     void execute(const MatchBase &plan) const override {
     223                 :         81 :         (*const_cast<Interpreter*>(this))(plan.get_matched_root()); // use former visitor pattern on logical operators
     224                 :         81 :     }
     225                 :            : 
     226                 :            :     using ConstOperatorVisitor::operator();
     227                 :            : #define DECLARE(CLASS) void operator()(Const<CLASS> &op) override;
     228                 :            :     M_OPERATOR_LIST(DECLARE)
     229                 :            : #undef DECLARE
     230                 :            : 
     231                 :       3277 :     static Value eval(const ast::Constant &c)
     232                 :            :     {
     233                 :       3277 :         errno = 0;
     234   [ +  +  -  +  :       3277 :         switch (c.tok.type) {
          +  +  +  +  +  
                -  -  - ]
     235                 :          0 :             default: M_unreachable("illegal token");
     236                 :            : 
     237                 :            :             /* Null */
     238                 :            :             case TK_Null:
     239                 :          0 :                 M_unreachable("NULL cannot be evaluated to a Value");
     240                 :            : 
     241                 :            :             /* Integer */
     242                 :            :             case TK_OCT_INT:
     243                 :         46 :                 return int64_t(strtoll(*c.tok.text, nullptr, 8));
     244                 :            : 
     245                 :            :             case TK_DEC_INT:
     246                 :       2967 :                 return int64_t(strtoll(*c.tok.text, nullptr, 10));
     247                 :            : 
     248                 :            :             case TK_HEX_INT:
     249                 :          0 :                 return int64_t(strtoll(*c.tok.text, nullptr, 16));
     250                 :            : 
     251                 :            :             /* Float */
     252                 :            :             case TK_DEC_FLOAT:
     253                 :         59 :                 return strtod(*c.tok.text, nullptr);
     254                 :            : 
     255                 :            :             case TK_HEX_FLOAT:
     256                 :          0 :                 M_unreachable("not implemented");
     257                 :            : 
     258                 :            :             /* String */
     259                 :            :             case TK_STRING_LITERAL: {
     260         [ +  - ]:         50 :                 std::string str(*c.tok.text);
     261         [ +  - ]:         50 :                 auto substr = interpret(str);
     262   [ +  -  +  -  :         50 :                 return Catalog::Get().pool(substr.c_str()); // return internalized string by reference
                   -  + ]
     263                 :         50 :             }
     264                 :            : 
     265                 :            :             /* Date */
     266                 :            :             case TK_DATE: {
     267                 :            :                 int year, month, day;
     268         [ +  - ]:          7 :                 if (3 != sscanf(*c.tok.text, "d'%d-%d-%d'", &year, &month, &day))
     269                 :          0 :                     M_unreachable("invalid date");
     270                 :          7 :                 return int32_t(unsigned(year) << 9 | month << 5 | day);
     271                 :            :             }
     272                 :            : 
     273                 :            :             /* Datetime */
     274                 :            :             case TK_DATE_TIME: {
     275                 :            :                 /* Extract date time from string. */
     276         [ +  - ]:          6 :                 std::string str_datetime(*c.tok.text);
     277   [ +  -  +  - ]:          6 :                 std::istringstream ss(str_datetime.substr(2, str_datetime.length() - 3));
     278                 :            : 
     279                 :            :                 /* Parse date time. */
     280                 :            :                 std::tm tm;
     281   [ +  -  +  - ]:          6 :                 ss >> get_tm(tm);
     282   [ +  -  +  - ]:          6 :                 M_insist(not ss.fail(), "failed to parse date time");
     283                 :            : 
     284                 :            : #if defined(__APPLE__)
     285                 :            :                 /* Adapt tm_year such that it is non-negative (i.e. >= 1900) since timegm() cannot handle these cases. */
     286                 :            :                 constexpr long SECONDS_PER_400_YEAR_CYCLE = 12622780800L;
     287                 :            :                 int cycles = tm.tm_year < 0 ? -tm.tm_year / 400 + 1 : 0;
     288                 :            :                 tm.tm_year += cycles * 400;
     289                 :            :                 M_insist(tm.tm_year >= 0);
     290                 :            : #endif
     291                 :            : 
     292                 :            :                 /* Convert date time from std::tm to time_t (seconds since epoch). */
     293                 :          6 :                 const time_t time = timegm(&tm);
     294         [ +  - ]:          6 :                 M_insist(time != -1, "datetime out of bounds");
     295                 :            : 
     296                 :            : #if defined(__APPLE__)
     297                 :            :                 return int64_t(time - cycles * SECONDS_PER_400_YEAR_CYCLE);
     298                 :            : #else
     299         [ +  - ]:          6 :                 return int64_t(time);
     300                 :            : #endif
     301                 :          6 :             }
     302                 :            : 
     303                 :            :             /* Boolean */
     304                 :            :             case TK_True:
     305                 :         74 :                 return true;
     306                 :            : 
     307                 :            :             case TK_False:
     308                 :         68 :                 return false;
     309                 :            :         }
     310                 :            :         M_insist(errno == 0, "constant could not be parsed");
     311                 :       3277 :     }
     312                 :            : 
     313                 :            :     /** Compile a `StackMachine` to load a tuple of `Schema` `tuple_schema` using a given memory address and a given
     314                 :            :      * `DataLayout`.
     315                 :            :      *
     316                 :            :      * @param tuple_schema  the `Schema` of the tuple to load, specifying the `Schema::Identifier`s to load
     317                 :            :      * @param address       the memory address of the `Store` we are loading from
     318                 :            :      * @param layout        the `DataLayout` of the `Table` we are loading from
     319                 :            :      * @param layout_schema the `Schema` of `layout`, specifying the `Schema::Identifier`s present in `layout`
     320                 :            :      * @param row_id        the ID of the *first* row to load
     321                 :            :      * @param tuple_id      the ID of the tuple used for loading
     322                 :            :      */
     323                 :            :     static StackMachine compile_load(const Schema &tuple_schema, void *address, const storage::DataLayout &layout,
     324                 :            :                                      const Schema &layout_schema, std::size_t row_id = 0, std::size_t tuple_id = 0);
     325                 :            : 
     326                 :            :     /** Compile a `StackMachine` to store a tuple of `Schema` `tuple_schema` using a given memory address and a given
     327                 :            :      * `DataLayout`.
     328                 :            :      *
     329                 :            :      * @param tuple_schema  the `Schema` of the tuple to store, specifying the `Schema::Identifier`s to store
     330                 :            :      * @param address       the memory address of the `Store` we are storing to
     331                 :            :      * @param layout        the `DataLayout` of the `Table` we are storing to
     332                 :            :      * @param layout_schema the `Schema` of `layout`, specifying the `Schema::Identifier`s present in `layout`
     333                 :            :      * @param row_id        the ID of the *first* row to store
     334                 :            :      * @param tuple_id      the ID of the tuple used for storing
     335                 :            :      */
     336                 :            :     static StackMachine compile_store(const Schema &tuple_schema, void *address, const storage::DataLayout &layout,
     337                 :            :                                       const Schema &layout_schema, std::size_t row_id = 0, std::size_t tuple_id = 0);
     338                 :            : };
     339                 :            : 
     340                 :            : }

Generated by: LCOV version 1.16