7#include <QLoggingCategory>
8#include <QJsonDocument>
11#include "RabbitCommonDir.h"
12#include "RabbitCommonTools.h"
16static Q_LOGGING_CATEGORY(log,
"DB")
19 , m_MinVersion(
"0.1.0")
22 qDebug(log) << Q_FUNC_INFO;
23 m_szConnectName =
"connect";
26CDatabase::~CDatabase()
28 qDebug(log) << Q_FUNC_INFO;
34 QString szErr =
"Only one of OpenDatabase and SetDatabase can be called, and it can only be called once";
35 Q_ASSERT_X(!IsOpen(),
"Database", szErr.toStdString().c_str());
40QSqlDatabase CDatabase::GetDatabase()
const
49 bRet = OpenSQLiteDatabase();
50 if(!bRet)
return false;
51 return OnInitializeDatabase();
55 QString szErr =
"Only one of OpenDatabase or SetDatabase can be called, and it can only be called once";
56 Q_ASSERT_X(!IsOpen(),
"Database", szErr.toStdString().c_str());
58 if(pPara->GetType() ==
"QSQLITE")
59 bRet = OpenSQLiteDatabase(QString(), pPara->GetDatabaseName());
60 else if(pPara->GetType() ==
"QMYSQL")
61 bRet = OpenMySqlDatabase(pPara);
62 else if(pPara->GetType() ==
"QODBC")
63 bRet = OpenODBCDatabase(pPara);
65 QSqlDriver *driver = GetDatabase().driver();
67 qDebug(log) <<
"=== Features for" << pPara->GetType() <<
"===";
68 qDebug(log) <<
"Transactions:" << driver->hasFeature(QSqlDriver::Transactions);
69 qDebug(log) <<
"Query size:" << driver->hasFeature(QSqlDriver::QuerySize);
70 qDebug(log) <<
"BLOB:" << driver->hasFeature(QSqlDriver::BLOB);
71 qDebug(log) <<
"Unicode:" << driver->hasFeature(QSqlDriver::Unicode);
72 qDebug(log) <<
"Prepared queries:" << driver->hasFeature(QSqlDriver::PreparedQueries);
73 qDebug(log) <<
"Named placeholders:" << driver->hasFeature(QSqlDriver::NamedPlaceholders);
74 qDebug(log) <<
"Positional placeholders:" << driver->hasFeature(QSqlDriver::PositionalPlaceholders);
75 qDebug(log) <<
"Last insert ID:" << driver->hasFeature(QSqlDriver::LastInsertId);
76 qDebug(log) <<
"Batch operations:" << driver->hasFeature(QSqlDriver::BatchOperations);
77 qDebug(log) <<
"Event notifications:" << driver->hasFeature(QSqlDriver::EventNotifications);
78 qDebug(log) <<
"Finish query:" << driver->hasFeature(QSqlDriver::FinishQuery);
79 qDebug(log) <<
"Multiple result sets:" << driver->hasFeature(QSqlDriver::MultipleResultSets);
80 qDebug(log) <<
"Cancel query:" << driver->hasFeature(QSqlDriver::CancelQuery);
83 if(!bRet)
return false;
85 return OnInitializeDatabase();
88bool CDatabase::OpenSQLiteDatabase(
89 const QString &connectionName,
const QString &dbPath)
92 if (dbPath.isEmpty()) {
94 QString dataDir = RabbitCommon::CDir::Instance()->GetDirUserDatabase();
99 databasePath = dir.filePath(
"remote_control.db");
101 databasePath = dbPath;
104 if(!connectionName.isEmpty())
105 m_szConnectName = connectionName;
108 m_database = QSqlDatabase::addDatabase(
"QSQLITE", m_szConnectName);
109 m_database.setDatabaseName(databasePath);
111 if (!m_database.open()) {
112 qCritical(log) <<
"Failed to open sqlite database:"
113 << m_database.lastError().text()
114 <<
"connect name:" << m_database.connectionName()
115 <<
"database name:" << m_database.databaseName();
119 qInfo(log) <<
"Open sqlite database connect:"
120 << m_database.connectionName()
121 <<
"database name:" << m_database.databaseName();
127 if(!pPara)
return false;
129 m_database = QSqlDatabase::addDatabase(
"QMYSQL", m_szConnectName);
130 QString szDbName = pPara->GetDatabaseName();
131 if(szDbName.isEmpty())
132 szDbName =
"remote_control";
133 m_database.setDatabaseName(szDbName);
134 auto &net = pPara->m_Net;
135 m_database.setHostName(net.GetHost());
136 m_database.setPort(net.GetPort());
137 auto &user = net.m_User;
138 m_database.setUserName(user.GetName());
139 m_database.setPassword(user.GetPassword());
141 if (!m_database.open()) {
142 qCritical(log) <<
"Failed to open mysql database:"
143 << m_database.lastError().text()
144 <<
"connect name:" << m_database.connectionName()
145 <<
"database name:" << m_database.databaseName();
149 QSqlQuery query(GetDatabase());
150 bool success = query.exec(
"CREATE DATABASE IF NOT EXISTS " + szDbName);
152 qCritical(log) <<
"Failed to create" << szDbName <<
"database:"
153 << query.lastError().text()
154 <<
"Sql:" << query.executedQuery();
158 success = query.exec(
"use remote_control");
160 qCritical(log) <<
"Failed to use" << szDbName <<
"database:"
161 << query.lastError().text()
162 <<
"Sql:" << query.executedQuery();
166 qInfo(log) <<
"Open mysql database connect:"
167 << m_database.connectionName()
168 <<
"database name:" << m_database.databaseName();
174 if(!pPara)
return false;
176 m_database = QSqlDatabase::addDatabase(
"QODBC", m_szConnectName);
177 QString szDbName = pPara->GetDatabaseName();
178 if(szDbName.isEmpty())
179 szDbName =
"remote_control";
180 m_database.setDatabaseName(szDbName);
182 if (!m_database.open()) {
183 qCritical(log) <<
"Failed to open odbc database:"
184 << m_database.lastError().text()
185 <<
"connect name:" << m_database.connectionName()
186 <<
"database name:" << m_database.databaseName();
190 QSqlQuery query(GetDatabase());
191 bool success = query.exec(
"CREATE DATABASE IF NOT EXISTS " + szDbName);
193 qCritical(log) <<
"Failed to create" << szDbName <<
"database:"
194 << query.lastError().text()
195 <<
"Sql:" << query.executedQuery();
199 success = query.exec(
"use remote_control");
201 qCritical(log) <<
"Failed to use" << szDbName <<
"database:"
202 << query.lastError().text()
203 <<
"Sql:" << query.executedQuery();
207 qInfo(log) <<
"Open odbc database connect:"
208 << m_database.connectionName()
209 <<
"database name:" << m_database.databaseName();
213bool CDatabase::OnInitializeDatabase()
217 bRet = OnInitializeSqliteDatabase();
220 if(m_pPara->GetType() ==
"QSQLITE")
221 bRet = OnInitializeSqliteDatabase();
222 else if(m_pPara->GetType() ==
"QMYSQL" || m_pPara->GetType() ==
"QODBC") {
223 bRet = OnInitializeMySqlDatabase();
228bool CDatabase::OnInitializeSqliteDatabase()
233bool CDatabase::OnInitializeMySqlDatabase()
238bool CDatabase::IsOpen()
const
240 return m_database.isOpen();
243void CDatabase::CloseDatabase()
245 if (m_database.isOpen()) {
248 QSqlDatabase::removeDatabase(m_szConnectName);
251bool CDatabase::ExportToJsonFile(
const QString &szFile)
254 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
255 qCritical(log) <<
"Failed to open file:" << szFile << file.errorString();
259 QTextStream out(&file);
260#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
261 out.setEncoding(QStringConverter::Utf8);
263 out.setCodec(
"UTF-8");
265 out.setGenerateByteOrderMark(
true);
269 root.insert(
"Title",
"Rabbit Remote Control");
270 root.insert(
"Author",
"Kang Lin");
271 root.insert(
"Version",
"0.1.0");
274 bRet = ExportToJson(root);
284bool CDatabase::ImportFromJsonFile(
const QString &szFile)
288 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
289 qCritical(log) <<
"Failed to open file:" << szFile << file.errorString();
295 doc = QJsonDocument::fromJson(file.readAll());
298 auto root = doc.object();
299 if(root[
"Title"] !=
"Rabbit Remote Control") {
300 qCritical(log) <<
"The file format is error";
303 QString szVersion = root[
"Version"].toString();
304 if(RabbitCommon::CTools::VersionCompare(szVersion, m_MinVersion) < 0) {
305 qCritical(log) <<
"The version is not support:"
306 << szVersion <<
"<" << m_MinVersion;
309 bRet = ImportFromJson(doc.object());
316CDatabaseIcon::CDatabaseIcon(QObject *parent)
319 m_szConnectName =
"icon_connect";
320 m_szTableName =
"icon";
323CDatabaseIcon::CDatabaseIcon(
const QString &szPrefix, QObject *parent)
326 m_szConnectName =
"icon_connect";
327 if(!szPrefix.isEmpty())
328 m_szTableName = szPrefix +
"_" + m_szTableName;
331bool CDatabaseIcon::OnInitializeSqliteDatabase()
333 QSqlQuery query(GetDatabase());
337 "CREATE TABLE IF NOT EXISTS " + m_szTableName +
" ("
338 " id INTEGER PRIMARY KEY AUTOINCREMENT,"
344 bool success = query.exec(szSql);
346 qCritical(log) <<
"Failed to create icon sqlite table:"
347 << m_szTableName << query.lastError().text()
348 <<
"Sql:" << query.executedQuery();
351 success = query.exec(
"CREATE INDEX IF NOT EXISTS idx_" + m_szTableName +
"_name ON " + m_szTableName +
"(name)");
353 qWarning(log) <<
"Failed to create icon name index:"
354 << m_szTableName << query.lastError().text()
355 <<
"Sql:" << query.executedQuery();
357 success = query.exec(
"CREATE INDEX IF NOT EXISTS idx_" + m_szTableName +
"_hash ON " + m_szTableName +
"(hash)");
359 qWarning(log) <<
"Failed to create icon hash index:"
360 << m_szTableName << query.lastError().text()
361 <<
"Sql:" << query.executedQuery();
366bool CDatabaseIcon::OnInitializeMySqlDatabase()
368 QSqlQuery query(GetDatabase());
372 "CREATE TABLE IF NOT EXISTS " + m_szTableName +
" ("
373 " id INTEGER PRIMARY KEY AUTO_INCREMENT,"
377 " UNIQUE KEY idx_icon_name (name(255)),"
378 " INDEX idx_icon_hash (hash(255))"
381 bool success = query.exec(szSql);
383 qCritical(log) <<
"Failed to create icon mysql table:"
384 << m_szTableName << query.lastError().text()
385 <<
"Sql:" << query.executedQuery();
395 if(icon.isNull())
return 0;
398 QSqlQuery query(GetDatabase());
399 QString szName = icon.name();
400 if(szName.isEmpty()) {
402 QByteArray data = RabbitCommon::CIconUtils::iconToByteArray(icon);
403 QString szHash = RabbitCommon::CIconUtils::hashIconData(data);
404 if(data.isEmpty() || szHash.isEmpty())
406 szSql =
"SELECT id, data FROM " + m_szTableName +
" WHERE hash=:hash";
407 query.prepare(szSql);
408 query.bindValue(
":hash", szHash);
413 qCritical(log) <<
"Failed to select icon hash:"
414 << szHash << query.lastError().text();
417 while (query.next()) {
419 if(data == query.value(1).toByteArray()) {
420 return query.value(0).toInt();
424 szSql =
"INSERT INTO " + m_szTableName +
" (hash, data) "
425 "VALUES (:hash, :data)";
426 query.prepare(szSql);
427 query.bindValue(
":hash", szHash);
428 query.bindValue(
":data", data);
431 qCritical(log) <<
"Failed to insert icon hash:"
432 << szHash << query.lastError().text();
435 return query.lastInsertId().toInt();
439 szSql =
"SELECT id FROM " + m_szTableName +
" WHERE name=:name";
440 query.prepare(szSql);
441 query.bindValue(
":name", szName);
444 qCritical(log) <<
"Failed to select icon name:"
445 << szName << query.lastError().text();
449 return query.value(0).toInt();
453 szSql =
"INSERT INTO " + m_szTableName +
" (name) VALUES (:name)";
454 query.prepare(szSql);
455 query.bindValue(
":name", szName);
458 qCritical(log) <<
"Failed to insert icon name:"
459 << szName << query.lastError().text();
462 return query.lastInsertId().toInt();
468 QSqlQuery query(GetDatabase());
470 "SELECT name, data FROM " + m_szTableName +
473 query.bindValue(
":id",
id);
474 bool bRet = query.exec();
476 qCritical(log) <<
"Failed to get icon id:"
477 <<
id << query.lastError().text();
482 QString szName = query.value(0).toString();
483 if(!szName.isEmpty()) {
484 return QIcon::fromTheme(szName);
486 QByteArray ba = query.value(1).toByteArray();
487 return RabbitCommon::CIconUtils::byteArrayToIcon(ba);
492bool CDatabaseIcon::ExportToJson(QJsonObject &obj)
494 QSqlQuery query(GetDatabase());
496 "SELECT id, name, hash, data FROM " + m_szTableName
499 bool bRet = query.exec();
501 qCritical(log) <<
"Failed to export icon to json:"
502 << query.lastError().text()
503 <<
"Sql:" << query.executedQuery();
508 while (query.next()) {
510 i.insert(
"id", query.value(0).toInt());
511 i.insert(
"name", query.value(1).toString());
512 i.insert(
"hash", query.value(2).toString());
513 i.insert(
"data", query.value(3).toByteArray().toBase64().toStdString().c_str());
517 obj.insert(
"icon", icon);
521bool CDatabaseIcon::ImportFromJson(
const QJsonObject &obj)
526bool CDatabaseIcon::ExportIconToJson(
const QIcon &icon, QJsonObject &obj)
528 QString szIconName = icon.name();
529 if(szIconName.isEmpty()) {
530 QByteArray baIcon = RabbitCommon::CIconUtils::iconToByteArray(icon);
531 obj.insert(
"IconData", baIcon.toBase64().toStdString().c_str());
533 obj.insert(
"IconName", szIconName);
538bool CDatabaseIcon::ImportIconFromJson(
const QJsonObject &itemObj, QIcon &icon)
540 QString szIconName = itemObj[
"IconName"].toString();
541 if(szIconName.isEmpty()) {
542 QByteArray baIcon(itemObj[
"IconData"].toString().toStdString().c_str());
543 if(!baIcon.isEmpty()) {
544 baIcon = QByteArray::fromBase64(baIcon);
545 icon = RabbitCommon::CIconUtils::byteArrayToIcon(baIcon);
548 icon = QIcon::fromTheme(szIconName);
553bool CDatabaseFile::ExportFileToJson(
const QString &szFile, QJsonObject &obj)
555 QFileInfo fi(szFile);
557 qCritical(log) <<
"File is not exist:" << szFile;
561 if(!f.open(QFile::ReadOnly | QFile::Text)) {
562 qCritical(log) <<
"Failed to open file:" << szFile << f.errorString();
565 QString szFileContent = f.readAll();
567 if(szFileContent.isEmpty()) {
568 qCritical(log) <<
"The file is empty:" << szFile;
571 obj.insert(
"FileName", fi.fileName());
572 obj.insert(
"FileContent", szFileContent);
576bool CDatabaseFile::ImportFileFromJson(
const QJsonObject &obj, QString &szFile)
578 QString szFileContent = obj[
"FileContent"].toString();
579 if(szFileContent.isEmpty()) {
580 qCritical(log) <<
"The file is empty";
583 szFile = obj[
"FileName"].toString();
584 if(szFile.isEmpty()) {
585 qCritical(log) <<
"The file name is empty";
588 szFile = RabbitCommon::CDir::Instance()->GetDirUserData()
589 + QDir::separator() + szFile;
590 QFileInfo fi(szFile);
593 if(!f.open(QFile::WriteOnly | QFile::Text)) {
594 qCritical(log) <<
"Failed to open file:" << szFile
598 f.write(szFileContent.toStdString().c_str(), szFileContent.size());
604bool CDatabaseFile::ImportFileToDatabaseFromJson(
const QJsonObject &obj, QString &szFile)
606 bool bRet = ImportFileFromJson(obj, szFile);
607 if(!bRet)
return bRet;
612CDatabaseFile::CDatabaseFile(QObject* parent) :
CDatabase(parent)
614 m_szConnectName =
"file_connect";
615 m_szTableName =
"file";
618CDatabaseFile::CDatabaseFile(
const QString &szPrefix, QObject *parent)
621 m_szConnectName =
"file_connect";
622 if(!szPrefix.isEmpty())
623 m_szTableName = szPrefix +
"_" + m_szTableName;
626bool CDatabaseFile::ExportToJson(QJsonObject &obj)
631bool CDatabaseFile::ImportFromJson(
const QJsonObject &obj)
639 if(szFile.isEmpty())
return content;
640 QFileInfo fi(szFile);
641 QSqlQuery query(GetDatabase());
643 "SELECT content FROM " + m_szTableName +
" "
644 " WHERE file=:file");
645 query.bindValue(
":file", fi.fileName());
646 bool ok = query.exec();
649 content = query.value(0).toByteArray();
652 qCritical(log) <<
"Failed to Load:"
653 << m_szTableName << query.lastError().text()
654 <<
"Sql:" << query.executedQuery();
662 if(szFile.isEmpty())
return false;
664 if(!f.open(QFile::ReadOnly | QFile::Text)) {
665 qCritical(log) <<
"Failed to open file:"
666 << szFile << f.errorString() <<
":" << f.error();
669 QByteArray content = f.readAll();
671 QFileInfo fi(szFile);
672 QSqlQuery query(GetDatabase());
674 "SELECT content FROM " + m_szTableName +
" "
675 " WHERE file=:file");
676 query.bindValue(
":file", fi.fileName());
677 bool success = query.exec();
678 if(success && query.next()) {
680 "UPDATE " + m_szTableName +
" "
681 "SET content = :content "
684 query.bindValue(
":file", fi.fileName());
685 query.bindValue(
":content", content);
688 "INSERT INTO " + m_szTableName +
" "
690 "VALUES (:file, :content)"
692 query.bindValue(
":file", fi.fileName());
693 query.bindValue(
":content", content);
695 success = query.exec();
697 qCritical(log) <<
"Failed to save file:"
698 << m_szTableName << query.lastError().text()
699 <<
"Sql:" << query.executedQuery();
706bool CDatabaseFile::OnInitializeSqliteDatabase()
708 QSqlQuery query(GetDatabase());
712 "CREATE TABLE IF NOT EXISTS " + m_szTableName +
" ("
713 " file TEXT KEY NOT NULL UNIQUE,"
717 bool success = query.exec(szSql);
719 qCritical(log) <<
"Failed to create file table:"
720 << m_szTableName << query.lastError().text()
721 <<
"Sql:" << query.executedQuery();
728bool CDatabaseFile::OnInitializeMySqlDatabase()
730 QSqlQuery query(GetDatabase());
731 QString szSql =
"CREATE TABLE IF NOT EXISTS `" + m_szTableName +
"` ( "
732 "`file` TEXT NOT NULL , "
733 "`content` LONGBLOB, "
734 "UNIQUE KEY `uk_file` (`file`(255))"
736 bool success = query.exec(szSql);
738 qCritical(log) <<
"Failed to create file table:"
739 << m_szTableName << query.lastError().text()
740 <<
"Sql:" << query.executedQuery();
bool Save(const QString &szFile)
Save
QByteArray Load(const QString &szFile)
Load
int GetIcon(const QIcon &icon)
Get icon id
virtual bool OpenDatabase(CParameterDatabase *pPara=nullptr)
OpenDatabase