LCOV - code coverage report
Current view: top level - src/storage - Index.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 57 118 48.3 %
Date: 2025-03-25 01:19:55 Functions: 33 121 27.3 %
Branches: 168 1070 15.7 %

           Branch data     Line data    Source code
       1                 :            : #include <mutable/storage/Index.hpp>
       2                 :            : 
       3                 :            : #include <mutable/catalog/Schema.hpp>
       4                 :            : #include <mutable/catalog/Type.hpp>
       5                 :            : #include <mutable/mutable.hpp>
       6                 :            : #include <mutable/Options.hpp>
       7                 :            : #include <mutable/util/Timer.hpp>
       8                 :            : #include <sstream>
       9                 :            : 
      10                 :            : 
      11                 :            : using namespace m;
      12                 :            : using namespace m::idx;
      13                 :            : 
      14                 :            : namespace {
      15                 :            : 
      16                 :            : namespace options {
      17                 :            : 
      18                 :            : /** Which ratio of linear models to index entries should be used for `idx::RecursiveModelIndex`. */
      19                 :            : double rmi_model_entry_ratio = 0.01;
      20                 :            : 
      21                 :            : }
      22                 :            : 
      23                 :            : __attribute__((constructor(201)))
      24                 :          1 : static void add_index_args()
      25                 :            : {
      26                 :          1 :     Catalog &C = Catalog::Get();
      27                 :            : 
      28                 :            :     /*----- Command-line arguments -----*/
      29                 :          1 :     C.arg_parser().add<double>(
      30                 :            :         /* group=       */ "Index",
      31                 :            :         /* short=       */ nullptr,
      32                 :            :         /* long=        */ "--rmi-model-entry-ratio",
      33                 :            :         /* description= */ "specify the ratio of linear models to index entries for recursive model indexes",
      34                 :          0 :         /* callback=    */ [](double rmi_model_entry_ratio){ options::rmi_model_entry_ratio = rmi_model_entry_ratio; }
      35                 :            :     );
      36                 :          1 : }
      37                 :            : 
      38                 :            : }
      39                 :            : 
      40                 :          6 : std::string IndexBase::build_query(const Table &table, const Schema &schema)
      41                 :            : {
      42                 :          6 :     std::ostringstream oss;
      43         [ +  - ]:          6 :     oss << "SELECT ";
      44   [ +  -  +  + ]:         12 :     for (std::size_t i = 0; i != schema.num_entries(); ++i) {
      45   [ -  +  #  # ]:          6 :         if (i != 0) oss << ", ";
      46   [ +  -  +  - ]:          6 :         oss << schema.at(i).id;
      47                 :          6 :     }
      48   [ +  -  +  -  :          6 :     oss << " FROM " << table.name() << ';';
             +  -  +  - ]
      49         [ +  - ]:          6 :     return oss.str();
      50                 :          6 : }
      51                 :            : 
      52                 :            : template<typename Key>
      53                 :          6 : void ArrayIndex<Key>::bulkload(const Table &table, const Schema &key_schema)
      54                 :            : {
      55                 :            :     /* XXX: Disable timer during execution to not print times for query that is performed as part of bulkloading. */
      56                 :          6 :     const auto &old_timer = std::exchange(Catalog::Get().timer(), Timer());
      57                 :            : 
      58                 :            :     /* Check that key schema contains a single entry. */
      59   [ #  #  #  #  :          6 :     if (key_schema.num_entries() != 1)
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  #  
                #  #  # ]
      60   [ #  #  #  #  :          0 :         throw invalid_argument("Key schema should contain exactly one entry.");
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
             #  #  #  #  
                      # ]
      61   [ #  #  #  #  :          6 :     auto entry = key_schema.at(0);
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  #  
                #  #  # ]
      62                 :            : 
      63                 :            :     /* Check that key type and attribute type match. */
      64                 :          6 :     auto attribute_type = entry.type;
      65                 :            : #define CHECK(TYPE) \
      66                 :            :     if constexpr(not std::same_as<key_type, TYPE>) \
      67                 :          0 :         throw invalid_argument("Key type and attribute type do not match."); \
      68                 :            :     return
      69                 :            : 
      70   [ #  #  +  -  :          6 :     visit(overloaded {
          +  -  +  -  +  
          -  +  -  +  -  
                   #  # ]
      71   [ #  #  #  #  :          0 :         [](const Boolean&) { CHECK(bool); },
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
      72                 :          6 :         [](const Numeric &n) {
      73   [ #  #  #  -  :          6 :             switch (n.kind) {
          +  -  -  +  -  
          -  +  -  -  +  
          -  -  +  -  -  
             +  -  #  #  
                      # ]
      74                 :          1 :                 case Numeric::N_Int:
      75                 :            :                 case Numeric::N_Decimal:
      76   [ #  #  #  #  :          4 :                     switch (n.size()) {
          #  +  -  -  -  
          -  +  -  -  -  
          -  +  -  -  -  
          -  +  -  -  -  
          -  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                      # ]
      77                 :          0 :                         default: M_unreachable("invalid size");
      78   [ #  #  #  #  :          1 :                         case  8: CHECK(int8_t);
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                #  #  # ]
      79   [ #  #  #  #  :          1 :                         case 16: CHECK(int16_t);
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                #  #  # ]
      80   [ #  #  #  #  :          1 :                         case 32: CHECK(int32_t);
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                #  #  # ]
      81   [ #  #  #  #  :          1 :                         case 64: CHECK(int64_t);
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                #  #  # ]
      82                 :            :                     }
      83                 :            :                 case Numeric::N_Float:
      84   [ #  #  #  #  :          2 :                     switch (n.size()) {
          #  #  #  #  #  
          #  #  #  #  #  
          #  +  -  -  +  
             -  -  #  #  
                      # ]
      85                 :          0 :                         default: M_unreachable("invalid size");
      86   [ #  #  #  #  :          1 :                         case 32: CHECK(float);
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                #  #  # ]
      87   [ #  #  #  #  :          1 :                         case 64: CHECK(double);
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                #  #  # ]
      88                 :            :                     }
      89                 :            :             }
      90                 :          6 :         },
      91   [ #  #  #  #  :          0 :         [](const CharacterSequence&) { CHECK(const char*); },
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
      92   [ #  #  #  #  :          0 :         [](const Date&) { CHECK(int32_t); },
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
      93   [ #  #  #  #  :          0 :         [](const DateTime&) { CHECK(int64_t); },
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
      94                 :          0 :         [](auto&&) { M_unreachable("invalid type"); },
      95                 :          6 :     }, *attribute_type);
      96                 :            : #undef CHECk
      97                 :            : 
      98                 :            :     /* Build the query to retrieve keys. */
      99   [ #  #  +  -  :          6 :     auto query = build_query(table, key_schema);
          +  -  +  -  +  
          -  +  -  +  -  
                   #  # ]
     100                 :            : 
     101                 :            :     /* Create the diagnostics object. */
     102   [ #  #  #  #  :          6 :     Diagnostic diag(Options::Get().has_color, std::cout, std::cerr);
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  #  
                #  #  # ]
     103                 :            : 
     104                 :            :     /* Compute statement from query string. */
     105   [ #  #  +  -  :          6 :     auto stmt = statement_from_string(diag, query);
          +  -  +  -  +  
          -  +  -  +  -  
                   #  # ]
     106                 :            : 
     107                 :            :     /* Define get function based on key_type. */
     108                 :          6 :     std::function<key_type(const Tuple&)> fn_get;
     109                 :            :     if constexpr(integral<key_type>)
     110                 :         16 :         fn_get = [](const Tuple &t) { return static_cast<key_type>(t.get(0).as<int64_t>()); };
     111                 :            :     else // bool, float, double, const char*
     112                 :          8 :         fn_get = [](const Tuple &t) { return t.get(0).as<key_type>(); };
     113                 :            : 
     114                 :            :     /* Define callback operator to add keys to index. */
     115                 :          6 :     std::size_t tuple_id = 0;
     116                 :         30 :     auto fn_add = [&](const Schema&, const Tuple &tuple) {
     117   [ #  #  +  +  :         24 :         if (not tuple.is_null(0))
          +  +  +  +  +  
          +  +  +  +  +  
                   #  # ]
     118                 :         18 :             this->add(fn_get(tuple), tuple_id);
     119                 :         24 :         tuple_id++;
     120                 :         24 :     };
     121   [ #  #  +  -  :          6 :     auto consumer = std::make_unique<CallbackOperator>(fn_add);
          +  -  +  -  +  
          -  +  -  +  -  
                   #  # ]
     122                 :            : 
     123                 :            :     /* Create backend on which the query is exectued.  We are using the `Interpreter` as the `wasm::Scan` might have
     124                 :            :      * been deactivated via CLI which results in not finding a plan for the query.
     125                 :            :      * TODO: We plan on using the default `Backend` set in the `Catalog` with temporarily adjusting the options to make
     126                 :            :      * sure `wasm::Scan` is available. */
     127   [ #  #  -  +  :          6 :     static thread_local std::unique_ptr<Backend> backend;
          -  +  -  +  -  
          +  -  +  -  +  
                   #  # ]
     128   [ #  #  +  -  :          6 :     if (not backend)
          +  -  +  -  +  
          -  +  -  +  -  
                   #  # ]
     129   [ #  #  #  #  :          6 :         backend = Catalog::Get().create_backend(Catalog::Get().pool("Interpreter"));
          #  #  #  #  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  #  #  #  
             #  #  #  #  
                      # ]
     130                 :            : 
     131                 :            :     /* Execute query to insert tuples. */
     132   [ #  #  #  #  :          6 :     m::execute_query(diag, as<ast::SelectStmt>(*stmt), std::move(consumer), *backend);
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  #  
                #  #  # ]
     133                 :            : 
     134                 :            :     /* Finalize index. */
     135   [ #  #  +  -  :          6 :     finalize();
          +  -  +  -  +  
          -  +  -  +  -  
                   #  # ]
     136                 :            : 
     137                 :            :     /* XXX: Reenable timer. */
     138   [ #  #  #  #  :          6 :     std::exchange(Catalog::Get().timer(), std::move(old_timer));
          #  #  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  #  #  
             #  #  #  # ]
     139                 :          6 : }
     140                 :            : 
     141                 :            : template<typename Key>
     142                 :         37 : void ArrayIndex<Key>::add(const key_type key, const value_type value)
     143                 :            : {
     144                 :            :     if constexpr(std::same_as<key_type, const char*>) {
     145                 :          0 :         Catalog &C = Catalog::Get();
     146         [ #  # ]:          0 :         data_.emplace_back(C.pool(key), value);
     147                 :            :     } else {
     148                 :         37 :         data_.emplace_back(key, value);
     149                 :            :     }
     150                 :         37 :     finalized_ = false;
     151                 :         37 : }
     152                 :            : 
     153                 :            : template<arithmetic Key>
     154                 :          0 : void RecursiveModelIndex<Key>::finalize()
     155                 :            : {
     156                 :            :     /* Sort data. */
     157                 :          0 :     std::sort(base_type::data_.begin(), base_type::data_.end(), base_type::cmp);
     158                 :            : 
     159                 :            :     /* Compute number of models. */
     160                 :          0 :     auto begin = base_type::begin();
     161                 :          0 :     auto end = base_type::end();
     162                 :          0 :     std::size_t n_keys = std::distance(begin, end);
     163                 :          0 :     std::size_t n_models = std::max<std::size_t>(1, n_keys * options::rmi_model_entry_ratio);
     164                 :          0 :     models_.reserve(n_models + 1);
     165                 :            : 
     166                 :            :     /* Train first layer. */
     167                 :          0 :     models_.emplace_back(
     168                 :          0 :         LinearModel::train_linear_spline(
     169                 :          0 :             /* begin=              */ begin,
     170                 :          0 :             /* end=                */ end,
     171                 :            :             /* offset=             */ 0,
     172                 :          0 :             /* compression_factor= */ static_cast<double>(n_models) / n_keys
     173                 :            :         )
     174                 :            :     );
     175                 :            : 
     176                 :            :     /* Train second layer. */
     177                 :          0 :     auto get_segment_id = [&](entry_type e) { return std::clamp<double>(models_[0](e.first), 0, n_models - 1);  };
     178                 :          0 :     std::size_t segment_start = 0;
     179                 :          0 :     std::size_t segment_id = 0;
     180   [ #  #  #  #  :          0 :     for (std::size_t i = 0; i != n_keys; ++i) {
          #  #  #  #  #  
                #  #  # ]
     181                 :          0 :         auto pos = begin + i;
     182                 :          0 :         std::size_t pred_segment_id = get_segment_id(*pos);
     183   [ #  #  #  #  :          0 :         if (pred_segment_id > segment_id) {
          #  #  #  #  #  
                #  #  # ]
     184                 :          0 :             models_.emplace_back(
     185                 :          0 :                 LinearModel::train_linear_regression(
     186                 :          0 :                     /* begin=  */ begin + segment_start,
     187                 :          0 :                     /* end=    */ pos,
     188                 :          0 :                     /* offset= */ segment_start
     189                 :            :                 )
     190                 :            :             );
     191   [ #  #  #  #  :          0 :             for (std::size_t j = segment_id + 1; j < pred_segment_id; ++j) {
          #  #  #  #  #  
                #  #  # ]
     192                 :          0 :                 models_.emplace_back(
     193                 :          0 :                     LinearModel::train_linear_regression(
     194                 :          0 :                         /* begin=  */ pos - 1,
     195                 :          0 :                         /* end=    */ pos,
     196                 :          0 :                         /* offset= */ i - 1
     197                 :            :                     )
     198                 :            :                 );
     199                 :          0 :             }
     200                 :          0 :             segment_id = pred_segment_id;
     201                 :          0 :             segment_start = i;
     202                 :            : 
     203                 :          0 :         }
     204                 :          0 :     }
     205                 :          0 :     models_.emplace_back(
     206                 :          0 :         LinearModel::train_linear_regression(
     207                 :          0 :             /* begin=  */ begin + segment_start,
     208                 :          0 :             /* end=    */ end,
     209                 :          0 :             /* offset= */ segment_start
     210                 :            :         )
     211                 :            :     );
     212   [ #  #  #  #  :          0 :     for (std::size_t j = segment_id + 1; j < n_models; ++j) {
          #  #  #  #  #  
                #  #  # ]
     213                 :          0 :         models_.emplace_back(
     214                 :          0 :             LinearModel::train_linear_regression(
     215                 :          0 :                 /* begin=  */ end - 1,
     216                 :          0 :                 /* end=    */ end,
     217                 :          0 :                 /* offset= */ n_keys - 1
     218                 :            :             )
     219                 :            :         );
     220                 :          0 :     }
     221                 :            : 
     222                 :            :     /* Mark index as finalized. */
     223                 :          0 :     base_type::finalized_ = true;
     224                 :          0 : };
     225                 :            : 
     226                 :            : // explicit instantiations to prevent linker errors
     227                 :            : #define INSTANTIATE(CLASS) \
     228                 :            :     template struct CLASS;
     229                 :            : M_INDEX_LIST_TEMPLATED(INSTANTIATE)
     230                 :            : #undef INSTANTIATE

Generated by: LCOV version 1.16