~singpolyma/biboumi

biboumi/src/database/insert_query.hpp -rw-r--r-- 3.9 KiB
12223354Stephen Paul Weber Fix caps hash 3 months 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
#pragma once

#include <database/statement.hpp>
#include <database/database.hpp>
#include <database/column.hpp>
#include <database/query.hpp>
#include <database/row.hpp>

#include <logger/logger.hpp>

#include <utils/is_one_of.hpp>

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

template <std::size_t N=0, typename... T>
typename std::enable_if<N < sizeof...(T), void>::type
update_autoincrement_id(std::tuple<T...>& columns, Statement& statement)
{
  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);
}

template <std::size_t N=0, typename... T>
typename std::enable_if<N == sizeof...(T), void>::type
update_autoincrement_id(std::tuple<T...>&, Statement&)
{}

struct InsertQuery: public Query
{
  template <typename... T>
  InsertQuery(const std::string& name, const std::tuple<T...>& columns):
      Query("INSERT INTO ")
  {
    this->body += name;
    this->insert_col_names(columns);
    this->insert_values(columns);
  }

  template <typename... T>
  void execute(DatabaseEngine& db, std::tuple<T...>& columns)
  {
#ifdef DEBUG_SQL_QUERIES
    const auto timer = this->log_and_time();
#endif

    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");
  }

  template <int N=0, typename... T>
  typename std::enable_if<N < sizeof...(T), void>::type
  bind_param(const std::tuple<T...>& columns, Statement& statement, int index=1)
  {
    auto&& column = std::get<N>(columns);
    using ColumnType = std::decay_t<decltype(column)>;

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

    this->bind_param<N+1>(columns, statement, index);
  }

  template <int N=0, typename... T>
  typename std::enable_if<N == sizeof...(T), void>::type
  bind_param(const std::tuple<T...>&, Statement&, int)
  {}

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

  template <int N=0, typename... T>
  typename std::enable_if<N < sizeof...(T), void>::type
  insert_value(const std::tuple<T...>& columns, int index=1)
  {
    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);
  }
  template <int N=0, typename... T>
  typename std::enable_if<N == sizeof...(T), void>::type
  insert_value(const std::tuple<T...>&, const int)
  { }

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

  template <int N=0, typename... T>
  typename std::enable_if<N < sizeof...(T), void>::type
  insert_col_name(const std::tuple<T...>& columns)
  {
    using ColumnType = std::decay_t<decltype(std::get<N>(columns))>;

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

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

    this->insert_col_name<N+1>(columns);
  }

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

template <typename... T>
void insert(Row<T...>& row, DatabaseEngine& db)
{
  InsertQuery query(row.table_name, row.columns);
  // Ugly workaround for non portable stuff
  if (is_one_of<Id, T...>)
    query.body += db.get_returning_id_sql_string(Id::name);
  query.execute(db, row.columns);
}