~singpolyma/biboumi

ref: 50cadf3dac0d56ef8181d1800cc30f8dcb749141 biboumi/src/database/select_query.hpp -rw-r--r-- 3.9 KiB
50cadf3d — louiz’ Implement our own database ORM, and update the whole code to use it 5 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#pragma once

#include <database/query.hpp>
#include <logger/logger.hpp>
#include <database/row.hpp>

#include <vector>
#include <string>

#include <sqlite3.h>

using namespace std::string_literals;

template <typename T>
typename std::enable_if<std::is_integral<T>::value, sqlite3_int64>::type
extract_row_value(sqlite3_stmt* statement, const int i)
{
  return sqlite3_column_int64(statement, i);
}

template <typename T>
typename std::enable_if<std::is_same<std::string, T>::value, std::string>::type
extract_row_value(sqlite3_stmt* statement, const int i)
{
  const auto size = sqlite3_column_bytes(statement, i);
  const unsigned char* str = sqlite3_column_text(statement, i);
  std::string result(reinterpret_cast<const char*>(str), size);
  return result;
}

template <std::size_t N=0, typename... T>
typename std::enable_if<N < sizeof...(T), void>::type
extract_row_values(Row<T...>& row, sqlite3_stmt* statement)
{
  using ColumnType = typename std::remove_reference<decltype(std::get<N>(row.columns))>::type;

  auto&& column = std::get<N>(row.columns);
  column.value = static_cast<decltype(column.value)>(extract_row_value<typename ColumnType::real_type>(statement, N));

  extract_row_values<N+1>(row, statement);
}

template <std::size_t N=0, typename... T>
typename std::enable_if<N == sizeof...(T), void>::type
extract_row_values(Row<T...>&, sqlite3_stmt*)
{}

template <typename... T>
struct SelectQuery: public Query
{
    SelectQuery(std::string table_name):
        Query("SELECT"),
        table_name(table_name)
    {
      this->insert_col_name<0>();
      this->body += " from " + this->table_name;
    }

    template <std::size_t N>
    typename std::enable_if<N < sizeof...(T), void>::type
    insert_col_name()
    {
      using ColumnsType = std::tuple<T...>;
      ColumnsType tuple{};
      auto value = std::get<N>(tuple);

      this->body += " "s + value.name;

      if (N < (sizeof...(T) - 1))
        this->body += ", ";

      this->insert_col_name<N+1>();
    }
    template <std::size_t N>
    typename std::enable_if<N == sizeof...(T), void>::type
    insert_col_name()
    {}

  SelectQuery& where()
    {
      this->body += " WHERE ";
      return *this;
    };

    SelectQuery& order_by()
    {
      this->body += " ORDER BY ";
      return *this;
    }

    SelectQuery& limit()
    {
      this->body += " LIMIT ";
      return *this;
    }

    auto execute(sqlite3* db)
    {
      auto statement = this->prepare(db);
      int i = 1;
      for (const std::string& param: this->params)
        {
          if (sqlite3_bind_text(statement, i, param.data(), static_cast<int>(param.size()), SQLITE_TRANSIENT) != SQLITE_OK)
            log_debug("Failed to bind ", param, " to param ", i);
          else
            log_debug("Bound ", param, " to ", i);

          i++;
        }
      std::vector<Row<T...>> rows;
      while (sqlite3_step(statement) == SQLITE_ROW)
        {
          Row<T...> row(this->table_name);
          extract_row_values(row, statement);
          rows.push_back(row);
        }
      return rows;
    }

    const std::string table_name;
};

template <typename T, typename... ColumnTypes>
typename std::enable_if<!std::is_integral<T>::value, SelectQuery<ColumnTypes...>&>::type
operator<<(SelectQuery<ColumnTypes...>& query, const T&)
{
  query.body += T::name;
  return query;
}

template <typename... ColumnTypes>
SelectQuery<ColumnTypes...>& operator<<(SelectQuery<ColumnTypes...>& query, const char* str)
{
  query.body += str;
  return query;
}

template <typename... ColumnTypes>
SelectQuery<ColumnTypes...>& operator<<(SelectQuery<ColumnTypes...>& query, const std::string& str)
{
  query.body += "?";
  actual_add_param(query, str);
  return query;
}

template <typename Integer, typename... ColumnTypes>
typename std::enable_if<std::is_integral<Integer>::value, SelectQuery<ColumnTypes...>&>::type
operator<<(SelectQuery<ColumnTypes...>& query, const Integer& i)
{
  query.body += "?";
  actual_add_param(query, i);
  return query;
}