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