TrinityCore
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
MySQLPreparedStatement.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
19#include "Errors.h"
20#include "Log.h"
21#include "MySQLHacks.h"
22#include "PreparedStatement.h"
23#include <chrono>
24#include <cstring>
25
26template<typename T>
27struct MySQLType { };
28
29template<> struct MySQLType<uint8> : std::integral_constant<enum_field_types, MYSQL_TYPE_TINY> { };
30template<> struct MySQLType<uint16> : std::integral_constant<enum_field_types, MYSQL_TYPE_SHORT> { };
31template<> struct MySQLType<uint32> : std::integral_constant<enum_field_types, MYSQL_TYPE_LONG> { };
32template<> struct MySQLType<uint64> : std::integral_constant<enum_field_types, MYSQL_TYPE_LONGLONG> { };
33template<> struct MySQLType<int8> : std::integral_constant<enum_field_types, MYSQL_TYPE_TINY> { };
34template<> struct MySQLType<int16> : std::integral_constant<enum_field_types, MYSQL_TYPE_SHORT> { };
35template<> struct MySQLType<int32> : std::integral_constant<enum_field_types, MYSQL_TYPE_LONG> { };
36template<> struct MySQLType<int64> : std::integral_constant<enum_field_types, MYSQL_TYPE_LONGLONG> { };
37template<> struct MySQLType<float> : std::integral_constant<enum_field_types, MYSQL_TYPE_FLOAT> { };
38template<> struct MySQLType<double> : std::integral_constant<enum_field_types, MYSQL_TYPE_DOUBLE> { };
39
41 m_stmt(nullptr), m_Mstmt(stmt), m_bind(nullptr), m_queryString(std::move(queryString))
42{
44 m_paramCount = mysql_stmt_param_count(stmt);
45 m_paramsSet.assign(m_paramCount, false);
47 memset(m_bind, 0, sizeof(MySQLBind) * m_paramCount);
48
50 MySQLBool bool_tmp = MySQLBool(1);
51 mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &bool_tmp);
52}
53
55{
57 if (m_Mstmt->bind_result_done)
58 {
59 delete[] m_Mstmt->bind->length;
60 delete[] m_Mstmt->bind->is_null;
61 }
62 mysql_stmt_close(m_Mstmt);
63 delete[] m_bind;
64}
65
67{
68 m_stmt = stmt; // Cross reference them for debug output
69
70 uint8 pos = 0;
71 for (PreparedStatementData const& data : stmt->GetParameters())
72 {
73 std::visit([&](auto&& param)
74 {
75 SetParameter(pos, param);
76 }, data.data);
77 ++pos;
78 }
79#ifdef _DEBUG
80 if (pos < m_paramCount)
81 TC_LOG_WARN("sql.sql", "[WARNING]: BindParameters() for statement {} did not bind all allocated parameters", stmt->GetIndex());
82#endif
83}
84
86{
87 for (uint32 i=0; i < m_paramCount; ++i)
88 {
89 delete m_bind[i].length;
90 m_bind[i].length = nullptr;
91 delete[] (char*) m_bind[i].buffer;
92 m_bind[i].buffer = nullptr;
93 m_paramsSet[i] = false;
94 }
95}
96
97static bool ParamenterIndexAssertFail(uint32 stmtIndex, uint8 index, uint32 paramCount)
98{
99 TC_LOG_ERROR("sql.driver", "Attempted to bind parameter {}{} on a PreparedStatement {} (statement has only {} parameters)", uint32(index) + 1, (index == 1 ? "st" : (index == 2 ? "nd" : (index == 3 ? "rd" : "nd"))), stmtIndex, paramCount);
100 return false;
101}
102
103//- Bind on mysql level
105{
107
108 if (m_paramsSet[index])
109 TC_LOG_ERROR("sql.sql", "[ERROR] Prepared Statement (id: {}) trying to bind value on already bound index ({}).", m_stmt->GetIndex(), index);
110}
111
113{
114 AssertValidIndex(index);
115 m_paramsSet[index] = true;
116 MYSQL_BIND* param = &m_bind[index];
117 param->buffer_type = MYSQL_TYPE_NULL;
118 delete[] static_cast<char *>(param->buffer);
119 param->buffer = nullptr;
120 param->buffer_length = 0;
121 param->is_null_value = 1;
122 delete param->length;
123 param->length = nullptr;
124}
125
127{
128 SetParameter(index, uint8(value ? 1 : 0));
129}
130
131template<typename T>
133{
134 AssertValidIndex(index);
135 m_paramsSet[index] = true;
136 MYSQL_BIND* param = &m_bind[index];
137 uint32 len = uint32(sizeof(T));
138 param->buffer_type = MySQLType<T>::value;
139 delete[] static_cast<char*>(param->buffer);
140 param->buffer = new char[len];
141 param->buffer_length = 0;
142 param->is_null_value = 0;
143 param->length = nullptr; // Only != NULL for strings
144 param->is_unsigned = std::is_unsigned_v<T>;
145
146 memcpy(param->buffer, &value, len);
147}
148
150{
151 AssertValidIndex(index);
152 m_paramsSet[index] = true;
153 MYSQL_BIND* param = &m_bind[index];
154 uint32 len = sizeof(MYSQL_TIME);
155 param->buffer_type = MYSQL_TYPE_DATETIME;
156 delete[] static_cast<char*>(param->buffer);
157 param->buffer = new char[len];
158 param->buffer_length = len;
159 param->is_null_value = 0;
160 delete param->length;
161 param->length = new unsigned long(len);
162
163 std::chrono::year_month_day ymd(time_point_cast<std::chrono::days>(value));
164 std::chrono::hh_mm_ss hms(duration_cast<std::chrono::microseconds>(value - std::chrono::sys_days(ymd)));
165
166 MYSQL_TIME* time = reinterpret_cast<MYSQL_TIME*>(static_cast<char*>(param->buffer));
167 time->year = static_cast<int32>(ymd.year());
168 time->month = static_cast<uint32>(ymd.month());
169 time->day = static_cast<uint32>(ymd.day());
170 time->hour = hms.hours().count();
171 time->minute = hms.minutes().count();
172 time->second = hms.seconds().count();
173 time->second_part = hms.subseconds().count();
174}
175
176void MySQLPreparedStatement::SetParameter(uint8 index, std::string const& value)
177{
178 AssertValidIndex(index);
179 m_paramsSet[index] = true;
180 MYSQL_BIND* param = &m_bind[index];
181 uint32 len = uint32(value.size());
182 param->buffer_type = MYSQL_TYPE_VAR_STRING;
183 delete [] static_cast<char*>(param->buffer);
184 param->buffer = new char[len];
185 param->buffer_length = len;
186 param->is_null_value = 0;
187 delete param->length;
188 param->length = new unsigned long(len);
189
190 memcpy(param->buffer, value.c_str(), len);
191}
192
193void MySQLPreparedStatement::SetParameter(uint8 index, std::vector<uint8> const& value)
194{
195 AssertValidIndex(index);
196 m_paramsSet[index] = true;
197 MYSQL_BIND* param = &m_bind[index];
198 uint32 len = uint32(value.size());
199 param->buffer_type = MYSQL_TYPE_BLOB;
200 delete [] static_cast<char *>(param->buffer);
201 param->buffer = new char[len];
202 param->buffer_length = len;
203 param->is_null_value = 0;
204 delete param->length;
205 param->length = new unsigned long(len);
206
207 memcpy(param->buffer, value.data(), len);
208}
209
211{
212 std::string queryString(m_queryString);
213
214 size_t pos = 0;
215 for (PreparedStatementData const& data : m_stmt->GetParameters())
216 {
217 pos = queryString.find('?', pos);
218
219 std::string replaceStr = std::visit([&](auto&& data)
220 {
222 }, data.data);
223
224 queryString.replace(pos, 1, replaceStr);
225 pos += replaceStr.length();
226 }
227
228 return queryString;
229}
uint8_t uint8
Definition: Define.h:150
int64_t int64
Definition: Define.h:143
int16_t int16
Definition: Define.h:145
int8_t int8
Definition: Define.h:146
int32_t int32
Definition: Define.h:144
uint64_t uint64
Definition: Define.h:147
uint16_t uint16
Definition: Define.h:149
uint32_t uint32
Definition: Define.h:148
std::chrono::system_clock::time_point SystemTimePoint
Definition: Duration.h:41
#define ASSERT
Definition: Errors.h:68
#define TC_LOG_ERROR(filterType__, message__,...)
Definition: Log.h:189
#define TC_LOG_WARN(filterType__, message__,...)
Definition: Log.h:186
std::remove_pointer_t< decltype(std::declval< MYSQL_BIND >().is_null)> MySQLBool
Definition: MySQLHacks.h:32
static bool ParamenterIndexAssertFail(uint32 stmtIndex, uint8 index, uint32 paramCount)
PreparedStatementBase * m_stmt
void SetParameter(uint8 index, std::nullptr_t)
std::string getQueryString() const
std::string const m_queryString
void BindParameters(PreparedStatementBase *stmt)
std::vector< bool > m_paramsSet
MySQLPreparedStatement(MySQLStmt *stmt, std::string queryString)
std::vector< PreparedStatementData > const & GetParameters() const
STL namespace.
static std::string ToString(T value)