Mercurial > hg > orthanc-databases
annotate Framework/MySQL/MySQLStatement.cpp @ 583:ae7375d38607 find-refactoring tip
MySQL: fix ordering
author | Alain Mazy <am@orthanc.team> |
---|---|
date | Mon, 21 Oct 2024 18:19:51 +0200 |
parents | a80775ee5eea |
children |
rev | line source |
---|---|
0 | 1 /** |
2 * Orthanc - A Lightweight, RESTful DICOM Store | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
507
54d518dcd74a
updated copyright, as Orthanc Team now replaces Osimis
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
459
diff
changeset
|
5 * Copyright (C) 2017-2023 Osimis S.A., Belgium |
54d518dcd74a
updated copyright, as Orthanc Team now replaces Osimis
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
459
diff
changeset
|
6 * Copyright (C) 2024-2024 Orthanc Team SRL, Belgium |
459
ecd0b719cff5
update year to 2024
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
389
diff
changeset
|
7 * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium |
0 | 8 * |
9 * This program is free software: you can redistribute it and/or | |
10 * modify it under the terms of the GNU Affero General Public License | |
11 * as published by the Free Software Foundation, either version 3 of | |
12 * the License, or (at your option) any later version. | |
13 * | |
14 * This program is distributed in the hope that it will be useful, but | |
15 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 * Affero General Public License for more details. | |
18 * | |
19 * You should have received a copy of the GNU Affero General Public License | |
20 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
21 **/ | |
22 | |
23 | |
24 #include "MySQLStatement.h" | |
25 | |
26 #include "../Common/BinaryStringValue.h" | |
244
02cd7254c949
separating class InputFileValue from FileValue
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
242
diff
changeset
|
27 #include "../Common/InputFileValue.h" |
0 | 28 #include "../Common/Integer64Value.h" |
29 #include "../Common/NullValue.h" | |
30 #include "../Common/Utf8StringValue.h" | |
31 #include "MySQLResult.h" | |
32 | |
157
275e14f57f1e
replacing deprecated std::auto_ptr by std::unique_ptr
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
152
diff
changeset
|
33 #include <Compatibility.h> // For std::unique_ptr<> |
152 | 34 #include <Logging.h> |
35 #include <OrthancException.h> | |
0 | 36 |
37 #include <list> | |
38 #include <memory> | |
39 | |
40 namespace OrthancDatabases | |
41 { | |
42 class MySQLStatement::ResultField : public boost::noncopyable | |
43 { | |
44 private: | |
186 | 45 IValue* CreateIntegerValue(const MYSQL_BIND& bind) const |
0 | 46 { |
47 if (length_ != buffer_.size()) | |
48 { | |
49 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
50 } | |
51 | |
52 switch (mysqlType_) | |
53 { | |
54 case MYSQL_TYPE_TINY: | |
55 if (bind.is_unsigned) | |
56 { | |
57 return new Integer64Value(*reinterpret_cast<const uint8_t*>(&buffer_[0])); | |
58 } | |
59 else | |
60 { | |
61 return new Integer64Value(*reinterpret_cast<const int8_t*>(&buffer_[0])); | |
62 } | |
63 | |
64 case MYSQL_TYPE_SHORT: | |
65 if (bind.is_unsigned) | |
66 { | |
67 return new Integer64Value(*reinterpret_cast<const uint16_t*>(&buffer_[0])); | |
68 } | |
69 else | |
70 { | |
71 return new Integer64Value(*reinterpret_cast<const int16_t*>(&buffer_[0])); | |
72 } | |
73 | |
74 break; | |
75 | |
76 case MYSQL_TYPE_LONG: | |
77 if (bind.is_unsigned) | |
78 { | |
79 return new Integer64Value(*reinterpret_cast<const uint32_t*>(&buffer_[0])); | |
80 } | |
81 else | |
82 { | |
83 return new Integer64Value(*reinterpret_cast<const int32_t*>(&buffer_[0])); | |
84 } | |
85 | |
86 break; | |
87 | |
88 case MYSQL_TYPE_LONGLONG: | |
89 if (bind.is_unsigned) | |
90 { | |
91 uint64_t value = *reinterpret_cast<const uint64_t*>(&buffer_[0]); | |
92 if (static_cast<uint64_t>(static_cast<int64_t>(value)) != value) | |
93 { | |
94 LOG(WARNING) << "Overflow in a 64 bit integer"; | |
95 } | |
96 | |
97 return new Integer64Value(static_cast<int64_t>(value)); | |
98 } | |
99 else | |
100 { | |
101 return new Integer64Value(*reinterpret_cast<const int64_t*>(&buffer_[0])); | |
102 } | |
103 | |
104 break; | |
105 | |
106 default: | |
107 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
108 } | |
109 } | |
110 | |
111 | |
112 enum enum_field_types mysqlType_; | |
113 ValueType orthancType_; | |
114 std::string buffer_; | |
188 | 115 |
189
b968e7bfa7f9
fix build against mariadb client
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
188
diff
changeset
|
116 #if (MYSQL_VERSION_ID < 80000) || defined(MARIADB_VERSION_ID) |
0 | 117 my_bool isNull_; |
118 my_bool isError_; | |
188 | 119 #else |
120 bool isNull_; | |
121 bool isError_; | |
122 #endif | |
123 | |
0 | 124 unsigned long length_; |
125 | |
126 public: | |
186 | 127 explicit ResultField(const MYSQL_FIELD& field) : |
128 mysqlType_(field.type), | |
129 length_(0) | |
0 | 130 { |
131 // https://dev.mysql.com/doc/refman/8.0/en/c-api-data-structures.html | |
132 // https://dev.mysql.com/doc/refman/8.0/en/mysql-stmt-fetch.html => size of "buffer_" | |
133 switch (field.type) | |
134 { | |
135 case MYSQL_TYPE_TINY: | |
136 orthancType_ = ValueType_Integer64; | |
137 buffer_.resize(1); | |
138 break; | |
139 | |
140 case MYSQL_TYPE_SHORT: | |
141 orthancType_ = ValueType_Integer64; | |
142 buffer_.resize(2); | |
143 break; | |
144 | |
145 case MYSQL_TYPE_LONG: | |
146 orthancType_ = ValueType_Integer64; | |
147 buffer_.resize(4); | |
148 break; | |
149 | |
150 case MYSQL_TYPE_LONGLONG: | |
151 orthancType_ = ValueType_Integer64; | |
152 buffer_.resize(8); | |
153 break; | |
154 | |
581
a80775ee5eea
MySQL: Added support for ExtendedFind
Alain Mazy <am@orthanc.team>
parents:
507
diff
changeset
|
155 case MYSQL_TYPE_NULL: |
a80775ee5eea
MySQL: Added support for ExtendedFind
Alain Mazy <am@orthanc.team>
parents:
507
diff
changeset
|
156 orthancType_ = ValueType_Null; |
a80775ee5eea
MySQL: Added support for ExtendedFind
Alain Mazy <am@orthanc.team>
parents:
507
diff
changeset
|
157 break; |
a80775ee5eea
MySQL: Added support for ExtendedFind
Alain Mazy <am@orthanc.team>
parents:
507
diff
changeset
|
158 |
0 | 159 case MYSQL_TYPE_STRING: |
160 case MYSQL_TYPE_VAR_STRING: | |
161 case MYSQL_TYPE_BLOB: | |
242
b97a537f4613
MySQL: Support of range reads for the storage area
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
162 case MYSQL_TYPE_LONG_BLOB: |
0 | 163 // https://medium.com/@adamhooper/in-mysql-never-use-utf8-use-utf8mb4-11761243e434 |
164 switch (field.charsetnr) | |
165 { | |
166 case 45: // utf8mb4_general_ci | |
167 case 46: // utf8mb4_bin | |
168 case 224: // utf8mb4_unicode_ci => RECOMMENDED collation | |
46
6a574d810b98
Compatibility with MySQL 8.0
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
23
diff
changeset
|
169 case 255: // utf8mb4_0900_ai_ci => necessary for MySQL 8.0 |
0 | 170 // https://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci |
171 orthancType_ = ValueType_Utf8String; | |
172 break; | |
173 | |
174 case 63: | |
175 orthancType_ = ValueType_BinaryString; | |
176 break; | |
177 | |
178 default: | |
179 LOG(ERROR) << "Unsupported MySQL charset: " << field.charsetnr; | |
180 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
181 } | |
182 | |
183 if (field.max_length > 0) | |
184 { | |
185 buffer_.resize(field.max_length); | |
186 } | |
187 | |
188 break; | |
189 | |
190 default: | |
191 LOG(ERROR) << "MYSQL_TYPE not implemented: " << field.type; | |
192 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
193 } | |
194 } | |
195 | |
196 enum enum_field_types GetMysqlType() const | |
197 { | |
198 return mysqlType_; | |
199 } | |
200 | |
201 ValueType GetOrthancType() const | |
202 { | |
203 return orthancType_; | |
204 } | |
205 | |
206 void PrepareBind(MYSQL_BIND& bind) | |
207 { | |
208 memset(&bind, 0, sizeof(bind)); | |
209 | |
9
eff482803d30
fix uninitialized values
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
210 isNull_ = false; |
eff482803d30
fix uninitialized values
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
211 isError_ = false; |
0 | 212 length_ = 0; |
213 | |
214 bind.buffer_length = buffer_.size(); | |
215 bind.buffer_type = mysqlType_; | |
216 bind.is_null = &isNull_; | |
217 bind.length = &length_; | |
218 | |
219 if (buffer_.empty()) | |
220 { | |
221 // Only fetches the actual size of the field (*): | |
222 // mysql_stmt_fetch_column() must be invoked afterward | |
223 bind.buffer = 0; | |
224 } | |
225 else | |
226 { | |
227 bind.buffer = &buffer_[0]; | |
228 bind.error = &isError_; | |
229 } | |
230 } | |
231 | |
232 | |
233 IValue* FetchValue(MySQLDatabase& database, | |
234 MYSQL_STMT& statement, | |
235 MYSQL_BIND& bind, | |
236 unsigned int column) const | |
237 { | |
238 if (isError_) | |
239 { | |
240 throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); | |
241 } | |
242 else if (isNull_) | |
243 { | |
244 return new NullValue; | |
245 } | |
246 else if (orthancType_ == ValueType_Integer64) | |
247 { | |
248 return CreateIntegerValue(bind); | |
249 } | |
250 else if (orthancType_ == ValueType_Utf8String || | |
251 orthancType_ == ValueType_BinaryString) | |
252 { | |
253 std::string tmp; | |
254 tmp.resize(length_); | |
255 | |
256 if (!tmp.empty()) | |
257 { | |
258 if (buffer_.empty()) | |
259 { | |
260 bind.buffer = &tmp[0]; | |
261 bind.buffer_length = tmp.size(); | |
262 | |
263 database.CheckErrorCode(mysql_stmt_fetch_column(&statement, &bind, column, 0)); | |
264 } | |
265 else if (tmp.size() <= buffer_.size()) | |
266 { | |
267 memcpy(&tmp[0], &buffer_[0], length_); | |
268 } | |
269 else | |
270 { | |
271 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
272 } | |
273 } | |
274 | |
275 if (orthancType_ == ValueType_Utf8String) | |
276 { | |
277 return new Utf8StringValue(tmp); | |
278 } | |
279 else | |
280 { | |
281 return new BinaryStringValue(tmp); | |
282 } | |
283 } | |
284 else | |
285 { | |
286 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
287 } | |
288 } | |
289 }; | |
290 | |
291 | |
292 class MySQLStatement::ResultMetadata : public boost::noncopyable | |
293 { | |
294 private: | |
295 MYSQL_RES* metadata_; | |
296 | |
297 public: | |
298 ResultMetadata(MySQLDatabase& db, | |
299 MySQLStatement& statement) : | |
300 metadata_(NULL) | |
301 { | |
302 metadata_ = mysql_stmt_result_metadata(statement.GetObject()); | |
303 } | |
304 | |
305 ~ResultMetadata() | |
306 { | |
307 if (metadata_ != NULL) | |
308 { | |
309 mysql_free_result(metadata_); | |
310 } | |
311 } | |
312 | |
313 bool HasFields() const | |
314 { | |
315 return metadata_ != NULL; | |
316 } | |
317 | |
318 size_t GetFieldsCount() | |
319 { | |
320 if (HasFields()) | |
321 { | |
322 return mysql_num_fields(metadata_); | |
323 } | |
324 else | |
325 { | |
326 return 0; | |
327 } | |
328 } | |
329 | |
330 MYSQL_RES* GetObject() | |
331 { | |
332 return metadata_; | |
333 } | |
334 }; | |
335 | |
336 | |
337 void MySQLStatement::Close() | |
338 { | |
339 for (size_t i = 0; i < result_.size(); i++) | |
340 { | |
341 if (result_[i] != NULL) | |
342 { | |
343 delete result_[i]; | |
344 } | |
345 } | |
346 | |
347 if (statement_ != NULL) | |
348 { | |
349 mysql_stmt_close(statement_); | |
350 statement_ = NULL; | |
351 } | |
352 } | |
353 | |
354 | |
355 MySQLStatement::MySQLStatement(MySQLDatabase& db, | |
356 const Query& query) : | |
357 db_(db), | |
358 statement_(NULL), | |
359 formatter_(Dialect_MySQL) | |
360 { | |
361 std::string sql; | |
362 query.Format(sql, formatter_); | |
363 | |
364 statement_ = mysql_stmt_init(db.GetObject()); | |
365 if (statement_ == NULL) | |
366 { | |
367 db.LogError(); | |
368 throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); | |
369 } | |
370 | |
169
c17f219cec42
replacing VLOG(1) by LOG(TRACE)
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
162
diff
changeset
|
371 LOG(TRACE) << "Preparing MySQL statement: " << sql; |
0 | 372 |
373 db_.CheckErrorCode(mysql_stmt_prepare(statement_, sql.c_str(), sql.size())); | |
374 | |
375 if (mysql_stmt_param_count(statement_) != formatter_.GetParametersCount()) | |
376 { | |
377 Close(); | |
378 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
379 } | |
380 | |
381 try | |
382 { | |
383 ResultMetadata result(db, *this); | |
384 | |
385 if (result.HasFields()) | |
386 { | |
387 MYSQL_FIELD *field; | |
388 while ((field = mysql_fetch_field(result.GetObject()))) | |
389 { | |
390 result_.push_back(new ResultField(*field)); | |
391 } | |
392 } | |
393 | |
394 if (result_.size() != result.GetFieldsCount()) | |
395 { | |
396 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
397 } | |
398 } | |
399 catch (Orthanc::OrthancException&) | |
400 { | |
401 Close(); | |
402 throw; | |
403 } | |
404 | |
405 if (query.IsReadOnly()) | |
406 { | |
407 unsigned long type = (unsigned long) CURSOR_TYPE_READ_ONLY; | |
408 mysql_stmt_attr_set(statement_, STMT_ATTR_CURSOR_TYPE, (void*) &type); | |
409 } | |
410 } | |
411 | |
412 | |
46
6a574d810b98
Compatibility with MySQL 8.0
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
23
diff
changeset
|
413 MySQLStatement::~MySQLStatement() |
6a574d810b98
Compatibility with MySQL 8.0
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
23
diff
changeset
|
414 { |
6a574d810b98
Compatibility with MySQL 8.0
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
23
diff
changeset
|
415 try |
6a574d810b98
Compatibility with MySQL 8.0
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
23
diff
changeset
|
416 { |
6a574d810b98
Compatibility with MySQL 8.0
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
23
diff
changeset
|
417 Close(); |
6a574d810b98
Compatibility with MySQL 8.0
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
23
diff
changeset
|
418 } |
6a574d810b98
Compatibility with MySQL 8.0
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
23
diff
changeset
|
419 catch (Orthanc::OrthancException&) |
6a574d810b98
Compatibility with MySQL 8.0
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
23
diff
changeset
|
420 { |
6a574d810b98
Compatibility with MySQL 8.0
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
23
diff
changeset
|
421 // Ignore possible exceptions due to connection loss |
6a574d810b98
Compatibility with MySQL 8.0
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
23
diff
changeset
|
422 } |
6a574d810b98
Compatibility with MySQL 8.0
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
23
diff
changeset
|
423 } |
6a574d810b98
Compatibility with MySQL 8.0
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
23
diff
changeset
|
424 |
6a574d810b98
Compatibility with MySQL 8.0
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
23
diff
changeset
|
425 |
0 | 426 MYSQL_STMT* MySQLStatement::GetObject() |
427 { | |
428 if (statement_ == NULL) | |
429 { | |
430 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
431 } | |
432 else | |
433 { | |
434 return statement_; | |
435 } | |
436 } | |
437 | |
438 | |
439 IValue* MySQLStatement::FetchResultField(size_t i) | |
440 { | |
441 if (i >= result_.size()) | |
442 { | |
443 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
444 } | |
445 else | |
446 { | |
447 assert(result_[i] != NULL); | |
448 return result_[i]->FetchValue(db_, *statement_, outputs_[i], i); | |
449 } | |
450 } | |
451 | |
452 | |
23
b2ff1cd2907a
handling of implicit transactions in DatabaseManager
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
9
diff
changeset
|
453 IResult* MySQLStatement::Execute(ITransaction& transaction, |
0 | 454 const Dictionary& parameters) |
455 { | |
456 std::list<long long int> int64Parameters; | |
457 | |
458 std::vector<MYSQL_BIND> inputs(formatter_.GetParametersCount()); | |
459 | |
460 for (size_t i = 0; i < inputs.size(); i++) | |
461 { | |
462 memset(&inputs[i], 0, sizeof(MYSQL_BIND)); | |
463 | |
464 const std::string& name = formatter_.GetParameterName(i); | |
465 if (!parameters.HasKey(name)) | |
466 { | |
467 LOG(ERROR) << "Missing required parameter in a SQL query: " << name; | |
468 throw Orthanc::OrthancException(Orthanc::ErrorCode_InexistentItem); | |
469 } | |
470 | |
471 ValueType type = formatter_.GetParameterType(i); | |
472 | |
473 const IValue& value = parameters.GetValue(name); | |
474 if (value.GetType() != type) | |
475 { | |
476 LOG(ERROR) << "Bad type of argument provided to a SQL query: " << name; | |
477 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadParameterType); | |
478 } | |
479 | |
480 // https://dev.mysql.com/doc/refman/8.0/en/c-api-prepared-statement-type-codes.html | |
481 switch (type) | |
482 { | |
483 case ValueType_Integer64: | |
484 { | |
485 int64Parameters.push_back(dynamic_cast<const Integer64Value&>(value).GetValue()); | |
486 inputs[i].buffer = &int64Parameters.back(); | |
487 inputs[i].buffer_type = MYSQL_TYPE_LONGLONG; | |
488 break; | |
489 } | |
490 | |
491 case ValueType_Utf8String: | |
492 { | |
493 const std::string& utf8 = dynamic_cast<const Utf8StringValue&>(value).GetContent(); | |
494 inputs[i].buffer = const_cast<char*>(utf8.c_str()); | |
495 inputs[i].buffer_length = utf8.size(); | |
496 inputs[i].buffer_type = MYSQL_TYPE_STRING; | |
497 break; | |
498 } | |
499 | |
500 case ValueType_BinaryString: | |
501 { | |
502 const std::string& content = dynamic_cast<const BinaryStringValue&>(value).GetContent(); | |
503 inputs[i].buffer = const_cast<char*>(content.c_str()); | |
504 inputs[i].buffer_length = content.size(); | |
505 inputs[i].buffer_type = MYSQL_TYPE_BLOB; | |
506 break; | |
507 } | |
508 | |
244
02cd7254c949
separating class InputFileValue from FileValue
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
242
diff
changeset
|
509 case ValueType_InputFile: |
0 | 510 { |
244
02cd7254c949
separating class InputFileValue from FileValue
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
242
diff
changeset
|
511 const std::string& content = dynamic_cast<const InputFileValue&>(value).GetContent(); |
0 | 512 inputs[i].buffer = const_cast<char*>(content.c_str()); |
513 inputs[i].buffer_length = content.size(); | |
514 inputs[i].buffer_type = MYSQL_TYPE_BLOB; | |
515 break; | |
516 } | |
517 | |
518 case ValueType_Null: | |
519 { | |
520 inputs[i].buffer = NULL; | |
521 inputs[i].buffer_type = MYSQL_TYPE_NULL; | |
522 break; | |
523 } | |
524 | |
525 default: | |
526 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
527 } | |
528 } | |
529 | |
530 if (!inputs.empty()) | |
531 { | |
532 db_.CheckErrorCode(mysql_stmt_bind_param(statement_, &inputs[0])); | |
533 } | |
534 | |
535 db_.CheckErrorCode(mysql_stmt_execute(statement_)); | |
536 | |
537 outputs_.resize(result_.size()); | |
538 | |
539 for (size_t i = 0; i < result_.size(); i++) | |
540 { | |
541 assert(result_[i] != NULL); | |
542 result_[i]->PrepareBind(outputs_[i]); | |
543 } | |
544 | |
545 if (!outputs_.empty()) | |
546 { | |
547 db_.CheckErrorCode(mysql_stmt_bind_result(statement_, &outputs_[0])); | |
548 db_.CheckErrorCode(mysql_stmt_store_result(statement_)); | |
549 } | |
550 | |
551 return new MySQLResult(db_, *this); | |
552 } | |
553 | |
554 | |
23
b2ff1cd2907a
handling of implicit transactions in DatabaseManager
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
9
diff
changeset
|
555 void MySQLStatement::ExecuteWithoutResult(ITransaction& transaction, |
0 | 556 const Dictionary& parameters) |
557 { | |
157
275e14f57f1e
replacing deprecated std::auto_ptr by std::unique_ptr
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
152
diff
changeset
|
558 std::unique_ptr<IResult> dummy(Execute(transaction, parameters)); |
0 | 559 } |
560 } |