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

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

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

#include <sqlite3.h>

template <int N, typename ColumnType, typename... T>
typename std::enable_if<!std::is_same<std::decay_t<ColumnType>, Id>::value, void>::type
17
actual_bind(Statement& statement, std::vector<std::string>& params, const std::tuple<T...>&)
18 19 20
{
  const auto value = params.front();
  params.erase(params.begin());
21
  if (sqlite3_bind_text(statement.get(), N + 1, value.data(), static_cast<int>(value.size()), SQLITE_TRANSIENT) != SQLITE_OK)
22 23 24 25 26 27 28
    log_error("Failed to bind ", value, " to param ", N);
  else
    log_debug("Bound (not id) [", value, "] to ", N);
}

template <int N, typename ColumnType, typename... T>
typename std::enable_if<std::is_same<std::decay_t<ColumnType>, Id>::value, void>::type
29
actual_bind(Statement& statement, std::vector<std::string>&, const std::tuple<T...>& columns)
30 31 32 33
{
  auto&& column = std::get<Id>(columns);
  if (column.value != 0)
    {
34
      if (sqlite3_bind_int64(statement.get(), N + 1, column.value) != SQLITE_OK)
35 36
        log_error("Failed to bind ", column.value, " to id.");
    }
37
  else if (sqlite3_bind_null(statement.get(), N + 1) != SQLITE_OK)
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
    log_error("Failed to bind NULL to param ", N);
  else
    log_debug("Bound NULL to ", N);
}

struct InsertQuery: public Query
{
  InsertQuery(const std::string& name):
      Query("INSERT OR REPLACE INTO ")
  {
    this->body += name;
  }

  template <typename... T>
  void execute(const std::tuple<T...>& columns, sqlite3* db)
  {
    auto statement = this->prepare(db);
    {
      this->bind_param<0>(columns, statement);
57
      if (sqlite3_step(statement.get()) != SQLITE_DONE)
58 59 60 61 62 63
        log_error("Failed to execute query: ", sqlite3_errmsg(db));
    }
  }

  template <int N, typename... T>
  typename std::enable_if<N < sizeof...(T), void>::type
64
  bind_param(const std::tuple<T...>& columns, Statement& statement)
65 66 67 68 69 70 71 72 73
  {
    using ColumnType = typename std::remove_reference<decltype(std::get<N>(columns))>::type;

    actual_bind<N, ColumnType>(statement, this->params, columns);
    this->bind_param<N+1>(columns, statement);
  }

  template <int N, typename... T>
  typename std::enable_if<N == sizeof...(T), void>::type
74
  bind_param(const std::tuple<T...>&, Statement&)
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
  {}

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

  template <int N, typename... T>
  typename std::enable_if<N < sizeof...(T), void>::type
  insert_value(const std::tuple<T...>& columns)
  {
    this->body += "?";
    if (N != sizeof...(T) - 1)
      this->body += ",";
    this->body += " ";
    add_param(*this, std::get<N>(columns));
    this->insert_value<N+1>(columns);
  }
  template <int N, typename... T>
  typename std::enable_if<N == sizeof...(T), void>::type
  insert_value(const std::tuple<T...>&)
  { }

  template <typename... T>
  void insert_col_names(const std::tuple<T...>& columns)
  {
    this->body += " (";
    this->insert_col_name<0>(columns);
    this->body += ")\n";
  }

  template <int N, typename... T>
  typename std::enable_if<N < sizeof...(T), void>::type
  insert_col_name(const std::tuple<T...>& columns)
  {
    auto value = std::get<N>(columns);

    this->body += value.name;

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

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


 private:
};