File indexing completed on 2024-05-19 05:42:11

0001 // ct_lvtmdb_soci_helper.h                                         -*-C++-*-
0002 
0003 /*
0004 // Copyright 2023 Codethink Ltd <codethink@codethink.co.uk>
0005 // SPDX-License-Identifier: Apache-2.0
0006 //
0007 // Licensed under the Apache License, Version 2.0 (the "License");
0008 // you may not use this file except in compliance with the License.
0009 // You may obtain a copy of the License at
0010 //
0011 //     http://www.apache.org/licenses/LICENSE-2.0
0012 //
0013 // Unless required by applicable law or agreed to in writing, software
0014 // distributed under the License is distributed on an "AS IS" BASIS,
0015 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0016 // See the License for the specific language governing permissions and
0017 // limitations under the License.
0018 */
0019 
0020 #ifndef INCLUDED_CT_LVTMDB_SOCI_HELPER
0021 #define INCLUDED_CT_LVTMDB_SOCI_HELPER
0022 
0023 #include <any>
0024 #include <optional>
0025 #include <soci/soci.h>
0026 #include <soci/sqlite3/soci-sqlite3.h>
0027 #include <string>
0028 #include <vector>
0029 
0030 namespace Codethink::lvtmdb {
0031 
0032 using RawDBData = std::optional<std::any>;
0033 using RawDBCols = std::vector<RawDBData>;
0034 using RawDBRows = std::vector<RawDBCols>;
0035 
0036 namespace detail {
0037 
0038 template<typename T>
0039 static RawDBData _getDBData(soci::row& row, size_t pos)
0040 {
0041     if (row.get_indicator(pos) != soci::i_null) {
0042         return row.get<T>(pos);
0043     }
0044     return std::nullopt;
0045 }
0046 
0047 static RawDBData getDBData(soci::row& row, size_t pos)
0048 {
0049     auto const& props = row.get_properties(pos);
0050     switch (props.get_data_type()) {
0051     case soci::dt_string:
0052         return _getDBData<std::string>(row, pos);
0053     case soci::dt_double:
0054         return _getDBData<double>(row, pos);
0055     case soci::dt_integer:
0056         return _getDBData<int>(row, pos);
0057     case soci::dt_long_long:
0058         return _getDBData<long long>(row, pos);
0059     case soci::dt_unsigned_long_long:
0060         return _getDBData<unsigned long long>(row, pos);
0061     case soci::dt_date:
0062         /* Ignored */
0063         break;
0064     case soci::dt_blob:
0065         /* Ignored */
0066         break;
0067     case soci::dt_xml:
0068         /* Ignored */
0069         break;
0070     }
0071 
0072     throw std::runtime_error{"Unexpected data type"};
0073 }
0074 
0075 } // namespace detail
0076 
0077 struct SociHelper {
0078     //  Small helper methods and enums to be used from soci writer and
0079     // soci reader.
0080 
0081     enum class Key : int {
0082         Invalid = 0,
0083         DatabaseState = 1,
0084         Version = 2,
0085     };
0086 
0087     enum class Version : int {
0088         Unknown = 0,
0089         Jan22 = 1,
0090         March22 = 2,
0091         March23 = 3,
0092     };
0093 
0094     static constexpr int CURRENT_VERSION = static_cast<int>(Version::March23);
0095 
0096     static RawDBRows runSingleQuery(soci::session& db, std::string const& query)
0097     {
0098         soci::transaction tr(db);
0099         auto rowset = soci::rowset<soci::row>{db.prepare << query};
0100         tr.commit();
0101 
0102         // It is not possible to return the `rowset` directly since it'll lose internal references and most probably
0103         // crash. Currently, the approach is to copy all results, but this _may_ be a performance issue in some
0104         // contexts, but it seems the most user-friendly approach. One alternative would be to wrap the necessary
0105         // objects in a struct and yield rows either using an iterator or a coroutine (C++20). This may be done in the
0106         // future, if anyone ever has a plugin that is struggling due to those copies.
0107         auto resultRows = RawDBRows{};
0108         for (auto&& rowdata : rowset) {
0109             auto cols = RawDBCols{};
0110             for (decltype(rowdata.size()) i = 0; i < rowdata.size(); ++i) {
0111                 cols.emplace_back(detail::getDBData(rowdata, i));
0112             }
0113             resultRows.emplace_back(cols);
0114         }
0115         return resultRows;
0116     }
0117 };
0118 
0119 } // namespace Codethink::lvtmdb
0120 #endif