Heart 1.3.848.aa44c26
Heart is base back end library for your c++ Qt projects.
sqldbwriter.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2018-2025 QuasarApp.
3 * Distributed under the lgplv3 software license, see the accompanying
4 * Everyone is permitted to copy and distribute verbatim copies
5 * of this license document, but changing it is not allowed.
6*/
7
8#include "sqldbwriter.h"
9
10#include <QRegularExpression>
11#include <QSqlDatabase>
12#include <QSqlQuery>
13#include <QDebug>
14#include <QSqlError>
15#include <quasarapp.h>
16#include <QJsonDocument>
17#include <QJsonObject>
18#include <QHash>
19#include <dbobject.h>
20#include <QSqlRecord>
21#include <QStandardPaths>
22#include <QCoreApplication>
23
24namespace QH {
25using namespace PKG;
26
27bool SqlDBWriter::exec(QSqlQuery *sq, const QString& sqlFile) const {
28 QFile f(sqlFile);
29 bool result = true;
30 if (f.open(QIODevice::ReadOnly)) {
31
32 QString temp, delimiter = ";";
33 QTextStream stream(&f);
34#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
35 stream.setCodec("UTF8");
36#endif
37 int lineNumber = 0;
38 while(!stream.atEnd()) {
39 QString line = stream.readLine();
40 lineNumber++;
41 int linedelimiterIndex = line.lastIndexOf(delimiter, -1, Qt::CaseInsensitive);
42 int delimiterIndex = temp.size() + linedelimiterIndex;
43
44 if (line.contains("--"))
45 continue;
46
47 temp += line;
48
49 if (linedelimiterIndex > -1) {
50 result = result && sq->exec(temp.left(delimiterIndex));
51 temp = temp.remove(0, delimiterIndex + 1);
52
53 if (!result) {
54 qCritical() << "Exec database error. File: " << sqlFile << " Line:" << lineNumber << sq->lastError().text();
55 f.close();
56 return false;
57 }
58 }
59 }
60
61 f.close();
62 return result;
63 }
64
65 qCritical() << "sql source file is not open: " << sqlFile;
66
67 return false;
68}
69
70bool SqlDBWriter::initDbPrivate(const QVariantMap &params) {
71 _config = params;
72
73 if (_db)
74 delete _db;
75
76 _db = new QSqlDatabase(initSqlDataBasse(_config["DBDriver"].toString(),
77 _config["DBFilePath"].toString()));
78
79 if (_config.contains("DBFilePath")) {
80
81 auto path = QFileInfo(_config["DBFilePath"].toString());
82 if (!QDir("").mkpath(path.absolutePath())) {
83 return false;
84 }
85
86 _db->setDatabaseName(path.absoluteFilePath());
87 }
88
89 if (_config.contains("DBLogin")) {
90 _db->setPassword(_config["DBLogin"].toString());
91 }
92
93 if (_config.contains("DBPass")) {
94 _db->setPassword(_config["DBPass"].toString());
95 }
96
97 if (_config.contains("DBHost")) {
98 _db->setHostName(_config["DBHost"].toString());
99 }
100
101 if (_config.contains("DBPort")) {
102 _db->setPort(_config["DBPort"].toInt());
103 }
104
105 if (!_db->open()) {
106 qCritical() << _db->lastError().text();
107 return false;
108 }
109
110 for (const QString& sqlFile : std::as_const(_SQLSources)) {
111 QSqlQuery query(*_db);
112 if (!exec(&query, sqlFile)) {
113 return false;
114 }
115 }
116
117 if (_config.contains("DBInitFile")) {
118 auto path = QFileInfo(_config["DBInitFile"].toString()).absoluteFilePath();
119
120 QSqlQuery query(*_db);
121 if (!exec(&query, path)) {
122 return false;
123 }
124 }
125
126 initSuccessful = _db->isValid();
127 return initSuccessful;
128}
129
130bool SqlDBWriter::doQueryPrivate(const QString &query, const QVariantMap &bindValues, QSqlQuery* result) const {
131
132 if (!db()) {
133 return false;
134 }
135
136 QSqlQuery q(*db());
137 if (bindValues.size()) {
138 if (!q.prepare(query)) {
139 qCritical() << "request error : " << q.lastError().text();
140 return false;
141 }
142
143 for (auto it = bindValues.begin(); it != bindValues.end(); ++it) {
144 q.bindValue(it.key(), it.value());
145 }
146
147 if (!q.exec()) {
148 qCritical() << "execute error : " << q.lastError().text();
149
150 return false;
151 }
152 } else {
153 if (!q.exec(query)) {
154 qCritical() << "bind values error : " << q.lastError().text();
155
156 return false;
157 }
158 }
159
160 if (result) {
161 *result = std::move(q);
162 }
163
164 return true;
165
166}
167
168bool SqlDBWriter::doSqlPrivate(const QString &sqlFile) const {
169 QSqlQuery query(*_db);
170 if (!exec(&query, sqlFile)) {
171 return false;
172 }
173
174 return true;
175}
176
178 if (!db()) {
179 return false;
180 }
181
182 QSqlQuery query(*db());
183 QString request = QString("PRAGMA foreign_keys = ON");
184 if (!query.exec(request)) {
185 qDebug() << "request error : " << query.lastError().text();
186 return false;
187 }
188
189 return true;
190}
191
193
194 if (!db()) {
195 return false;
196 }
197
198 QSqlQuery query(*db());
199 QString request = QString("PRAGMA foreign_keys = OFF");
200 if (!query.exec(request)) {
201 qDebug() << "request error : " << query.lastError().text();
202 return false;
203 }
204
205 return true;
206
207}
208
209QVariantMap SqlDBWriter::getInitParams(const QString &initFile) const {
210 QFile file(initFile);
211 QVariantMap res;
212
213 if (file.open(QIODevice::ReadOnly)) {
214 QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
215 file.close();
216
217 if (!doc.isObject()) {
218 return res;
219 }
220
221 QJsonObject obj = doc.object();
222 return obj.toVariantMap();
223 }
224
225 return res;
226}
227
228/*
229 * about driver see https://doc.qt.io/qt-5/sql-driver.html
230 */
232 QVariantMap params;
233 params["DBDriver"] = "QSQLITE";
234 params["DBFile"] = DEFAULT_DB_PATH;
235 return params;
236}
237
238QSqlDatabase SqlDBWriter::initSqlDataBasse(const QString& driverName,
239 const QString& name) {
240
241 return QSqlDatabase::addDatabase(driverName,
242 QFileInfo(name).fileName());
243}
244
245QSqlDatabase *SqlDBWriter::db() {
246 return _db;
247}
248
249const QSqlDatabase *SqlDBWriter::db() const {
250 return _db;
251}
252
253SqlDBWriter::SqlDBWriter(QThread *thread, QObject* ptr):
254 Async(thread, ptr) {
255}
256
257bool SqlDBWriter::initDb(const QString &initDbParams) {
258 QVariantMap params;
259
260 if (initDbParams.isEmpty()) {
261 params = defaultInitPararm();
262 } else {
263 params = getInitParams(initDbParams);
264 }
265
266 return initDb(params);
267}
268
269bool SqlDBWriter::initDb(const QVariantMap &params) {
270 auto handleInitDb = [&params, this]() {
271 return initDbPrivate(params);
272 };
273
274 return asyncLauncher(handleInitDb, true);
275}
276
278 return db() && db()->isValid() && db()->isOpen() && initSuccessful;
279}
280
281bool SqlDBWriter::getAllObjects(const DBObject &templateObject,
282 QList<QSharedPointer<DBObject>> &result) {
283
284 auto getAll = [&templateObject, &result, this]() {
285 return SqlDBWriter::selectQuery(templateObject, result);
286 };
287
288 return asyncLauncher(getAll, true);
289}
290
291bool SqlDBWriter::updateObject(const QSharedPointer<DBObject> &ptr, bool wait) {
292
293 Async::Job job = [this, ptr]() {
294 return updateQuery(ptr);
295 };
296
297 return asyncLauncher(job, wait);
298}
299
300bool SqlDBWriter::deleteObject(const QSharedPointer<DBObject> &ptr, bool wait) {
301
302 Async::Job job = [this, ptr]() {
303 return deleteQuery(ptr);
304 };
305
306 return asyncLauncher(job, wait);
307}
308
309bool SqlDBWriter::insertObject(const QSharedPointer<DBObject> &ptr,
310 bool wait,
311 const QWeakPointer<unsigned int>& autoincrementIdResult) {
312
313 if (wait) {
314 auto resultId = QSharedPointer<int>::create();
315 Async::Job job = [this, ptr, autoincrementIdResult]() {
316 return insertQuery(ptr, autoincrementIdResult);
317 };
318
319 return asyncLauncher(job, wait);
320 } else {
321 Async::Job job = [this, ptr]() {
322 return insertQuery(ptr);
323 };
324
325 return asyncLauncher(job, wait);
326 }
327}
328
329bool SqlDBWriter::replaceObject(const QSharedPointer<PKG::DBObject> &ptr, bool wait) {
330 Async::Job job = [this, ptr]() {
331 return replaceQuery(ptr);
332 };
333
334 return asyncLauncher(job, wait);
335}
336
337void SqlDBWriter::setSQLSources(const QStringList &list) {
338 _SQLSources = list;
339}
340
342 if (!db())
343 return "";
344
345 return db()->databaseName();
346}
347
349 if (_db) {
350 _db->close();
351
352 QString connectionName = _db->connectionName();
353
354 delete _db;
355 _db = nullptr;
356
357 QSqlDatabase::removeDatabase(connectionName);
358 }
359
360}
361
362bool SqlDBWriter::insertQuery(const QSharedPointer<DBObject> &ptr,
363 const QWeakPointer<unsigned int>& autoincrementIdResult) const {
364 if (!ptr)
365 return false;
366
367 if (!db()) {
368 return false;
369 }
370
371 QSqlQuery q(*db());
372
373 auto prepare = [ptr](QSqlQuery&q) {
374 return ptr->prepareInsertQuery(q, false);
375 };
376
377 auto cb = [&q, autoincrementIdResult]() {
378
379 if (auto&& ptr = autoincrementIdResult.lock()) {
380 *ptr = q.lastInsertId().toInt();
381 }
382
383 return true;
384 };
385
386 return workWithQuery(q, prepare, cb);
387}
388
389bool SqlDBWriter::replaceQuery(const QSharedPointer<PKG::DBObject> &ptr) const {
390 if (!ptr)
391 return false;
392
393 if (!db()) {
394 return false;
395 }
396
397 QSqlQuery q(*db());
398
399 auto prepare = [ptr](QSqlQuery&q) {
400 return ptr->prepareInsertQuery(q, true);
401 };
402
403 auto cb = [](){return true;};
404
405 return workWithQuery(q, prepare, cb);
406}
407
408bool SqlDBWriter::doQuery(const QString &query, const QVariantMap &bindValues,
409 bool wait, QSqlQuery* result) const {
410
411 wait = result || wait;
412
413 Async::Job job = [this, query, bindValues, result]() {
414 return doQueryPrivate(query, bindValues, result);
415 };
416
417 return asyncLauncher(job, wait);
418}
419
420bool SqlDBWriter::doSql(const QString &sqlFile, bool wait) const {
421
422 Async::Job job = [this, sqlFile]() {
423 return doSqlPrivate(sqlFile);
424 };
425
426 return asyncLauncher(job, wait);
427
428}
429
430bool SqlDBWriter::selectQuery(const DBObject& requestObject,
431 QList<QSharedPointer<QH::PKG::DBObject>> &result) {
432
433 if (!db()) {
434 return false;
435 }
436
437 QSqlQuery q(*db());
438 auto prepare = [&requestObject](QSqlQuery&q) {
439 return requestObject.prepareSelectQuery(q);
440 };
441
442 auto cb = [&q, &requestObject, &result]() -> bool {
443
444 if (requestObject.isBundle()) {
445 auto newObject = QSharedPointer<QH::PKG::DBObject>(requestObject.createDBObject());
446
447 if (!newObject)
448 return false;
449
450 while (q.next()) {
451 if (!newObject->fromSqlRecord(q.record())) {
452 qCritical() << "Select query finished successful but, "
453 "the fromSqlRecord method return false." << newObject->toString();
454 return false;
455 }
456 }
457
458 result.push_back(newObject);
459
460 } else {
461 while (q.next()) {
462 auto newObject = QSharedPointer<QH::PKG::DBObject>(requestObject.createDBObject());
463
464 if (!newObject)
465 return false;
466
467 if (!newObject->fromSqlRecord(q.record())) {
468 qCritical() << "Init sql object error.";
469 return false;
470 }
471 result.push_back(newObject);
472 }
473
474 return true;
475 }
476
477 return result.size();
478 };
479
480 return workWithQuery(q, prepare, cb);
481}
482
483bool SqlDBWriter::deleteQuery(const QSharedPointer<DBObject> &deleteObject) const {
484 if (!deleteObject)
485 return false;
486
487 QSqlQuery q(*db());
488
489 auto prepare = [deleteObject](QSqlQuery&q) {
490 return deleteObject->prepareRemoveQuery(q);
491 };
492
493 auto cb = []() -> bool {
494 return true;
495 };
496
497
498 return workWithQuery(q, prepare, cb);
499}
500
501bool SqlDBWriter::updateQuery(const QSharedPointer<DBObject> &ptr) const {
502 if (!ptr)
503 return false;
504
505 QSqlQuery q(*db());
506
507 auto prepare = [ptr](QSqlQuery&q) {
508 return ptr->prepareUpdateQuery(q);
509 };
510
511 auto cb = [](){return true;};
512
513 return workWithQuery(q, prepare, cb);
514}
515
516bool SqlDBWriter::workWithQuery(QSqlQuery &q,
517 const std::function< PrepareResult (QSqlQuery &)> &prepareFunc,
518 const std::function<bool ()> &cb) const {
519
520 auto printError = [](const QSqlQuery &q) {
521
522 qDebug() << "prepare sql error: " << q.lastError().text();
523
524 qCritical() << "exec sql error: " << q.lastError();
525
526 };
527
528
529 switch (prepareFunc(q)) {
530 case PrepareResult::Success: {
531
532 if (!q.exec()) {
533 printError(q);
534 return false;
535 }
536
537#ifdef HEART_PRINT_SQL_QUERIES
538 qDebug() << QString("Query executed successfull into %0\n"
539 "query: %1").
540 arg(_db->databaseName(), q.executedQuery();
541#endif
542
543 return cb();
544 }
545 case PrepareResult::Disabled: {
546
547 qWarning() << "call disabled operator!";
548 return true;
549 }
550
551 case PrepareResult::Fail: {
552 printError(q);
553 return false;
554 }
555
556 }
557
558 return false;
559
560}
561
562}
The Async class This is bundle of async templates and async wrappers.
Definition async.h:26
std::function< bool()> Job
Definition async.h:35
bool asyncLauncher(const Job &job, bool await=false, bool freaze=true) const
asyncLauncher This method invoke a job on the thread (using the asyncHandler method) of this object.
Definition async.cpp:80
The DBObject class- main class for work with data base.
Definition dbobject.h:94
virtual PrepareResult prepareSelectQuery(QSqlQuery &q) const
prepareSelectQuery This method should be prepare a query for selected data. Override this method for ...
Definition dbobject.cpp:30
virtual bool isBundle() const
isBundle This method definef determines whether the object will be abstract (composite objects) or si...
Definition dbobject.cpp:202
virtual DBObject * createDBObject() const =0
createDBObject This method should be create a object with the some type as the object called this met...
bool disableFK()
disableFK This method disable foreign key for the sqlite database.
bool deleteObject(const QSharedPointer< PKG::DBObject > &ptr, bool wait=false) override
deleteObject This method execute a delete method of obj and remove current object from database.
virtual bool updateQuery(const QSharedPointer< QH::PKG::DBObject > &ptr) const
updateQuery This method execute update query of object. For more Information see DBObject::prepareUpd...
QString databaseLocation() const
databaseLocation This method return location of database. If it is sqlite then return path to db file...
bool updateObject(const QSharedPointer< PKG::DBObject > &ptr, bool wait=false) override
updateObject This method execute a update method of the saveObject and save all changes into database...
bool doSql(const QString &sqlFile, bool wait) const override
doSql This method execute a query in this database.
QSqlDatabase * db()
db This method return db connection. If SqlDBWriter in not inited then return nullptr....
bool enableFK()
enableFK This method enable foreign key for the sqlite database.
virtual bool replaceQuery(const QSharedPointer< QH::PKG::DBObject > &insertObject) const
replaceQuery This method prepare the replce object query.
virtual QSqlDatabase initSqlDataBasse(const QString &driverName, const QString &name)
initSqlDataBasse This method create ad database connection. Without configuration....
SqlDBWriter(QThread *thread, QObject *ptr=nullptr)
bool doQuery(const QString &query, const QVariantMap &bindValues={}, bool wait=false, QSqlQuery *result=nullptr) const override
doQuery This method execute a query in this database.
bool replaceObject(const QSharedPointer< PKG::DBObject > &ptr, bool wait=false) override
replaceObject This method execute a replace method of the saveObject and insert or save if not exists...
virtual bool selectQuery(const QH::PKG::DBObject &requestObject, QList< QSharedPointer< QH::PKG::DBObject > > &result)
selectQuery generate select query to database from parameters.
void setSQLSources(const QStringList &list) override
setSQLSources This method set sql sources for deployed database.
virtual bool isValid() const
isValid This method return true if database is successful inited and working correctly.
virtual bool deleteQuery(const QSharedPointer< QH::PKG::DBObject > &deleteObject) const
deleteQuery This method prepare the delete object query.
virtual bool initDb(const QString &initDbParams=DEFAULT_DB_PATH)
initDb This method is wraper of the initDb(const QVariantMap &params) method. This implementation rea...
virtual QVariantMap getInitParams(const QString &initFile) const
getInitPararm This method read parameters of database.
virtual ~SqlDBWriter() override
bool getAllObjects(const PKG::DBObject &templateObject, QList< QSharedPointer< PKG::DBObject > > &result) override
getAllObjects This method execute a select method of the templateObject and return list of all select...
virtual bool insertQuery(const QSharedPointer< QH::PKG::DBObject > &insertObject, const QWeakPointer< unsigned int > &autoIncrementID={}) const
insertQuery This method prepare the insert object query.
virtual QVariantMap defaultInitPararm() const
defaultInitPararm This method return default parameters of the database.
bool insertObject(const QSharedPointer< PKG::DBObject > &ptr, bool wait=false, const QWeakPointer< unsigned int > &autoincrementIdResult={}) override
insertObject This method execute a insert method of the saveObject and insert it into database.
#define DEFAULT_DB_PATH
Definition config.h:27
PrepareResult
The PrepareResult enum is result of work prepare sql query of dbobjects.
Definition dbobject.h:24
The QH namespace - QuasarApp Heart namespace. This namespace contains all classes of the Heart librar...
Definition heart.cpp:13