select_query.hpp 3.61 KB
Newer Older
1 2
#pragma once

louiz’'s avatar
louiz’ committed
3 4
#include <database/engine.hpp>

5
#include <database/statement.hpp>
louiz’'s avatar
louiz’ committed
6
#include <utils/datetime.hpp>
7 8 9
#include <database/query.hpp>
#include <database/row.hpp>

10 11
#include <utils/optional_bool.hpp>

12 13 14 15 16 17
#include <vector>
#include <string>

using namespace std::string_literals;

template <typename T>
18 19
typename std::enable_if<std::is_integral<T>::value, std::int64_t>::type
extract_row_value(Statement& statement, const int i)
20
{
21 22 23 24 25 26 27 28 29 30 31
  return statement.get_column_int64(i);
}

template <typename T>
typename std::enable_if<std::is_same<std::string, T>::value, T>::type
extract_row_value(Statement& statement, const int i)
{
  return statement.get_column_text(i);
}

template <typename T>
32
typename std::enable_if<std::is_same<OptionalBool, T>::value, T>::type
33 34 35
extract_row_value(Statement& statement, const int i)
{
  const auto integer = statement.get_column_int(i);
36
  OptionalBool result;
37
  if (integer > 0)
38
    result.set_value(true);
39
  else if (integer < 0)
40 41
    result.set_value(false);
  return result;
42 43
}

louiz’'s avatar
louiz’ committed
44 45 46 47 48 49 50 51
template <typename T>
typename std::enable_if<std::is_same<DateTime, T>::value, T>::type
extract_row_value(Statement& statement, const int i)
{
  const std::string timestamp = statement.get_column_text(i);
  return {timestamp};
}

52
template <std::size_t N=0, typename... T>
53 54
typename std::enable_if<N < sizeof...(T), void>::type
extract_row_values(Row<T...>& row, Statement& statement)
55
{
56
  using ColumnType = typename std::remove_reference<decltype(std::get<N>(row.columns))>::type;
57

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

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

64 65 66 67 68
template <std::size_t N=0, typename... T>
typename std::enable_if<N == sizeof...(T), void>::type
extract_row_values(Row<T...>&, Statement&)
{}

louiz’'s avatar
louiz’ committed
69 70 71 72 73 74 75 76 77 78 79 80
template <typename ColumnType>
std::string before_column()
{
  return {};
}

template <typename ColumnType>
std::string after_column()
{
  return {};
}

81 82 83 84 85 86 87
template <typename... T>
struct SelectQuery: public Query
{
    SelectQuery(std::string table_name):
        Query("SELECT"),
        table_name(table_name)
    {
88
      this->insert_col_name();
89 90 91
      this->body += " from " + this->table_name;
    }

92
    template <std::size_t N=0>
93 94
    typename std::enable_if<N < sizeof...(T), void>::type
    insert_col_name()
95
    {
96 97
      using ColumnsType = std::tuple<T...>;
      using ColumnType = typename std::remove_reference<decltype(std::get<N>(std::declval<ColumnsType>()))>::type;
98

louiz’'s avatar
louiz’ committed
99 100
      this->body += " ";
      this->body += before_column<ColumnType>() + ColumnType::name + after_column<ColumnType>();
101

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

105
      this->insert_col_name<N+1>();
106
    }
107 108 109 110
    template <std::size_t N=0>
    typename std::enable_if<N == sizeof...(T), void>::type
    insert_col_name()
    {}
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129

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

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

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

louiz’'s avatar
louiz’ committed
130
    auto execute(DatabaseEngine& db)
131 132
    {
      std::vector<Row<T...>> rows;
louiz’'s avatar
louiz’ committed
133

134 135 136 137
#ifdef DEBUG_SQL_QUERIES
      const auto timer = this->log_and_time();
#endif

louiz’'s avatar
louiz’ committed
138
      auto statement = db.prepare(this->body);
139 140
      if (!statement)
        return rows;
louiz’'s avatar
louiz’ committed
141 142 143
      statement->bind(std::move(this->params));

      while (statement->step() == StepResult::Row)
144 145
        {
          Row<T...> row(this->table_name);
louiz’'s avatar
louiz’ committed
146
          extract_row_values(row, *statement);
147 148
          rows.push_back(row);
        }
louiz’'s avatar
louiz’ committed
149

150 151 152 153 154 155
      return rows;
    }

    const std::string table_name;
};