table.hpp 3.38 KB
Newer Older
1 2
#pragma once

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

5
#include <database/delete_query.hpp>
6 7 8 9
#include <database/row.hpp>

#include <algorithm>
#include <string>
10 11 12 13
#include <set>

using namespace std::string_literals;

louiz’'s avatar
louiz’ committed
14 15 16 17 18 19 20
template <typename T>
std::string ToSQLType(DatabaseEngine& db)
{
  if (std::is_same<T, Id>::value)
    return db.id_column_type();
  else if (std::is_same<typename T::real_type, std::string>::value)
    return "TEXT";
louiz’'s avatar
louiz’ committed
21 22
  else if (std::is_same<typename T::real_type, DateTime>::value)
    return db.datetime_column_type();
louiz’'s avatar
louiz’ committed
23 24 25
  else
    return "INTEGER";
}
26 27

template <typename ColumnType>
louiz’'s avatar
louiz’ committed
28
void add_column_to_table(DatabaseEngine& db, const std::string& table_name)
29 30
{
  const std::string name = ColumnType::name;
louiz’'s avatar
louiz’ committed
31 32 33 34
  std::string query{"ALTER TABLE " + table_name + " ADD " + ColumnType::name + " " + ToSQLType<ColumnType>(db)};
  auto res = db.raw_exec(query);
  if (std::get<0>(res) == false)
    log_error("Error adding column ", name, " to table ", table_name, ": ",  std::get<1>(res));
35
}
36

37 38 39 40 41 42
template <typename ColumnType, decltype(ColumnType::options) = nullptr>
void append_option(std::string& s)
{
  s += " "s + ColumnType::options;
}

43 44
template <typename, typename... Args>
void append_option(Args&& ...)
45 46
{ }

47 48 49 50 51 52 53 54 55 56 57 58 59
template <typename... T>
class Table
{
  static_assert(sizeof...(T) > 0, "Table cannot be empty");
  using ColumnTypes = std::tuple<T...>;

 public:
  using RowType = Row<T...>;

  Table(std::string name):
      name(std::move(name))
  {}

louiz’'s avatar
louiz’ committed
60
  void upgrade(DatabaseEngine& db)
61
  {
louiz’'s avatar
louiz’ committed
62
    const auto existing_columns = db.get_all_columns_from_table(this->name);
63 64 65
    add_column_if_not_exists(db, existing_columns);
  }

louiz’'s avatar
louiz’ committed
66
  void create(DatabaseEngine& db)
67
  {
louiz’'s avatar
louiz’ committed
68 69
    std::string query{"CREATE TABLE IF NOT EXISTS "};
    query += this->name;
louiz’'s avatar
louiz’ committed
70
    query += " (";
louiz’'s avatar
louiz’ committed
71 72 73 74 75 76
    this->add_column_create(db, query);
    query += ")";

    auto result = db.raw_exec(query);
    if (std::get<0>(result) == false)
      log_error("Error executing query: ", std::get<1>(result));
77 78 79 80 81 82 83
  }

  RowType row()
  {
    return {this->name};
  }

84 85 86 87 88 89
  auto del()
  {
    DeleteQuery query(this->name);
    return query;
  }

90 91 92 93 94
  const std::string& get_name() const
  {
    return this->name;
  }

95 96
  const std::string name;

97
 private:
98 99

  template <std::size_t N=0>
100
  typename std::enable_if<N < sizeof...(T), void>::type
louiz’'s avatar
louiz’ committed
101
  add_column_if_not_exists(DatabaseEngine& db, const std::map<std::string, std::string>& existing_columns)
102
  {
103
    using ColumnType = typename std::remove_reference<decltype(std::get<N>(std::declval<ColumnTypes>()))>::type;
louiz’'s avatar
louiz’ committed
104
    if (existing_columns.find(ColumnType::name) == existing_columns.end())
105 106
      add_column_to_table<ColumnType>(db, this->name);
    add_column_if_not_exists<N+1>(db, existing_columns);
107
  }
108 109
  template <std::size_t N=0>
  typename std::enable_if<N == sizeof...(T), void>::type
louiz’'s avatar
louiz’ committed
110
  add_column_if_not_exists(DatabaseEngine&, const std::map<std::string, std::string>&)
111
  {}
112

113
  template <std::size_t N=0>
114 115
  typename std::enable_if<N < sizeof...(T), void>::type
  add_column_create(DatabaseEngine& db, std::string& str)
116
  {
117 118 119 120 121 122 123 124
    using ColumnType = typename std::remove_reference<decltype(std::get<N>(std::declval<ColumnTypes>()))>::type;
    str += ColumnType::name;
    str += " ";
    str += ToSQLType<ColumnType>(db);
    if (N != sizeof...(T) - 1)
      str += ",";

    add_column_create<N+1>(db, str);
125
  }
126 127 128 129
  template <std::size_t N=0>
  typename std::enable_if<N == sizeof...(T), void>::type
  add_column_create(DatabaseEngine&, std::string&)
  { }
130 131

};