insert_query.hpp 3.03 KB
Newer Older
1 2
#pragma once

3
#include <database/statement.hpp>
4 5 6 7 8 9 10 11 12
#include <database/column.hpp>
#include <database/query.hpp>
#include <logger/logger.hpp>

#include <type_traits>
#include <vector>
#include <string>
#include <tuple>

louiz’'s avatar
louiz’ committed
13
template <std::size_t N=0, typename... T>
14
void update_autoincrement_id(std::tuple<T...>& columns, Statement& statement)
15
{
16 17 18 19 20 21 22
  if constexpr(N < sizeof...(T))
    {
      using ColumnType = typename std::decay<decltype(std::get<N>(columns))>::type;
      if (std::is_same<ColumnType, Id>::value)
        auto&& column = std::get<Id>(columns);
      update_autoincrement_id<N + 1>(columns, statement);
    }
23 24 25 26
}

struct InsertQuery: public Query
{
louiz’'s avatar
louiz’ committed
27 28 29
  template <typename... T>
  InsertQuery(const std::string& name, const std::tuple<T...>& columns):
      Query("INSERT INTO ")
30 31
  {
    this->body += name;
louiz’'s avatar
louiz’ committed
32 33
    this->insert_col_names(columns);
    this->insert_values(columns);
34 35 36
  }

  template <typename... T>
louiz’'s avatar
louiz’ committed
37
  void execute(DatabaseEngine& db, std::tuple<T...>& columns)
38
  {
39 40 41 42
#ifdef DEBUG_SQL_QUERIES
    const auto timer = this->log_and_time();
#endif

louiz’'s avatar
louiz’ committed
43 44 45 46 47 48 49
    auto statement = db.prepare(this->body);
    this->bind_param(columns, *statement);

    if (statement->step() != StepResult::Error)
      db.extract_last_insert_rowid(*statement);
    else
      log_error("Failed to extract the rowid from the last INSERT");
50 51
  }

52
  template <int N=0, typename... T>
53
  void bind_param(const std::tuple<T...>& columns, Statement& statement, int index=1)
54
  {
55 56 57 58
    if constexpr(N < sizeof...(T))
      {
        auto&& column = std::get<N>(columns);
        using ColumnType = std::decay_t<decltype(column)>;
louiz’'s avatar
louiz’ committed
59

60 61
        if constexpr(!std::is_same<ColumnType, Id>::value)
          actual_bind(statement, column.value, index++);
62

63 64
        this->bind_param<N + 1>(columns, statement, index);
      }
65 66 67 68 69 70
  }

  template <typename... T>
  void insert_values(const std::tuple<T...>& columns)
  {
    this->body += "VALUES (";
71
    this->insert_value(columns);
72 73 74
    this->body += ")";
  }

75
  template <int N=0, typename... T>
76
  void insert_value(const std::tuple<T...>& columns, int index=1)
77
  {
78
    if constexpr(N < sizeof...(T))
louiz’'s avatar
louiz’ committed
79
      {
80 81 82 83 84 85 86 87 88
        using ColumnType = std::decay_t<decltype(std::get<N>(columns))>;

        if (!std::is_same<ColumnType, Id>::value)
          {
            this->body += "$" + std::to_string(index++);
            if (N != sizeof...(T) - 1)
              this->body += ", ";
          }
        this->insert_value<N + 1>(columns, index);
louiz’'s avatar
louiz’ committed
89
      }
90 91 92 93 94 95
  }

  template <typename... T>
  void insert_col_names(const std::tuple<T...>& columns)
  {
    this->body += " (";
96
    this->insert_col_name(columns);
louiz’'s avatar
louiz’ committed
97
    this->body += ")";
98 99
  }

100
  template <int N=0, typename... T>
101
  void insert_col_name(const std::tuple<T...>& columns)
102
  {
103
    if constexpr(N < sizeof...(T))
louiz’'s avatar
louiz’ committed
104
      {
105
        using ColumnType = std::decay_t<decltype(std::get<N>(columns))>;
106

107 108 109
        if (!std::is_same<ColumnType, Id>::value)
          {
            this->body += ColumnType::name;
110

111 112 113
            if (N < (sizeof...(T) - 1))
              this->body += ", ";
          }
louiz’'s avatar
louiz’ committed
114

115 116 117
        this->insert_col_name<N + 1>(columns);
      }
  }
118
};