1 /*
2  *  Copyright (C) 2005-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "Database.h"
10 #include "settings/AdvancedSettings.h"
11 #include "filesystem/SpecialProtocol.h"
12 #include "profiles/ProfileManager.h"
13 #include "settings/SettingsComponent.h"
14 #include "utils/log.h"
15 #include "utils/SortUtils.h"
16 #include "utils/StringUtils.h"
17 #include "sqlitedataset.h"
18 #include "DatabaseManager.h"
19 #include "DbUrl.h"
20 #include "ServiceBroker.h"
21 
22 #if defined(HAS_MYSQL) || defined(HAS_MARIADB)
23 #include "mysqldataset.h"
24 #endif
25 
26 #ifdef TARGET_POSIX
27 #include "platform/posix/ConvUtils.h"
28 #endif
29 
30 using namespace dbiplus;
31 
32 #define MAX_COMPRESS_COUNT 20
33 
AppendField(const std::string & strField)34 void CDatabase::Filter::AppendField(const std::string &strField)
35 {
36   if (strField.empty())
37     return;
38 
39   if (fields.empty() || fields == "*")
40     fields = strField;
41   else
42     fields += ", " + strField;
43 }
44 
AppendJoin(const std::string & strJoin)45 void CDatabase::Filter::AppendJoin(const std::string &strJoin)
46 {
47   if (strJoin.empty())
48     return;
49 
50   if (join.empty())
51     join = strJoin;
52   else
53     join += " " + strJoin;
54 }
55 
AppendWhere(const std::string & strWhere,bool combineWithAnd)56 void CDatabase::Filter::AppendWhere(const std::string &strWhere, bool combineWithAnd /* = true */)
57 {
58   if (strWhere.empty())
59     return;
60 
61   if (where.empty())
62     where = strWhere;
63   else
64   {
65     where = "(" + where + ") ";
66     where += combineWithAnd ? "AND" : "OR";
67     where += " (" + strWhere + ")";
68   }
69 }
70 
AppendOrder(const std::string & strOrder)71 void CDatabase::Filter::AppendOrder(const std::string &strOrder)
72 {
73   if (strOrder.empty())
74     return;
75 
76   if (order.empty())
77     order = strOrder;
78   else
79     order += ", " + strOrder;
80 }
81 
AppendGroup(const std::string & strGroup)82 void CDatabase::Filter::AppendGroup(const std::string &strGroup)
83 {
84   if (strGroup.empty())
85     return;
86 
87   if (group.empty())
88     group = strGroup;
89   else
90     group += ", " + strGroup;
91 }
92 
AppendJoin(const std::string & strJoin)93 void CDatabase::ExistsSubQuery::AppendJoin(const std::string &strJoin)
94 {
95   if (strJoin.empty())
96     return;
97 
98   if (join.empty())
99     join = strJoin;
100   else
101     join += " " + strJoin;
102 }
103 
AppendWhere(const std::string & strWhere,bool combineWithAnd)104 void CDatabase::ExistsSubQuery::AppendWhere(const std::string &strWhere, bool combineWithAnd /* = true */)
105 {
106   if (strWhere.empty())
107     return;
108 
109   if (where.empty())
110     where = strWhere;
111   else
112   {
113     where += combineWithAnd ? " AND " : " OR ";
114     where += strWhere;
115   }
116 }
117 
BuildSQL(std::string & strSQL)118 bool CDatabase::ExistsSubQuery::BuildSQL(std::string & strSQL)
119 {
120   if (tablename.empty())
121     return false;
122   strSQL = "EXISTS (SELECT 1 FROM " + tablename;
123   if (!join.empty())
124     strSQL += " " + join;
125   std::string strWhere;
126   if (!param.empty())
127     strWhere = param;
128   if (!where.empty())
129   {
130     if (!strWhere.empty())
131       strWhere += " AND ";
132     strWhere += where;
133   }
134   if (!strWhere.empty())
135     strSQL += " WHERE " + strWhere;
136 
137   strSQL += ")";
138   return true;
139 }
140 
DatasetLayout(size_t totalfields)141 CDatabase::DatasetLayout::DatasetLayout(size_t totalfields)
142 {
143   m_fields.resize(totalfields, DatasetFieldInfo(false, false, -1));
144 }
145 
SetField(int fieldNo,const std::string & strField,bool bOutput)146 void CDatabase::DatasetLayout::SetField(int fieldNo, const std::string &strField, bool bOutput /*= false*/)
147 {
148   if (fieldNo >= 0 && fieldNo < static_cast<int>(m_fields.size()))
149   {
150     m_fields[fieldNo].strField = strField;
151     m_fields[fieldNo].fetch = true;
152     m_fields[fieldNo].output = bOutput;
153   }
154 }
155 
AdjustRecordNumbers(int offset)156 void CDatabase::DatasetLayout::AdjustRecordNumbers(int offset)
157 {
158   int recno = 0;
159   for (auto& field : m_fields)
160   {
161     if (field.fetch)
162     {
163       field.recno = recno + offset;
164       ++recno;
165     }
166   }
167 }
168 
GetFetch(int fieldno)169 bool CDatabase::DatasetLayout::GetFetch(int fieldno)
170 {
171   if (fieldno >= 0 && fieldno < static_cast<int>(m_fields.size()))
172     return m_fields[fieldno].fetch;
173   return false;
174 }
175 
SetFetch(int fieldno,bool bFetch)176 void CDatabase::DatasetLayout::SetFetch(int fieldno, bool bFetch /*= true*/)
177 {
178   if (fieldno >= 0 && fieldno < static_cast<int>(m_fields.size()))
179     m_fields[fieldno].fetch = bFetch;
180 }
181 
GetOutput(int fieldno)182 bool CDatabase::DatasetLayout::GetOutput(int fieldno)
183 {
184   if (fieldno >= 0 && fieldno < static_cast<int>(m_fields.size()))
185     return m_fields[fieldno].output;
186   return false;
187 }
188 
GetRecNo(int fieldno)189 int CDatabase::DatasetLayout::GetRecNo(int fieldno)
190 {
191   if (fieldno >= 0 && fieldno < static_cast<int>(m_fields.size()))
192     return m_fields[fieldno].recno;
193   return -1;
194 }
195 
GetFields()196 const std::string CDatabase::DatasetLayout::GetFields()
197 {
198   std::string strSQL;
199   for (const auto& field : m_fields)
200   {
201     if (!field.strField.empty() && field.fetch)
202     {
203       if (strSQL.empty())
204         strSQL = field.strField;
205       else
206         strSQL += ", " + field.strField;
207     }
208   }
209 
210   return strSQL;
211 }
212 
HasFilterFields()213 bool CDatabase::DatasetLayout::HasFilterFields()
214 {
215   for (const auto& field : m_fields)
216   {
217     if (field.fetch)
218       return true;
219   }
220   return false;
221 }
222 
CDatabase()223 CDatabase::CDatabase() :
224   m_profileManager(*CServiceBroker::GetSettingsComponent()->GetProfileManager())
225 {
226   m_openCount = 0;
227   m_sqlite = true;
228   m_multipleExecute = false;
229 }
230 
~CDatabase(void)231 CDatabase::~CDatabase(void)
232 {
233   Close();
234 }
235 
Split(const std::string & strFileNameAndPath,std::string & strPath,std::string & strFileName)236 void CDatabase::Split(const std::string& strFileNameAndPath, std::string& strPath, std::string& strFileName)
237 {
238   strFileName = "";
239   strPath = "";
240   int i = strFileNameAndPath.size() - 1;
241   while (i > 0)
242   {
243     char ch = strFileNameAndPath[i];
244     if (ch == ':' || ch == '/' || ch == '\\') break;
245     else i--;
246   }
247   strPath = strFileNameAndPath.substr(0, i);
248   strFileName = strFileNameAndPath.substr(i);
249 }
250 
PrepareSQL(std::string strStmt,...) const251 std::string CDatabase::PrepareSQL(std::string strStmt, ...) const
252 {
253   std::string strResult = "";
254 
255   if (nullptr != m_pDB)
256   {
257     va_list args;
258     va_start(args, strStmt);
259     strResult = m_pDB->vprepare(strStmt.c_str(), args);
260     va_end(args);
261   }
262 
263   return strResult;
264 }
265 
GetSingleValue(const std::string & query,std::unique_ptr<Dataset> & ds)266 std::string CDatabase::GetSingleValue(const std::string &query, std::unique_ptr<Dataset> &ds)
267 {
268   std::string ret;
269   try
270   {
271     if (!m_pDB || !ds)
272       return ret;
273 
274     if (ds->query(query) && ds->num_rows() > 0)
275       ret = ds->fv(0).get_asString();
276 
277     ds->close();
278   }
279   catch(...)
280   {
281     CLog::Log(LOGERROR, "%s - failed on query '%s'", __FUNCTION__, query.c_str());
282   }
283   return ret;
284 }
285 
GetSingleValue(const std::string & strTable,const std::string & strColumn,const std::string & strWhereClause,const std::string & strOrderBy)286 std::string CDatabase::GetSingleValue(const std::string &strTable, const std::string &strColumn, const std::string &strWhereClause /* = std::string() */, const std::string &strOrderBy /* = std::string() */)
287 {
288   std::string query = PrepareSQL("SELECT %s FROM %s", strColumn.c_str(), strTable.c_str());
289   if (!strWhereClause.empty())
290     query += " WHERE " + strWhereClause;
291   if (!strOrderBy.empty())
292     query += " ORDER BY " + strOrderBy;
293   query += " LIMIT 1";
294   return GetSingleValue(query, m_pDS);
295 }
296 
GetSingleValue(const std::string & query)297 std::string CDatabase::GetSingleValue(const std::string &query)
298 {
299   return GetSingleValue(query, m_pDS);
300 }
301 
GetSingleValueInt(const std::string & query,std::unique_ptr<Dataset> & ds)302 int CDatabase::GetSingleValueInt(const std::string& query, std::unique_ptr<Dataset>& ds)
303 {
304   int ret = 0;
305   try
306   {
307     if (!m_pDB || !ds)
308       return ret;
309 
310     if (ds->query(query) && ds->num_rows() > 0)
311       ret = ds->fv(0).get_asInt();
312 
313     ds->close();
314   }
315   catch (...)
316   {
317     CLog::Log(LOGERROR, "%s - failed on query '%s'", __FUNCTION__, query.c_str());
318   }
319   return ret;
320 }
321 
GetSingleValueInt(const std::string & strTable,const std::string & strColumn,const std::string & strWhereClause,const std::string & strOrderBy)322 int CDatabase::GetSingleValueInt(const std::string& strTable,
323                                  const std::string& strColumn,
324                                  const std::string& strWhereClause /* = std::string() */,
325                                  const std::string& strOrderBy /* = std::string() */)
326 {
327   std::string strResult = GetSingleValue(strTable, strColumn, strWhereClause, strOrderBy);
328   return static_cast<int>(strtol(strResult.c_str(), NULL, 10));
329 }
330 
GetSingleValueInt(const std::string & query)331 int CDatabase::GetSingleValueInt(const std::string& query)
332 {
333   return GetSingleValueInt(query, m_pDS);
334 }
335 
DeleteValues(const std::string & strTable,const Filter & filter)336 bool CDatabase::DeleteValues(const std::string &strTable, const Filter &filter /* = Filter() */)
337 {
338   std::string strQuery;
339   BuildSQL(PrepareSQL("DELETE FROM %s ", strTable.c_str()), filter, strQuery);
340   return ExecuteQuery(strQuery);
341 }
342 
BeginMultipleExecute()343 bool CDatabase::BeginMultipleExecute()
344 {
345   m_multipleExecute = true;
346   m_multipleQueries.clear();
347   return true;
348 }
349 
CommitMultipleExecute()350 bool CDatabase::CommitMultipleExecute()
351 {
352   m_multipleExecute = false;
353   BeginTransaction();
354   for (const auto& i : m_multipleQueries)
355   {
356     if (!ExecuteQuery(i))
357     {
358       RollbackTransaction();
359       return false;
360     }
361   }
362   m_multipleQueries.clear();
363   return CommitTransaction();
364 }
365 
ExecuteQuery(const std::string & strQuery)366 bool CDatabase::ExecuteQuery(const std::string &strQuery)
367 {
368   if (m_multipleExecute)
369   {
370     m_multipleQueries.push_back(strQuery);
371     return true;
372   }
373 
374   bool bReturn = false;
375 
376   try
377   {
378     if (nullptr == m_pDB)
379       return bReturn;
380     if (nullptr == m_pDS)
381       return bReturn;
382     m_pDS->exec(strQuery);
383     bReturn = true;
384   }
385   catch (...)
386   {
387     CLog::Log(LOGERROR, "%s - failed to execute query '%s'",
388         __FUNCTION__, strQuery.c_str());
389   }
390 
391   return bReturn;
392 }
393 
ResultQuery(const std::string & strQuery)394 bool CDatabase::ResultQuery(const std::string &strQuery)
395 {
396   bool bReturn = false;
397 
398   try
399   {
400     if (nullptr == m_pDB)
401       return bReturn;
402     if (nullptr == m_pDS)
403       return bReturn;
404 
405     std::string strPreparedQuery = PrepareSQL(strQuery.c_str());
406 
407     bReturn = m_pDS->query(strPreparedQuery);
408   }
409   catch (...)
410   {
411     CLog::Log(LOGERROR, "%s - failed to execute query '%s'",
412         __FUNCTION__, strQuery.c_str());
413   }
414 
415   return bReturn;
416 }
417 
QueueInsertQuery(const std::string & strQuery)418 bool CDatabase::QueueInsertQuery(const std::string &strQuery)
419 {
420   if (strQuery.empty())
421     return false;
422 
423   if (!m_bMultiInsert)
424   {
425     if (nullptr == m_pDB)
426       return false;
427     if (nullptr == m_pDS2)
428       return false;
429 
430     m_bMultiInsert = true;
431     m_pDS2->insert();
432   }
433 
434   m_pDS2->add_insert_sql(strQuery);
435 
436   return true;
437 }
438 
CommitInsertQueries()439 bool CDatabase::CommitInsertQueries()
440 {
441   bool bReturn = true;
442 
443   if (m_bMultiInsert)
444   {
445     try
446     {
447       m_bMultiInsert = false;
448       m_pDS2->post();
449       m_pDS2->clear_insert_sql();
450     }
451     catch(...)
452     {
453       bReturn = false;
454       CLog::Log(LOGERROR, "%s - failed to execute queries",
455           __FUNCTION__);
456     }
457   }
458 
459   return bReturn;
460 }
461 
GetInsertQueriesCount()462 size_t CDatabase::GetInsertQueriesCount()
463 {
464   return m_pDS2->insert_sql_count();
465 }
466 
QueueDeleteQuery(const std::string & strQuery)467 bool CDatabase::QueueDeleteQuery(const std::string& strQuery)
468 {
469   if (strQuery.empty() || !m_pDB || !m_pDS)
470     return false;
471 
472   m_bMultiDelete = true;
473   m_pDS->del();
474   m_pDS->add_delete_sql(strQuery);
475   return true;
476 }
477 
CommitDeleteQueries()478 bool CDatabase::CommitDeleteQueries()
479 {
480   bool bReturn = true;
481 
482   if (m_bMultiDelete)
483   {
484     try
485     {
486       m_bMultiDelete = false;
487       m_pDS->deletion();
488       m_pDS->clear_delete_sql();
489     }
490     catch (...)
491     {
492       bReturn = false;
493       CLog::Log(LOGERROR, "%s - failed to execute queries", __FUNCTION__);
494     }
495   }
496 
497   return bReturn;
498 }
499 
GetDeleteQueriesCount()500 size_t CDatabase::GetDeleteQueriesCount()
501 {
502   return m_pDS->delete_sql_count();
503 }
504 
Open()505 bool CDatabase::Open()
506 {
507   DatabaseSettings db_fallback;
508   return Open(db_fallback);
509 }
510 
Open(const DatabaseSettings & settings)511 bool CDatabase::Open(const DatabaseSettings &settings)
512 {
513   if (IsOpen())
514   {
515     m_openCount++;
516     return true;
517   }
518 
519   // check our database manager to see if this database can be opened
520   if (!CServiceBroker::GetDatabaseManager().CanOpen(GetBaseDBName()))
521     return false;
522 
523   DatabaseSettings dbSettings = settings;
524   InitSettings(dbSettings);
525 
526   std::string dbName = dbSettings.name;
527   dbName += StringUtils::Format("%d", GetSchemaVersion());
528   return Connect(dbName, dbSettings, false);
529 }
530 
InitSettings(DatabaseSettings & dbSettings)531 void CDatabase::InitSettings(DatabaseSettings &dbSettings)
532 {
533   m_sqlite = true;
534 
535 #if defined(HAS_MYSQL) || defined(HAS_MARIADB)
536   if (dbSettings.type == "mysql")
537   {
538     // check we have all information before we cancel the fallback
539     if ( ! (dbSettings.host.empty() ||
540             dbSettings.user.empty() || dbSettings.pass.empty()) )
541       m_sqlite = false;
542     else
543       CLog::Log(LOGINFO, "Essential mysql database information is missing. Require at least host, user and pass defined.");
544   }
545   else
546 #else
547   if (dbSettings.type == "mysql")
548     CLog::Log(LOGERROR, "MySQL library requested but MySQL support is not compiled in. Falling back to sqlite3.");
549 #endif
550   {
551     dbSettings.type = "sqlite3";
552     if (dbSettings.host.empty())
553       dbSettings.host = CSpecialProtocol::TranslatePath(m_profileManager.GetDatabaseFolder());
554   }
555 
556   // use separate, versioned database
557   if (dbSettings.name.empty())
558     dbSettings.name = GetBaseDBName();
559 }
560 
CopyDB(const std::string & latestDb)561 void CDatabase::CopyDB(const std::string& latestDb)
562 {
563   m_pDB->copy(latestDb.c_str());
564 }
565 
DropAnalytics()566 void CDatabase::DropAnalytics()
567 {
568   m_pDB->drop_analytics();
569 }
570 
Connect(const std::string & dbName,const DatabaseSettings & dbSettings,bool create)571 bool CDatabase::Connect(const std::string &dbName, const DatabaseSettings &dbSettings, bool create)
572 {
573   // create the appropriate database structure
574   if (dbSettings.type == "sqlite3")
575   {
576     m_pDB.reset( new SqliteDatabase() ) ;
577   }
578 #if defined(HAS_MYSQL) || defined(HAS_MARIADB)
579   else if (dbSettings.type == "mysql")
580   {
581     m_pDB.reset( new MysqlDatabase() ) ;
582   }
583 #endif
584   else
585   {
586     CLog::Log(LOGERROR, "Unable to determine database type: %s", dbSettings.type.c_str());
587     return false;
588   }
589 
590   // host name is always required
591   m_pDB->setHostName(dbSettings.host.c_str());
592 
593   if (!dbSettings.port.empty())
594     m_pDB->setPort(dbSettings.port.c_str());
595 
596   if (!dbSettings.user.empty())
597     m_pDB->setLogin(dbSettings.user.c_str());
598 
599   if (!dbSettings.pass.empty())
600     m_pDB->setPasswd(dbSettings.pass.c_str());
601 
602   // database name is always required
603   m_pDB->setDatabase(dbName.c_str());
604 
605   // set configuration regardless if any are empty
606   m_pDB->setConfig(dbSettings.key.c_str(),
607                    dbSettings.cert.c_str(),
608                    dbSettings.ca.c_str(),
609                    dbSettings.capath.c_str(),
610                    dbSettings.ciphers.c_str(),
611                    dbSettings.compression);
612 
613   // create the datasets
614   m_pDS.reset(m_pDB->CreateDataset());
615   m_pDS2.reset(m_pDB->CreateDataset());
616 
617   if (m_pDB->connect(create) != DB_CONNECTION_OK)
618     return false;
619 
620   try
621   {
622     // test if db already exists, if not we need to create the tables
623     if (!m_pDB->exists() && create)
624     {
625       if (dbSettings.type == "sqlite3")
626       {
627         //  Modern file systems have a cluster/block size of 4k.
628         //  To gain better performance when performing write
629         //  operations to the database, set the page size of the
630         //  database file to 4k.
631         //  This needs to be done before any table is created.
632         m_pDS->exec("PRAGMA page_size=4096\n");
633 
634         //  Also set the memory cache size to 16k
635         m_pDS->exec("PRAGMA default_cache_size=4096\n");
636       }
637       CreateDatabase();
638     }
639 
640     // sqlite3 post connection operations
641     if (dbSettings.type == "sqlite3")
642     {
643       m_pDS->exec("PRAGMA cache_size=4096\n");
644       m_pDS->exec("PRAGMA synchronous='NORMAL'\n");
645       m_pDS->exec("PRAGMA count_changes='OFF'\n");
646     }
647   }
648   catch (DbErrors &error)
649   {
650     CLog::Log(LOGERROR, "%s failed with '%s'", __FUNCTION__, error.getMsg());
651     m_openCount = 1; // set to open so we can execute Close()
652     Close();
653     return false;
654   }
655 
656   m_openCount = 1; // our database is open
657   return true;
658 }
659 
GetDBVersion()660 int CDatabase::GetDBVersion()
661 {
662   m_pDS->query("SELECT idVersion FROM version\n");
663   if (m_pDS->num_rows() > 0)
664     return m_pDS->fv("idVersion").get_asInt();
665   return 0;
666 }
667 
IsOpen()668 bool CDatabase::IsOpen()
669 {
670   return m_openCount > 0;
671 }
672 
Close()673 void CDatabase::Close()
674 {
675   if (m_openCount == 0)
676     return;
677 
678   if (m_openCount > 1)
679   {
680     m_openCount--;
681     return;
682   }
683 
684   m_openCount = 0;
685   m_multipleExecute = false;
686 
687   if (nullptr == m_pDB)
688     return;
689   if (nullptr != m_pDS)
690     m_pDS->close();
691   m_pDB->disconnect();
692   m_pDB.reset();
693   m_pDS.reset();
694   m_pDS2.reset();
695 }
696 
Compress(bool bForce)697 bool CDatabase::Compress(bool bForce /* =true */)
698 {
699   if (!m_sqlite)
700     return true;
701 
702   try
703   {
704     if (nullptr == m_pDB)
705       return false;
706     if (nullptr == m_pDS)
707       return false;
708     if (!bForce)
709     {
710       m_pDS->query("select iCompressCount from version");
711       if (!m_pDS->eof())
712       {
713         int iCount = m_pDS->fv(0).get_asInt();
714         if (iCount > MAX_COMPRESS_COUNT)
715           iCount = -1;
716         m_pDS->close();
717         std::string strSQL=PrepareSQL("update version set iCompressCount=%i\n",++iCount);
718         m_pDS->exec(strSQL);
719         if (iCount != 0)
720           return true;
721       }
722     }
723 
724     if (!m_pDS->exec("vacuum\n"))
725       return false;
726   }
727   catch (...)
728   {
729     CLog::Log(LOGERROR, "%s - Compressing the database failed", __FUNCTION__);
730     return false;
731   }
732   return true;
733 }
734 
Interrupt()735 void CDatabase::Interrupt()
736 {
737   m_pDS->interrupt();
738 }
739 
BeginTransaction()740 void CDatabase::BeginTransaction()
741 {
742   try
743   {
744     if (nullptr != m_pDB)
745       m_pDB->start_transaction();
746   }
747   catch (...)
748   {
749     CLog::Log(LOGERROR, "database:begintransaction failed");
750   }
751 }
752 
CommitTransaction()753 bool CDatabase::CommitTransaction()
754 {
755   try
756   {
757     if (nullptr != m_pDB)
758       m_pDB->commit_transaction();
759   }
760   catch (...)
761   {
762     CLog::Log(LOGERROR, "database:committransaction failed");
763     return false;
764   }
765   return true;
766 }
767 
RollbackTransaction()768 void CDatabase::RollbackTransaction()
769 {
770   try
771   {
772     if (nullptr != m_pDB)
773       m_pDB->rollback_transaction();
774   }
775   catch (...)
776   {
777     CLog::Log(LOGERROR, "database:rollbacktransaction failed");
778   }
779 }
780 
CreateDatabase()781 bool CDatabase::CreateDatabase()
782 {
783   BeginTransaction();
784   try
785   {
786     CLog::Log(LOGINFO, "creating version table");
787     m_pDS->exec("CREATE TABLE version (idVersion integer, iCompressCount integer)\n");
788     std::string strSQL=PrepareSQL("INSERT INTO version (idVersion,iCompressCount) values(%i,0)\n", GetSchemaVersion());
789     m_pDS->exec(strSQL);
790 
791     CreateTables();
792     CreateAnalytics();
793   }
794   catch (...)
795   {
796     CLog::Log(LOGERROR, "%s unable to create database:%i", __FUNCTION__, (int)GetLastError());
797     RollbackTransaction();
798     return false;
799   }
800 
801   return CommitTransaction();
802 }
803 
UpdateVersionNumber()804 void CDatabase::UpdateVersionNumber()
805 {
806   std::string strSQL=PrepareSQL("UPDATE version SET idVersion=%i\n", GetSchemaVersion());
807   m_pDS->exec(strSQL);
808 }
809 
BuildSQL(const std::string & strQuery,const Filter & filter,std::string & strSQL)810 bool CDatabase::BuildSQL(const std::string &strQuery, const Filter &filter, std::string &strSQL)
811 {
812   strSQL = strQuery;
813 
814   if (!filter.join.empty())
815     strSQL += filter.join;
816   if (!filter.where.empty())
817     strSQL += " WHERE " + filter.where;
818   if (!filter.group.empty())
819     strSQL += " GROUP BY " + filter.group;
820   if (!filter.order.empty())
821     strSQL += " ORDER BY " + filter.order;
822   if (!filter.limit.empty())
823     strSQL += " LIMIT " + filter.limit;
824 
825   return true;
826 }
827 
BuildSQL(const std::string & strBaseDir,const std::string & strQuery,Filter & filter,std::string & strSQL,CDbUrl & dbUrl)828 bool CDatabase::BuildSQL(const std::string &strBaseDir, const std::string &strQuery, Filter &filter, std::string &strSQL, CDbUrl &dbUrl)
829 {
830   SortDescription sorting;
831   return BuildSQL(strBaseDir, strQuery, filter, strSQL, dbUrl, sorting);
832 }
833 
BuildSQL(const std::string & strBaseDir,const std::string & strQuery,Filter & filter,std::string & strSQL,CDbUrl & dbUrl,SortDescription & sorting)834 bool CDatabase::BuildSQL(const std::string &strBaseDir, const std::string &strQuery, Filter &filter, std::string &strSQL, CDbUrl &dbUrl, SortDescription &sorting /* = SortDescription() */)
835 {
836   // parse the base path to get additional filters
837   dbUrl.Reset();
838   if (!dbUrl.FromString(strBaseDir) || !GetFilter(dbUrl, filter, sorting))
839     return false;
840 
841   return BuildSQL(strQuery, filter, strSQL);
842 }
843