ffff2dae05638a914a00e3b16ac43a7c7a33527c
[framework/web/wrt-commons.git] / modules / db / src / sql_connection.cpp
1 /*
2  * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16 /*
17  * @file        sql_connection.cpp
18  * @author      Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com)
19  * @version     1.0
20  * @brief       This file is the implementation file of SQL connection
21  */
22 #include <dpl/db/sql_connection.h>
23 #include <dpl/db/naive_synchronization_object.h>
24 #include <dpl/scoped_free.h>
25 #include <dpl/noncopyable.h>
26 #include <dpl/assert.h>
27 #include <db-util.h>
28 #include <unistd.h>
29 #include <cstdio>
30 #include <cstdarg>
31
32 namespace DPL
33 {
34 namespace DB
35 {
36
37 namespace // anonymous
38 {
39 class ScopedNotifyAll
40     : public Noncopyable
41 {
42 private:
43     SqlConnection::SynchronizationObject *m_synchronizationObject;
44
45 public:
46     explicit ScopedNotifyAll(
47         SqlConnection::SynchronizationObject *synchronizationObject)
48         : m_synchronizationObject(synchronizationObject)
49     {
50     }
51
52     ~ScopedNotifyAll()
53     {
54         if (!m_synchronizationObject)
55             return;
56
57         LogPedantic("Notifying after successful synchronize");
58         m_synchronizationObject->NotifyAll();
59     }
60 };
61 } // namespace anonymous
62
63 SqlConnection::DataCommand::DataCommand(SqlConnection *connection,
64                                         const char *buffer)
65     : m_masterConnection(connection),
66       m_stmt(NULL)
67 {
68     Assert(connection != NULL);
69
70     // Notify all after potentially synchronized database connection access
71     ScopedNotifyAll notifyAll(connection->m_synchronizationObject.Get());
72
73     for (;;)
74     {
75         int ret = sqlite3_prepare_v2(connection->m_connection,
76                                      buffer, strlen(buffer),
77                                      &m_stmt, NULL);
78
79         if (ret == SQLITE_OK)
80         {
81             LogPedantic("Data command prepared successfuly");
82             break;
83         }
84         else if (ret == SQLITE_BUSY)
85         {
86             LogPedantic("Collision occurred while preparing SQL command");
87
88             // Synchronize if synchronization object is available
89             if (connection->m_synchronizationObject)
90             {
91                 LogPedantic("Performing synchronization");
92                 connection->m_synchronizationObject->Synchronize();
93                 continue;
94             }
95
96             // No synchronization object defined. Fail.
97         }
98
99         // Fatal error
100         const char *error = sqlite3_errmsg(m_masterConnection->m_connection);
101
102         LogPedantic("SQL prepare data command failed");
103         LogPedantic("    Statement: " << buffer);
104         LogPedantic("    Error: " << error);
105
106         ThrowMsg(Exception::SyntaxError, error);
107     }
108
109     LogPedantic("Prepared data command: " << buffer);
110
111     // Increment stored data command count
112     ++m_masterConnection->m_dataCommandsCount;
113 }
114
115 SqlConnection::DataCommand::~DataCommand()
116 {
117     LogPedantic("SQL data command finalizing");
118
119     if (sqlite3_finalize(m_stmt) != SQLITE_OK)
120         LogPedantic("Failed to finalize data command");
121
122     // Decrement stored data command count
123     --m_masterConnection->m_dataCommandsCount;
124 }
125
126 void SqlConnection::DataCommand::CheckBindResult(int result)
127 {
128     if (result != SQLITE_OK)
129     {
130         const char *error = sqlite3_errmsg(
131             m_masterConnection->m_connection);
132
133         LogPedantic("Failed to bind SQL statement parameter");
134         LogPedantic("    Error: " << error);
135
136         ThrowMsg(Exception::SyntaxError, error);
137     }
138 }
139
140 void SqlConnection::DataCommand::BindNull(
141     SqlConnection::ArgumentIndex position)
142 {
143     CheckBindResult(sqlite3_bind_null(m_stmt, position));
144     LogPedantic("SQL data command bind null: ["
145                 << position << "]");
146 }
147
148 void SqlConnection::DataCommand::BindInteger(
149     SqlConnection::ArgumentIndex position,
150     int value)
151 {
152     CheckBindResult(sqlite3_bind_int(m_stmt, position, value));
153     LogPedantic("SQL data command bind integer: ["
154                 << position << "] -> " << value);
155 }
156
157 void SqlConnection::DataCommand::BindInt8(
158     SqlConnection::ArgumentIndex position,
159     int8_t value)
160 {
161     CheckBindResult(sqlite3_bind_int(m_stmt, position,
162                                      static_cast<int>(value)));
163     LogPedantic("SQL data command bind int8: ["
164                 << position << "] -> " << value);
165 }
166
167 void SqlConnection::DataCommand::BindInt16(
168     SqlConnection::ArgumentIndex position,
169     int16_t value)
170 {
171     CheckBindResult(sqlite3_bind_int(m_stmt, position,
172                                      static_cast<int>(value)));
173     LogPedantic("SQL data command bind int16: ["
174                 << position << "] -> " << value);
175 }
176
177 void SqlConnection::DataCommand::BindInt32(
178     SqlConnection::ArgumentIndex position,
179     int32_t value)
180 {
181     CheckBindResult(sqlite3_bind_int(m_stmt, position,
182                                      static_cast<int>(value)));
183     LogPedantic("SQL data command bind int32: ["
184                 << position << "] -> " << value);
185 }
186
187 void SqlConnection::DataCommand::BindInt64(
188     SqlConnection::ArgumentIndex position,
189     int64_t value)
190 {
191     CheckBindResult(sqlite3_bind_int64(m_stmt, position,
192                                        static_cast<sqlite3_int64>(value)));
193     LogPedantic("SQL data command bind int64: ["
194                 << position << "] -> " << value);
195 }
196
197 void SqlConnection::DataCommand::BindFloat(
198     SqlConnection::ArgumentIndex position,
199     float value)
200 {
201     CheckBindResult(sqlite3_bind_double(m_stmt, position,
202                                         static_cast<double>(value)));
203     LogPedantic("SQL data command bind float: ["
204                 << position << "] -> " << value);
205 }
206
207 void SqlConnection::DataCommand::BindDouble(
208     SqlConnection::ArgumentIndex position,
209     double value)
210 {
211     CheckBindResult(sqlite3_bind_double(m_stmt, position, value));
212     LogPedantic("SQL data command bind double: ["
213                 << position << "] -> " << value);
214 }
215
216 void SqlConnection::DataCommand::BindString(
217     SqlConnection::ArgumentIndex position,
218     const char *value)
219 {
220     if (!value)
221     {
222         BindNull(position);
223         return;
224     }
225
226     // Assume that text may disappear
227     CheckBindResult(sqlite3_bind_text(m_stmt, position,
228                                       value, strlen(value),
229                                       SQLITE_TRANSIENT));
230
231     LogPedantic("SQL data command bind string: ["
232                 << position << "] -> " << value);
233 }
234
235 void SqlConnection::DataCommand::BindString(
236     SqlConnection::ArgumentIndex position,
237     const String &value)
238 {
239     BindString(position, ToUTF8String(value).c_str());
240 }
241
242 void SqlConnection::DataCommand::BindInteger(
243     SqlConnection::ArgumentIndex position,
244     const Optional<int> &value)
245 {
246     if (value.IsNull())
247         BindNull(position);
248     else
249         BindInteger(position, *value);
250 }
251
252 void SqlConnection::DataCommand::BindInt8(
253     SqlConnection::ArgumentIndex position,
254     const Optional<int8_t> &value)
255 {
256     if (value.IsNull())
257         BindNull(position);
258     else
259         BindInt8(position, *value);
260 }
261
262 void SqlConnection::DataCommand::BindInt16(
263     SqlConnection::ArgumentIndex position,
264     const Optional<int16_t> &value)
265 {
266     if (value.IsNull())
267         BindNull(position);
268     else
269         BindInt16(position, *value);
270 }
271
272 void SqlConnection::DataCommand::BindInt32(
273     SqlConnection::ArgumentIndex position,
274     const Optional<int32_t> &value)
275 {
276     if (value.IsNull())
277         BindNull(position);
278     else
279         BindInt32(position, *value);
280 }
281
282 void SqlConnection::DataCommand::BindInt64(
283     SqlConnection::ArgumentIndex position,
284     const Optional<int64_t> &value)
285 {
286     if (value.IsNull())
287         BindNull(position);
288     else
289         BindInt64(position, *value);
290 }
291
292 void SqlConnection::DataCommand::BindFloat(
293     SqlConnection::ArgumentIndex position,
294     const Optional<float> &value)
295 {
296     if (value.IsNull())
297         BindNull(position);
298     else
299         BindFloat(position, *value);
300 }
301
302 void SqlConnection::DataCommand::BindDouble(
303     SqlConnection::ArgumentIndex position,
304     const Optional<double> &value)
305 {
306     if (value.IsNull())
307         BindNull(position);
308     else
309         BindDouble(position, *value);
310 }
311
312 void SqlConnection::DataCommand::BindString(
313     SqlConnection::ArgumentIndex position,
314     const Optional<String> &value)
315 {
316     if (!!value)
317         BindString(position, ToUTF8String(*value).c_str());
318     else
319         BindNull(position);
320 }
321
322 bool SqlConnection::DataCommand::Step()
323 {
324     // Notify all after potentially synchronized database connection access
325     ScopedNotifyAll notifyAll(
326         m_masterConnection->m_synchronizationObject.Get());
327
328     for (;;)
329     {
330         int ret = sqlite3_step(m_stmt);
331
332         if (ret == SQLITE_ROW)
333         {
334             LogPedantic("SQL data command step ROW");
335             return true;
336         }
337         else if (ret == SQLITE_DONE)
338         {
339             LogPedantic("SQL data command step DONE");
340             return false;
341         }
342         else if (ret == SQLITE_BUSY)
343         {
344             LogPedantic("Collision occurred while executing SQL command");
345
346             // Synchronize if synchronization object is available
347             if (m_masterConnection->m_synchronizationObject)
348             {
349                 LogPedantic("Performing synchronization");
350
351                 m_masterConnection->
352                     m_synchronizationObject->Synchronize();
353
354                 continue;
355             }
356
357             // No synchronization object defined. Fail.
358         }
359
360         // Fatal error
361         const char *error = sqlite3_errmsg(m_masterConnection->m_connection);
362
363         LogPedantic("SQL step data command failed");
364         LogPedantic("    Error: " << error);
365
366         ThrowMsg(Exception::InternalError, error);
367     }
368 }
369
370 void SqlConnection::DataCommand::Reset()
371 {
372     /*
373      * According to:
374      * http://www.sqlite.org/c3ref/stmt.html
375      *
376      * if last sqlite3_step command on this stmt returned an error,
377      * then sqlite3_reset will return that error, althought it is not an error.
378      * So sqlite3_reset allways succedes.
379      */
380     sqlite3_reset(m_stmt);
381
382     LogPedantic("SQL data command reset");
383 }
384
385 void SqlConnection::DataCommand::CheckColumnIndex(
386     SqlConnection::ColumnIndex column)
387 {
388     if (column < 0 || column >= sqlite3_column_count(m_stmt))
389         ThrowMsg(Exception::InvalidColumn, "Column index is out of bounds");
390 }
391
392 bool SqlConnection::DataCommand::IsColumnNull(
393     SqlConnection::ColumnIndex column)
394 {
395     LogPedantic("SQL data command get column type: [" << column << "]");
396     CheckColumnIndex(column);
397     return sqlite3_column_type(m_stmt, column) == SQLITE_NULL;
398 }
399
400 int SqlConnection::DataCommand::GetColumnInteger(
401     SqlConnection::ColumnIndex column)
402 {
403     LogPedantic("SQL data command get column integer: [" << column << "]");
404     CheckColumnIndex(column);
405     int value = sqlite3_column_int(m_stmt, column);
406     LogPedantic("    Value: " << value);
407     return value;
408 }
409
410 int8_t SqlConnection::DataCommand::GetColumnInt8(
411     SqlConnection::ColumnIndex column)
412 {
413     LogPedantic("SQL data command get column int8: [" << column << "]");
414     CheckColumnIndex(column);
415     int8_t value = static_cast<int8_t>(sqlite3_column_int(m_stmt, column));
416     LogPedantic("    Value: " << value);
417     return value;
418 }
419
420 int16_t SqlConnection::DataCommand::GetColumnInt16(
421     SqlConnection::ColumnIndex column)
422 {
423     LogPedantic("SQL data command get column int16: [" << column << "]");
424     CheckColumnIndex(column);
425     int16_t value = static_cast<int16_t>(sqlite3_column_int(m_stmt, column));
426     LogPedantic("    Value: " << value);
427     return value;
428 }
429
430 int32_t SqlConnection::DataCommand::GetColumnInt32(
431     SqlConnection::ColumnIndex column)
432 {
433     LogPedantic("SQL data command get column int32: [" << column << "]");
434     CheckColumnIndex(column);
435     int32_t value = static_cast<int32_t>(sqlite3_column_int(m_stmt, column));
436     LogPedantic("    Value: " << value);
437     return value;
438 }
439
440 int64_t SqlConnection::DataCommand::GetColumnInt64(
441     SqlConnection::ColumnIndex column)
442 {
443     LogPedantic("SQL data command get column int64: [" << column << "]");
444     CheckColumnIndex(column);
445     int64_t value = static_cast<int64_t>(sqlite3_column_int64(m_stmt, column));
446     LogPedantic("    Value: " << value);
447     return value;
448 }
449
450 float SqlConnection::DataCommand::GetColumnFloat(
451     SqlConnection::ColumnIndex column)
452 {
453     LogPedantic("SQL data command get column float: [" << column << "]");
454     CheckColumnIndex(column);
455     float value = static_cast<float>(sqlite3_column_double(m_stmt, column));
456     LogPedantic("    Value: " << value);
457     return value;
458 }
459
460 double SqlConnection::DataCommand::GetColumnDouble(
461     SqlConnection::ColumnIndex column)
462 {
463     LogPedantic("SQL data command get column double: [" << column << "]");
464     CheckColumnIndex(column);
465     double value = sqlite3_column_double(m_stmt, column);
466     LogPedantic("    Value: " << value);
467     return value;
468 }
469
470 std::string SqlConnection::DataCommand::GetColumnString(
471     SqlConnection::ColumnIndex column)
472 {
473     LogPedantic("SQL data command get column string: [" << column << "]");
474     CheckColumnIndex(column);
475
476     const char *value = reinterpret_cast<const char *>(
477         sqlite3_column_text(m_stmt, column));
478
479     LogPedantic("Value: " << (value ? value : "NULL"));
480
481     if (value == NULL)
482         return std::string();
483
484     return std::string(value);
485 }
486
487 Optional<int> SqlConnection::DataCommand::GetColumnOptionalInteger(
488     SqlConnection::ColumnIndex column)
489 {
490     LogPedantic("SQL data command get column optional integer: ["
491                 << column << "]");
492     CheckColumnIndex(column);
493     if (sqlite3_column_type(m_stmt, column) == SQLITE_NULL)
494         return Optional<int>::Null;
495     int value = sqlite3_column_int(m_stmt, column);
496     LogPedantic("    Value: " << value);
497     return Optional<int>(value);
498 }
499
500 Optional<int8_t> SqlConnection::DataCommand::GetColumnOptionalInt8(
501     SqlConnection::ColumnIndex column)
502 {
503     LogPedantic("SQL data command get column optional int8: ["
504                 << column << "]");
505     CheckColumnIndex(column);
506     if (sqlite3_column_type(m_stmt, column) == SQLITE_NULL)
507         return Optional<int8_t>::Null;
508     int8_t value = static_cast<int8_t>(sqlite3_column_int(m_stmt, column));
509     LogPedantic("    Value: " << value);
510     return Optional<int8_t>(value);
511 }
512
513 Optional<int16_t> SqlConnection::DataCommand::GetColumnOptionalInt16(
514     SqlConnection::ColumnIndex column)
515 {
516     LogPedantic("SQL data command get column optional int16: ["
517                 << column << "]");
518     CheckColumnIndex(column);
519     if (sqlite3_column_type(m_stmt, column) == SQLITE_NULL)
520         return Optional<int16_t>::Null;
521     int16_t value = static_cast<int16_t>(sqlite3_column_int(m_stmt, column));
522     LogPedantic("    Value: " << value);
523     return Optional<int16_t>(value);
524 }
525
526 Optional<int32_t> SqlConnection::DataCommand::GetColumnOptionalInt32(
527     SqlConnection::ColumnIndex column)
528 {
529     LogPedantic("SQL data command get column optional int32: ["
530                 << column << "]");
531     CheckColumnIndex(column);
532     if (sqlite3_column_type(m_stmt, column) == SQLITE_NULL)
533         return Optional<int32_t>::Null;
534     int32_t value = static_cast<int32_t>(sqlite3_column_int(m_stmt, column));
535     LogPedantic("    Value: " << value);
536     return Optional<int32_t>(value);
537 }
538
539 Optional<int64_t> SqlConnection::DataCommand::GetColumnOptionalInt64(
540     SqlConnection::ColumnIndex column)
541 {
542     LogPedantic("SQL data command get column optional int64: ["
543                 << column << "]");
544     CheckColumnIndex(column);
545     if (sqlite3_column_type(m_stmt, column) == SQLITE_NULL)
546         return Optional<int64_t>::Null;
547     int64_t value = static_cast<int64_t>(sqlite3_column_int64(m_stmt, column));
548     LogPedantic("    Value: " << value);
549     return Optional<int64_t>(value);
550 }
551
552 Optional<float> SqlConnection::DataCommand::GetColumnOptionalFloat(
553     SqlConnection::ColumnIndex column)
554 {
555     LogPedantic("SQL data command get column optional float: ["
556                 << column << "]");
557     CheckColumnIndex(column);
558     if (sqlite3_column_type(m_stmt, column) == SQLITE_NULL)
559         return Optional<float>::Null;
560     float value = static_cast<float>(sqlite3_column_double(m_stmt, column));
561     LogPedantic("    Value: " << value);
562     return Optional<float>(value);
563 }
564
565 Optional<double> SqlConnection::DataCommand::GetColumnOptionalDouble(
566     SqlConnection::ColumnIndex column)
567 {
568     LogPedantic("SQL data command get column optional double: ["
569                 << column << "]");
570     CheckColumnIndex(column);
571     if (sqlite3_column_type(m_stmt, column) == SQLITE_NULL)
572         return Optional<double>::Null;
573     double value = sqlite3_column_double(m_stmt, column);
574     LogPedantic("    Value: " << value);
575     return Optional<double>(value);
576 }
577
578 Optional<String> SqlConnection::DataCommand::GetColumnOptionalString(
579     SqlConnection::ColumnIndex column)
580 {
581     LogPedantic("SQL data command get column optional string: ["
582                 << column << "]");
583     CheckColumnIndex(column);
584     if (sqlite3_column_type(m_stmt, column) == SQLITE_NULL)
585         return Optional<String>::Null;
586     const char *value = reinterpret_cast<const char *>(
587         sqlite3_column_text(m_stmt, column));
588     LogPedantic("Value: " << value);
589     String s = FromUTF8String(value);
590     return Optional<String>(s);
591 }
592
593 void SqlConnection::Connect(const std::string &address,
594     Flag::Type type,
595     Flag::Option flag)
596 {
597     if (m_connection != NULL) {
598         LogPedantic("Already connected.");
599         return;
600     }
601
602     LogPedantic("Connecting to DB: " << address << "...");
603
604     // Connect to database
605     int result;
606
607     if (type & Flag::UseLucene) {
608         result = db_util_open_with_options(
609             address.c_str(),
610             &m_connection,
611             flag,
612             NULL);
613
614         m_usingLucene = true;
615         LogPedantic("Lucene index enabled");
616     } else {
617         result = sqlite3_open_v2(
618             address.c_str(),
619             &m_connection,
620             flag,
621             NULL);
622
623         m_usingLucene = false;
624         LogPedantic("Lucene index disabled");
625     }
626
627     if (result == SQLITE_OK) {
628         LogPedantic("Connected to DB");
629     } else {
630         LogPedantic("Failed to connect to DB!");
631         ThrowMsg(Exception::ConnectionBroken, address);
632     }
633
634     // Enable foreign keys
635     TurnOnForeignKeys();
636 }
637
638 void SqlConnection::Disconnect()
639 {
640     if (m_connection == NULL)
641     {
642         LogPedantic("Already disconnected.");
643         return;
644     }
645
646     LogPedantic("Disconnecting from DB...");
647
648     // All stored data commands must be deleted before disconnect
649     Assert(m_dataCommandsCount == 0 &&
650            "All stored procedures must be deleted"
651            " before disconnecting SqlConnection");
652
653     int result;
654
655     if (m_usingLucene)
656     {
657         result = db_util_close(m_connection);
658     }
659     else
660     {
661         result = sqlite3_close(m_connection);
662     }
663
664     if (result != SQLITE_OK)
665     {
666         const char *error = sqlite3_errmsg(m_connection);
667         LogPedantic("SQL close failed");
668         LogPedantic("    Error: " << error);
669         Throw(Exception::InternalError);
670     }
671
672     m_connection = NULL;
673
674     LogPedantic("Disconnected from DB");
675 }
676
677 bool SqlConnection::CheckTableExist(const char *tableName)
678 {
679     if (m_connection == NULL)
680     {
681         LogPedantic("Cannot execute command. Not connected to DB!");
682         return false;
683     }
684
685     DataCommandAutoPtr command =
686         PrepareDataCommand("select tbl_name from sqlite_master where name=?;");
687
688     command->BindString(1, tableName);
689
690     if (!command->Step())
691     {
692         LogPedantic("No matching records in table");
693         return false;
694     }
695
696     return command->GetColumnString(0) == tableName;
697 }
698
699 SqlConnection::SqlConnection(const std::string &address,
700                              Flag::Type flag,
701                              Flag::Option option,
702                              SynchronizationObject *synchronizationObject)
703     : m_connection(NULL),
704       m_usingLucene(false),
705       m_dataCommandsCount(0),
706       m_synchronizationObject(synchronizationObject)
707 {
708     LogPedantic("Opening database connection to: " << address);
709
710     // Connect to DB
711     SqlConnection::Connect(address, flag, option);
712
713     if (!m_synchronizationObject)
714     {
715         LogPedantic("No synchronization object defined");
716     }
717 }
718
719 SqlConnection::~SqlConnection()
720 {
721     LogPedantic("Closing database connection");
722
723     // Disconnect from DB
724     Try
725     {
726         SqlConnection::Disconnect();
727     }
728     Catch (Exception::Base)
729     {
730         LogPedantic("Failed to disconnect from database");
731     }
732 }
733
734 void SqlConnection::ExecCommand(const char *format, ...)
735 {
736     if (m_connection == NULL)
737     {
738         LogPedantic("Cannot execute command. Not connected to DB!");
739         return;
740     }
741
742     char *rawBuffer;
743
744     va_list args;
745     va_start(args, format);
746
747     if (vasprintf(&rawBuffer, format, args) == -1)
748         rawBuffer = NULL;
749
750     va_end(args);
751
752     ScopedFree<char> buffer(rawBuffer);
753
754     if (!buffer)
755     {
756         LogPedantic("Failed to allocate statement string");
757         return;
758     }
759
760     LogPedantic("Executing SQL command: " << buffer.Get());
761
762     // Notify all after potentially synchronized database connection access
763     ScopedNotifyAll notifyAll(m_synchronizationObject.Get());
764
765     for (;;)
766     {
767         char *errorBuffer;
768
769         int ret = sqlite3_exec(m_connection,
770                                buffer.Get(),
771                                NULL,
772                                NULL,
773                                &errorBuffer);
774
775         std::string errorMsg;
776
777         // Take allocated error buffer
778         if (errorBuffer != NULL)
779         {
780             errorMsg = errorBuffer;
781             sqlite3_free(errorBuffer);
782         }
783
784         if (ret == SQLITE_OK)
785             return;
786
787         if (ret == SQLITE_BUSY)
788         {
789             LogPedantic("Collision occurred while executing SQL command");
790
791             // Synchronize if synchronization object is available
792             if (m_synchronizationObject)
793             {
794                 LogPedantic("Performing synchronization");
795                 m_synchronizationObject->Synchronize();
796                 continue;
797             }
798
799             // No synchronization object defined. Fail.
800         }
801
802         // Fatal error
803         LogPedantic("Failed to execute SQL command. Error: " << errorMsg);
804         ThrowMsg(Exception::SyntaxError, errorMsg);
805     }
806 }
807
808 SqlConnection::DataCommandAutoPtr SqlConnection::PrepareDataCommand(
809     const char *format,
810     ...)
811 {
812     if (m_connection == NULL)
813     {
814         LogPedantic("Cannot execute data command. Not connected to DB!");
815         return DataCommandAutoPtr();
816     }
817
818     char *rawBuffer;
819
820     va_list args;
821     va_start(args, format);
822
823     if (vasprintf(&rawBuffer, format, args) == -1)
824         rawBuffer = NULL;
825
826     va_end(args);
827
828     ScopedFree<char> buffer(rawBuffer);
829
830     if (!buffer)
831     {
832         LogPedantic("Failed to allocate statement string");
833         return DataCommandAutoPtr();
834     }
835
836     LogPedantic("Executing SQL data command: " << buffer.Get());
837
838     return DataCommandAutoPtr(new DataCommand(this, buffer.Get()));
839 }
840
841 SqlConnection::RowID SqlConnection::GetLastInsertRowID() const
842 {
843     return static_cast<RowID>(sqlite3_last_insert_rowid(m_connection));
844 }
845
846 void SqlConnection::TurnOnForeignKeys()
847 {
848     ExecCommand("PRAGMA foreign_keys = ON;");
849 }
850
851 SqlConnection::SynchronizationObject *
852     SqlConnection::AllocDefaultSynchronizationObject()
853 {
854     return new NaiveSynchronizationObject();
855 }
856
857 } // namespace DB
858 } // namespace DPL