├── AsyncSql.pro ├── README.md ├── asyncsql ├── asyncmodelregister.cpp ├── asyncmodelregister.h ├── asyncsqltablemodel.cpp ├── asyncsqltablemodel.h ├── databaseconnection.cpp ├── databaseconnection.h ├── databaseexception.cpp ├── databaseexception.h ├── queryrequest.cpp ├── queryrequest.h ├── queryresult.cpp ├── queryresult.h ├── querythread.cpp ├── querythread.h ├── queryworker.cpp └── queryworker.h ├── databases └── example.db └── examples └── databaseviewer ├── main.cpp ├── mainwindow.cpp ├── mainwindow.h ├── mainwindow.ui ├── optionsdialog.cpp ├── optionsdialog.h ├── optionsdialog.ui ├── ui_mainwindow.h └── ui_optionsdialog.h /AsyncSql.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2016-02-22T02:57:22 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui sql 8 | 9 | CONFIG += c++11 10 | 11 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 12 | 13 | TARGET = AsyncSql 14 | TEMPLATE = app 15 | 16 | 17 | SOURCES += examples/databaseviewer/main.cpp\ 18 | examples/databaseviewer/mainwindow.cpp \ 19 | asyncsql/asyncmodelregister.cpp \ 20 | asyncsql/asyncsqltablemodel.cpp \ 21 | asyncsql/databaseconnection.cpp \ 22 | asyncsql/queryrequest.cpp \ 23 | asyncsql/queryresult.cpp \ 24 | asyncsql/querythread.cpp \ 25 | asyncsql/queryworker.cpp \ 26 | asyncsql/databaseexception.cpp \ 27 | examples/databaseviewer/optionsdialog.cpp 28 | 29 | HEADERS += examples/databaseviewer/mainwindow.h \ 30 | asyncsql/asyncmodelregister.h \ 31 | asyncsql/asyncsqltablemodel.h \ 32 | asyncsql/databaseconnection.h \ 33 | asyncsql/queryrequest.h \ 34 | asyncsql/queryresult.h \ 35 | asyncsql/querythread.h \ 36 | asyncsql/queryworker.h \ 37 | asyncsql/databaseexception.h \ 38 | examples/databaseviewer/optionsdialog.h 39 | 40 | FORMS += examples/databaseviewer/mainwindow.ui \ 41 | examples/databaseviewer/optionsdialog.ui 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Async SQL 2 | 3 | The asynchronous version of **QSqlTableModel**. 4 | 5 | I'm sure you love **QSqlTableModel** as much as I do. The only problem with it is that it's synchronous (which means that all queries run on the UI thread). This makes them unusable for large data sets or complex database queries or slow database access over a network (because it would cause your UI to freeze occasionally). 6 | 7 | The solution? Combine the ease of using **QSqlTableModel** with a powerful single-thread backend. It's basically **QSqlTableModel** on steroids! 8 | 9 | I conceived the concept of this project after reading [this article](http://www.linuxjournal.com/article/9602?page=0,0) about a year ago. Thanks Dave Berton! 10 | 11 | **NOTE**: I will make this article sound a lot more professional later. Let me have my fun for now! 12 | 13 | ## Features 14 | - Works for the **SQLite** and **MySQL** databases (only) 15 | - Allows for limit setting 16 | - Can be used to display tables that requires foreign key (it's still pretty difficult for now, but I've done it before) 17 | - Every other feature that the **QSqlTableModel** has 18 | - Allows for custom operations on the database thread (see example below) 19 | 20 | ## How to use 21 | The project itself is an example of how to use this library. However, here's a preview: 22 | 23 | // In header file 24 | AsyncSql::AsyncSqlTableModel *model; 25 | ... 26 | 27 | // In source file 28 | using namespace AsyncSql; 29 | ... 30 | // Set database connection options using the DatabaseConnection class 31 | DatabaseConnection::setDefaultDriver(DatabaseConnection::SQLite); 32 | ... 33 | model = new AsyncSqlTableModel(this); 34 | model->setTable("sales"); 35 | 36 | // Using C++11 37 | connect(model, &AsyncSqlTableModel::selected, [this](bool successful) 38 | { 39 | // Display the first record if successful. 40 | if(successful) 41 | qDebug() << "Hello AsyncSql!" << model->record(0); 42 | else 43 | qDebug() << "Error discovered: " << model->lastError().text(); 44 | }); 45 | 46 | You can view the table with a **QTableView**, since **AsyncTableModel** inherits **QAbstractTableModel**. 47 | 48 | // In header file... 49 | QTableView *view; 50 | 51 | // In source file... 52 | view = new QTableView(this); 53 | view->setModel(model); 54 | 55 | ## Custom operations 56 | In some occasions, you may want to run specific commands that are not provided by this library. A good example would be creating a user on a MySQL database. To achieve this, use the **AsyncSql::QueryRequest::setCustomOperation()** function. 57 | 58 | // Assume user name and password are defined 59 | // using namespace AsyncSql; 60 | QueryRequest request(this, "" /*no query*/, tableName(), QueryRequest::CustomOperation); 61 | 62 | // Using C++11 ... 63 | request.setCustomOperation([userName, password](QSqlDatabase db) 64 | { 65 | // db is the connection object used on the query thread 66 | QSqlQuery qry(db); 67 | 68 | // Create user and grant access to database on localhost only 69 | qry.prepare(QString("CREATE USER '%1'@'localhost' IDENTIFIED BY '%2'").arg(userName, password)); 70 | 71 | // If query fails, throw an exception that would be caught by the thread 72 | if(!qry.exec()) 73 | throw DatabaseException(qry.lastError(), tr("Failed to create user %1 on localhost.").arg(userName)); 74 | 75 | // More commands... 76 | } 77 | 78 | emit execute(request); 79 | 80 | // Connect to the AsyncSql::AsyncSqlTableModel::executed() signal to check if operation was performed successfully. 81 | 82 | 83 | The functions **AsyncSql::QueryRequest::runBefore()** and **AsyncSql::QueryRequest::runAfter()** are also provided to conveniently run commands before or after a query respectively. 84 | 85 | ## Dependencies 86 | - [Qt 5] framework 87 | 88 | ## Todo 89 | * Database transaction handling 90 | * More examples (especially one that uses **QML**) 91 | * Other databases 92 | 93 | Lastly, as I always say: 94 | 95 | **Please report all bugs. 96 | Also, I am FAR FROM perfect. If you see anything that can be done better, please notify me.** 97 | 98 | License 99 | ---- 100 | 101 | MIT 102 | 103 | [//]: # (These are reference links used in the body of this note and get stripped out when the markdown processor does its job. There is no need to format nicely because it shouldn't be seen. Thanks SO - http://stackoverflow.com/questions/4823468/store-comments-in-markdown-syntax) 104 | 105 | [Qt 5]: 106 | -------------------------------------------------------------------------------- /asyncsql/asyncmodelregister.cpp: -------------------------------------------------------------------------------- 1 | #include "asyncmodelregister.h" 2 | #include "asyncsqltablemodel.h" 3 | #include 4 | 5 | using namespace AsyncSql; 6 | 7 | AsyncModelRegister::AsyncModelRegister(QObject *parent, MarkMethod method) : 8 | method(method), 9 | totalMarked(0), 10 | QObject(parent) 11 | { 12 | } 13 | 14 | void AsyncModelRegister::addModel(AsyncSqlTableModel *model) { 15 | if(modelMap.contains(model)) 16 | return; 17 | 18 | modelMap.insert(model, false); 19 | 20 | if(method == MarkAfterSelected) 21 | connect(model, SIGNAL(selected(bool)), this, SLOT(mark())); 22 | else if(method == MarkAfterSubmitted) 23 | connect(model, SIGNAL(submitted(bool)), this, SLOT(mark())); 24 | } 25 | 26 | void AsyncModelRegister::mark() { 27 | AsyncSqlTableModel *model = qobject_cast(sender()); 28 | 29 | if(modelMap.value(model)) 30 | return; 31 | 32 | modelMap.insert(model, true); 33 | totalMarked++; 34 | 35 | if(isDone()) 36 | emit allMarked(true); 37 | } 38 | 39 | bool AsyncModelRegister::removeModel(AsyncSqlTableModel *model) { 40 | if(!modelMap.remove(model)) 41 | return false; 42 | 43 | if(totalMarked > 0) 44 | totalMarked--; 45 | 46 | disconnect(model, SIGNAL(selected(bool)), this, SLOT(mark())); 47 | disconnect(model, SIGNAL(submitted(bool)), this, SLOT(mark())); 48 | 49 | return true; 50 | } 51 | 52 | bool AsyncModelRegister::removeAllModels() { 53 | if(modelMap.isEmpty()) 54 | return false; 55 | 56 | for(auto &model : modelMap.keys()) 57 | removeModel(model); 58 | 59 | return true; 60 | } 61 | 62 | void AsyncModelRegister::clear() { 63 | for(int i = 0; i < modelMap.count(); ++i) 64 | { 65 | AsyncSqlTableModel *model = modelMap.keys().at(i); 66 | 67 | modelMap.insert(model, false); 68 | } 69 | 70 | totalMarked = 0; 71 | } 72 | 73 | QList AsyncModelRegister::models() const { 74 | return modelMap.keys(); 75 | } 76 | 77 | bool AsyncModelRegister::isDone() const { 78 | return modelMap.count() == totalMarked; 79 | } 80 | -------------------------------------------------------------------------------- /asyncsql/asyncmodelregister.h: -------------------------------------------------------------------------------- 1 | #ifndef ASYNCMODELREGISTER_H 2 | #define ASYNCMODELREGISTER_H 3 | 4 | #include 5 | #include 6 | 7 | namespace AsyncSql { 8 | class AsyncSqlTableModel; 9 | 10 | class AsyncModelRegister : public QObject 11 | { 12 | Q_OBJECT 13 | public: 14 | enum MarkMethod {MarkAfterSelected, MarkAfterSubmitted}; 15 | explicit AsyncModelRegister(QObject *parent = 0, MarkMethod method = MarkAfterSelected); 16 | 17 | void addModel(AsyncSqlTableModel *model); 18 | bool removeModel(AsyncSqlTableModel *model); 19 | bool removeAllModels(); 20 | bool isDone() const; 21 | 22 | QList models() const; 23 | signals: 24 | void allMarked(bool); 25 | public slots: 26 | void mark(); 27 | void clear(); 28 | private: 29 | QMap modelMap; 30 | MarkMethod method; 31 | int totalMarked; 32 | }; 33 | } 34 | 35 | #endif // ASYNCMODELREGISTER_H 36 | -------------------------------------------------------------------------------- /asyncsql/asyncsqltablemodel.cpp: -------------------------------------------------------------------------------- 1 | #include "asyncsqltablemodel.h" 2 | #include "queryrequest.h" 3 | #include "queryresult.h" 4 | #include "querythread.h" 5 | #include 6 | 7 | using namespace AsyncSql; 8 | 9 | AsyncSqlTableModel::AsyncSqlTableModel(QObject *parent) : 10 | limit_(-1), 11 | sortColumn_(-1), 12 | order_(Qt::AscendingOrder), 13 | selectedSignalSuppressed_(false), 14 | error_(QSqlError()), 15 | submitCalled_(false), 16 | currentRow_(-1), 17 | foreignKeyFlag_(true), 18 | busy_(false), 19 | QAbstractTableModel(parent) 20 | { 21 | connect(this, SIGNAL(execute(const QueryRequest &)), 22 | &QueryThread::instance(), SLOT(execute(const QueryRequest &))); 23 | connect(&QueryThread::instance(), SIGNAL(queryFinished(const QueryResult &)), 24 | this, SLOT(getResults(const QueryResult &))); 25 | } 26 | 27 | AsyncSqlTableModel::~AsyncSqlTableModel() 28 | { 29 | } 30 | 31 | Qt::ItemFlags AsyncSqlTableModel::flags(const QModelIndex &index) const 32 | { 33 | Qt::ItemFlags theFlags = QAbstractTableModel::flags(index); 34 | 35 | if (index.isValid()) 36 | theFlags |= Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled; 37 | 38 | return theFlags; 39 | } 40 | 41 | QVariant AsyncSqlTableModel::data(const QModelIndex &index, int role) const { 42 | if(records_.isEmpty()) 43 | return QVariant(); 44 | if (!index.isValid() || index.row() < 0 || index.row() >= records_.count() 45 | || index.column() < 0 || index.column() >= records_.first().count()) 46 | return QVariant(); 47 | 48 | // For roles 49 | if(role >= Qt::UserRole + 1) 50 | return record(index.row()).value(fieldIndex(roleNames()[role])); 51 | if (role == Qt::DisplayRole || role == Qt::EditRole) 52 | return records_.at(index.row()).value(index.column()); 53 | 54 | return QVariant(); 55 | } 56 | 57 | QVariant AsyncSqlTableModel::headerData(int section, Qt::Orientation orientation, int role) const 58 | { 59 | if (role != Qt::DisplayRole) 60 | return QVariant(); 61 | 62 | if(orientation == Qt::Horizontal) { 63 | if(records_.isEmpty()) 64 | return emptyRecord_.fieldName(section); 65 | else 66 | return QVariant(records_.first().fieldName(section)); 67 | } 68 | else { 69 | if(removedRows_.contains(section)) { 70 | //qDebug() << "The removed section is " << section; 71 | return "!"; 72 | } 73 | if(insertedRows_.contains(section)) { 74 | //qDebug() << "The inserted section is " << section; 75 | return "*"; 76 | } 77 | return section + 1; 78 | } 79 | 80 | return QVariant(); 81 | } 82 | 83 | int AsyncSqlTableModel::rowCount(const QModelIndex &parent) const { 84 | return parent.isValid() ? 0 : records_.count(); 85 | } 86 | 87 | int AsyncSqlTableModel::columnCount(const QModelIndex &parent) const { 88 | if(records_.isEmpty()) 89 | return emptyRecord_.count(); 90 | 91 | return parent.isValid() ? 0 : records_.first().count(); 92 | } 93 | 94 | bool AsyncSqlTableModel::setData(const QModelIndex &index, const QVariant &value, int role) { 95 | if(records_.isEmpty()) 96 | return false; 97 | 98 | if (!index.isValid() || role != Qt::EditRole || index.row() < 0 || index.row() >= records_.count() 99 | || index.column() < 0 || index.column() >= records_.first().count()) 100 | return false; 101 | 102 | QSqlRecord record(records_.first()); 103 | record.clearValues(); 104 | record = records_[index.row()]; 105 | record.setValue(index.column(), value); 106 | records_[index.row()] = record; 107 | 108 | if(!insertedRows_.contains(index.row()) && !removedRows_.contains(index.row()) && 109 | !updatedRecordMap_.contains(index.row())) 110 | { 111 | QSqlRecord record(emptyRecord_); 112 | for(int i = 0; i < record.count(); ++i) 113 | record.setGenerated(i, false); 114 | 115 | // Set the primary key first 116 | record.setValue(primaryIndex_.fieldName(0), 117 | index.sibling(index.row(), record.indexOf(primaryIndex_.fieldName(0))).data(Qt::EditRole)); 118 | record.setValue(index.column(), value); 119 | // By default, QSqlRecord::isGenerated() returns false. I set the flag to true if the field 120 | // was updated. 121 | record.setGenerated(index.column(), true); 122 | updatedRecordMap_.insert(index.row(), record); 123 | } 124 | else if(!insertedRows_.contains(index.row()) && !removedRows_.contains(index.row()) && 125 | updatedRecordMap_.contains(index.row())) 126 | { 127 | QSqlRecord record(updatedRecordMap_.value(index.row())); 128 | // Set the primary key first 129 | record.setValue(primaryIndex_.fieldName(0), 130 | this->index(index.row(), record.indexOf(primaryIndex_.fieldName(0))).data(Qt::EditRole)); 131 | record.setValue(index.column(), value); 132 | // By default, QSqlRecord::isGenerated() returns false. I set the flag to true if the field 133 | // was updated. 134 | record.setGenerated(index.column(), true); 135 | updatedRecordMap_.insert(index.row(), record); 136 | } 137 | 138 | emit dataChanged(index, index); 139 | return true; 140 | } 141 | 142 | bool AsyncSqlTableModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant& value, int) { 143 | return false; 144 | } 145 | 146 | bool AsyncSqlTableModel::insertRows(int row, int count, const QModelIndex&) 147 | { 148 | beginInsertRows(QModelIndex(), row, row + count - 1); 149 | for (int i = 0; i < count; ++i) { 150 | records_.insert(row, emptyRecord_); 151 | if(!insertedRows_.contains(row)) { 152 | qDebug() << "Inserted row: " << row; 153 | insertedRows_ << row; 154 | } 155 | } 156 | endInsertRows(); 157 | 158 | return true; 159 | } 160 | 161 | bool AsyncSqlTableModel::removeRows(int row, int count, const QModelIndex&) 162 | { 163 | qDebug() << "What is row? " << row << " and count " << count; 164 | if((row < 0) || (row >= records_.count()) || records_.isEmpty()) 165 | return false; 166 | 167 | if(originalRecords_.contains(records_.at(row))) { 168 | if(!removedRows_.contains(row)) { 169 | removedRows_ << row; 170 | } 171 | 172 | qDebug() << "Removed rows? " << removedRows_; 173 | insertedRows_.removeAll(row); 174 | return true; 175 | } 176 | 177 | beginRemoveRows(QModelIndex(), row, row + count - 1); 178 | for (int i = 0; i < count; ++i) 179 | records_.removeAt(row); 180 | endRemoveRows(); 181 | 182 | /* 183 | for(int i = 0; i < removedRows_.count(); ++i) { 184 | removedRows_[i] = removedRows_.at(i) - count; 185 | insertedRows_.removeAll(removedRows_.at(i)); 186 | } 187 | */ 188 | 189 | insertedRows_.removeAll(row); 190 | return true; 191 | } 192 | 193 | QModelIndexList AsyncSqlTableModel::match(const QModelIndex &start, int role, 194 | const QVariant &value, int hits, 195 | Qt::MatchFlags flags) const 196 | { 197 | switch(value.type()) { 198 | case QVariant::Int: 199 | case QVariant::Double: 200 | { 201 | int count = 0; 202 | QModelIndexList foundIndexes; 203 | for(int i = 0; i < rowCount(); ++i) { 204 | QModelIndex cell = index(i, start.column()); 205 | 206 | if(cell.data(Qt::EditRole).toDouble() == value.toDouble()) { 207 | foundIndexes << cell; 208 | count++; 209 | } 210 | 211 | if(count == hits) 212 | break; 213 | } 214 | 215 | return foundIndexes; 216 | } 217 | break; 218 | } 219 | 220 | return QAbstractTableModel::match(start, role, value, hits, flags); 221 | } 222 | 223 | void AsyncSqlTableModel::select() { 224 | QString query = QString("SELECT * FROM %1").arg(tableName_); 225 | 226 | if(!filter_.trimmed().isEmpty()) 227 | query += " WHERE " + filter_.trimmed(); 228 | 229 | if(sortColumn_ >= 0) { 230 | switch(order_) { 231 | case Qt::AscendingOrder: 232 | query += QString(" ORDER BY %1"); 233 | break; 234 | case Qt::DescendingOrder: 235 | query += QString(" ORDER BY %1 DESC"); 236 | break; 237 | default: 238 | break; 239 | } 240 | } 241 | 242 | if(limit_ >= 0) 243 | query += " LIMIT " + QString::number(limit_); 244 | 245 | if(records_.count()) 246 | removeRows(0, records_.count()); 247 | records_.clear(); 248 | originalRecords_.clear(); 249 | insertedRows_.clear(); 250 | removedRows_.clear(); 251 | updatedRecordMap_.clear(); 252 | originalInsertedRows_.clear(); 253 | originalRemovedRows_.clear(); 254 | originalUpdatedRecordMap_.clear(); 255 | error_ = QSqlError(); 256 | 257 | setBusy(true); 258 | 259 | QueryRequest request(this, query, tableName_, QueryRequest::Select); 260 | request.setSortColumn(sortColumn_); 261 | emit execute(request); 262 | } 263 | 264 | bool AsyncSqlTableModel::getResults(const QueryResult &result) { 265 | if(static_cast(result.getReceiver()) != this) 266 | return false; 267 | 268 | setBusy(false); 269 | 270 | if(error_.isValid()) 271 | return false; 272 | 273 | if(result.getError().isValid()) { 274 | error_ = result.getError(); 275 | 276 | if(result.getRequestType() == QueryRequest::Select) 277 | emit selected(false); 278 | else if(result.getRequestType() == QueryRequest::CustomOperation) 279 | emit executed(false); 280 | else 281 | emit submitted(false); 282 | 283 | //qDebug() << "AsyncSqlTableModel: Error discovered."; 284 | submitCalled_ = false; 285 | 286 | return false; 287 | } 288 | 289 | switch(result.getRequestType()) { 290 | case QueryRequest::Select: 291 | { 292 | beginResetModel(); 293 | records_ = result.getRecords(); 294 | originalRecords_ = result.getRecords(); 295 | primaryIndex_ = result.getPrimaryIndex(); 296 | lastRecord_ = result.getLastRecord(); 297 | 298 | // Get empty record 299 | emptyRecord_ = result.getRecord(); 300 | 301 | if(!selectedSignalSuppressed_) 302 | emit selected(true); 303 | endResetModel(); 304 | } 305 | break; 306 | case QueryRequest::Insert: 307 | insertedRows_.clear(); 308 | break; 309 | case QueryRequest::Update: 310 | updatedRecordMap_.clear(); 311 | break; 312 | case QueryRequest::Delete: 313 | removedRows_.clear(); 314 | break; 315 | case QueryRequest::BeginTransaction: 316 | break; 317 | case QueryRequest::CommitTransaction: 318 | break; 319 | case QueryRequest::CustomOperation: 320 | emit executed(true); 321 | break; 322 | } 323 | 324 | if(!insertedRows_.count() && !updatedRecordMap_.count() && !removedRows_.count() && submitCalled_) { 325 | submitCalled_ = false; 326 | emit submitted(true); 327 | } 328 | 329 | return true; 330 | } 331 | 332 | void AsyncSqlTableModel::setFilter(const QString &filter) { 333 | filter_ = filter; 334 | } 335 | 336 | QString AsyncSqlTableModel::filter() const { 337 | return filter_; 338 | } 339 | 340 | void AsyncSqlTableModel::setSort(int column, Qt::SortOrder order) { 341 | sortColumn_ = column; 342 | order_ = order; 343 | } 344 | 345 | void AsyncSqlTableModel::setTable(const QString &tableName) { 346 | tableName_ = tableName; 347 | } 348 | 349 | QString AsyncSqlTableModel::tableName() const { 350 | return tableName_; 351 | } 352 | 353 | bool AsyncSqlTableModel::isDirty() const { 354 | return !insertedRows_.isEmpty() || !updatedRecordMap_.isEmpty() || !removedRows_.isEmpty() ; 355 | } 356 | 357 | bool AsyncSqlTableModel::isDirty(const QModelIndex &index) const { 358 | // Check if the insert rows or removed rows contains the index's row, or the 359 | // updated record map contains a field that is not generated i.e. that has been edited. 360 | return insertedRows_.contains(index.row()) 361 | || !updatedRecordMap_.value(index.row()).field(index.column()).isGenerated() 362 | || removedRows_.contains(index.row()); 363 | } 364 | 365 | QList AsyncSqlTableModel::insertedRecords() const { 366 | QList records; 367 | 368 | for(auto &row : insertedRows_) 369 | records << records_.at(row); 370 | 371 | return records; 372 | } 373 | 374 | QMap AsyncSqlTableModel::updatedRecords() const { 375 | return updatedRecordMap_; 376 | } 377 | 378 | QList AsyncSqlTableModel::removedRecords() const { 379 | QList records; 380 | 381 | for(auto &row : removedRows_) 382 | records << records_.at(row); 383 | 384 | return records; 385 | } 386 | 387 | void AsyncSqlTableModel::setLastError(const QSqlError &e) { 388 | error_ = e; 389 | } 390 | 391 | QSqlError AsyncSqlTableModel::lastError() const { 392 | return error_; 393 | } 394 | 395 | void AsyncSqlTableModel::setForeignKeyFlag(bool flag) 396 | { 397 | foreignKeyFlag_ = flag; 398 | } 399 | 400 | bool AsyncSqlTableModel::foreignKeyFlag() const 401 | { 402 | return foreignKeyFlag_; 403 | } 404 | 405 | void AsyncSqlTableModel::setCurrentRow(int row) 406 | { 407 | if(currentRow_ == row) 408 | return; 409 | 410 | currentRow_ = row; 411 | emit currentRowChanged(row); 412 | } 413 | 414 | int AsyncSqlTableModel::currentRow() const 415 | { 416 | return currentRow_; 417 | } 418 | 419 | QVariant AsyncSqlTableModel::field(const QString &columnName) const 420 | { 421 | return data(index(currentRow_, fieldIndex(columnName)), Qt::DisplayRole); 422 | } 423 | 424 | void AsyncSqlTableModel::beginTransaction() { 425 | QueryRequest request(this, "", tableName_, QueryRequest::BeginTransaction); 426 | emit execute(request); 427 | } 428 | 429 | void AsyncSqlTableModel::commitTransaction() { 430 | QueryRequest request(this, "", tableName_, QueryRequest::CommitTransaction); 431 | emit execute(request); 432 | } 433 | 434 | bool AsyncSqlTableModel::validateModel() { 435 | return true; 436 | } 437 | 438 | // WARNING: This function would only work if the primary key is not changed!!! 439 | void AsyncSqlTableModel::submitAll() { 440 | if(insertedRows_.isEmpty() && updatedRecordMap_.isEmpty() && removedRows_.isEmpty()) { 441 | emit submitted(true); 442 | return; 443 | } 444 | if(!validateModel()) 445 | return; 446 | if(submitCalled_) 447 | return; 448 | 449 | // if(transactionState == -1) { 450 | // insertedRows_ = originalInsertedRows_; 451 | // updatedRecordMap_ = originalUpdatedRecordMap_; 452 | // removedRows_ = originalRemovedRows_; 453 | // } 454 | // else { 455 | originalUpdatedRecordMap_ = updatedRecordMap_; 456 | originalInsertedRows_ = insertedRows_; 457 | originalRemovedRows_ = removedRows_; 458 | //} 459 | 460 | qDebug() << "For " << tableName_ << ":"; 461 | qDebug() << "What are the updated rows? " << updatedRecordMap_.keys(); 462 | qDebug() << "What are the inserted rows? " << insertedRows_; 463 | qDebug() << "What are the removed rows? " << removedRows_; 464 | qDebug() << "-----------------------------------------------------------"; 465 | 466 | error_ = QSqlError(); 467 | 468 | QueryRequest request(this); 469 | request.setTableName(tableName_); 470 | request.setPrimaryIndex(primaryIndex_); 471 | QList records; 472 | 473 | qDebug() << "Primary index? " << primaryIndex_.fieldName(0) << ", auto value: " 474 | << primaryIndex_.field(0).isAutoValue(); 475 | 476 | // Update records 477 | if(!updatedRecordMap_.isEmpty()) { 478 | request.setRequestType(QueryRequest::Update); 479 | records = updatedRecordMap_.values(); 480 | request.setRecords(records); 481 | emit execute(request); 482 | } 483 | 484 | 485 | // Insert records 486 | if(!insertedRows_.isEmpty()) { 487 | request.setRequestType(QueryRequest::Insert); 488 | records.clear(); 489 | for(int i = 0; i < insertedRows_.count(); ++i) { 490 | qDebug() << "Inserted records: " << this->records_.at(insertedRows_.at(i)); 491 | records << this->records_.at(insertedRows_.at(i)); 492 | } 493 | request.setRecords(records); 494 | emit execute(request); 495 | records.clear(); 496 | } 497 | 498 | 499 | // Delete records 500 | if(!removedRows_.isEmpty()) { 501 | request.setRequestType(QueryRequest::Delete); 502 | for(int i = 0; i < removedRows_.count(); ++i) 503 | records << this->records_.at(removedRows_.at(i)); 504 | request.setRecords(records); 505 | emit execute(request); 506 | records.clear(); 507 | } 508 | 509 | submitCalled_ = true; 510 | } 511 | 512 | void AsyncSqlTableModel::revertAll() { 513 | beginResetModel(); 514 | records_ = originalRecords_; 515 | insertedRows_.clear(); 516 | updatedRecordMap_.clear(); 517 | removedRows_.clear(); 518 | endResetModel(); 519 | } 520 | 521 | QSqlRecord AsyncSqlTableModel::record() const { 522 | return emptyRecord_; 523 | } 524 | 525 | QSqlRecord AsyncSqlTableModel::record(int row) const { 526 | if((row < 0) || (row >= records_.count())) 527 | return emptyRecord_; 528 | 529 | return records_.at(row); 530 | } 531 | 532 | bool AsyncSqlTableModel::appendRecord(const QSqlRecord &record) { 533 | if(record.count() != this->record().count()) 534 | return false; 535 | 536 | bool inserted = insertRow(rowCount()); 537 | int row = rowCount() - 1; 538 | 539 | for(int i = 0; i < record.count(); ++i) { 540 | QModelIndex cell = index(row, i); 541 | setData(cell, record.value(i)); 542 | } 543 | 544 | return inserted; 545 | } 546 | 547 | void AsyncSqlTableModel::setSelectedSignalSuppressed(bool s) { 548 | this->selectedSignalSuppressed_ = s; 549 | } 550 | 551 | bool AsyncSqlTableModel::isSelectedSignalSuppressed() const { 552 | return selectedSignalSuppressed_; 553 | } 554 | 555 | void AsyncSqlTableModel::setRecords(const QList &records) { 556 | this->records_ = records; 557 | } 558 | 559 | void AsyncSqlTableModel::setOriginalRecords(const QList &records) { 560 | this->records_ = records; 561 | } 562 | 563 | QList AsyncSqlTableModel::records() const { 564 | return records_; 565 | } 566 | 567 | QList AsyncSqlTableModel::originalRecords() const { 568 | return originalRecords_; 569 | } 570 | 571 | QSqlRecord AsyncSqlTableModel::lastRecord() const { 572 | return lastRecord_; 573 | } 574 | 575 | void AsyncSqlTableModel::setBusy(bool b) 576 | { 577 | if(busy_ == b) 578 | return; 579 | 580 | busy_ = b; 581 | emit busyChanged(b); 582 | } 583 | 584 | void AsyncSqlTableModel::setLimit(int limit) { 585 | limit_ = limit; 586 | } 587 | 588 | int AsyncSqlTableModel::limit() const { 589 | return limit_; 590 | } 591 | 592 | int AsyncSqlTableModel::fieldIndex(const QString &fieldName) const { 593 | if(fieldName.trimmed().isEmpty()) 594 | return -1; 595 | 596 | return record().indexOf(fieldName); 597 | } 598 | 599 | QList AsyncSqlTableModel::insertedRows() const { 600 | return insertedRows_; 601 | } 602 | 603 | QSqlIndex AsyncSqlTableModel::primaryIndex() const 604 | { 605 | return primaryIndex_; 606 | } 607 | 608 | bool AsyncSqlTableModel::setRecord(int row, const QSqlRecord &record) { 609 | if((row < 0) || (row >= rowCount())) 610 | return false; 611 | if(record.count() != emptyRecord_.count()) 612 | return false; 613 | 614 | for(int column = 0; column < record.count(); ++column) { 615 | QModelIndex cell = index(row, column); 616 | setData(cell, record.value(column)); 617 | } 618 | 619 | return true; 620 | } 621 | 622 | void AsyncSqlTableModel::customInsert(const QVariantMap &values) 623 | { 624 | if(values.isEmpty()) 625 | return; 626 | } 627 | 628 | void AsyncSqlTableModel::customUpdate(const QVariantMap &values) 629 | { 630 | if(values.isEmpty()) 631 | return; 632 | } 633 | 634 | bool AsyncSqlTableModel::isBusy() const 635 | { 636 | return busy_; 637 | } 638 | -------------------------------------------------------------------------------- /asyncsql/asyncsqltablemodel.h: -------------------------------------------------------------------------------- 1 | #ifndef ASYNCSQLTABLEMODEL_H 2 | #define ASYNCSQLTABLEMODEL_H 3 | 4 | #include 5 | #include 6 | #include "queryrequest.h" 7 | #include "queryresult.h" 8 | 9 | namespace AsyncSql { 10 | class AsyncSqlTableModel : public QAbstractTableModel 11 | { 12 | Q_OBJECT 13 | Q_PROPERTY(int currentRow READ currentRow WRITE setCurrentRow NOTIFY currentRowChanged) 14 | public: 15 | explicit AsyncSqlTableModel(QObject *parent = nullptr); 16 | virtual ~AsyncSqlTableModel(); 17 | 18 | void beginTransaction(); 19 | void commitTransaction(); 20 | 21 | virtual Qt::ItemFlags flags(const QModelIndex &index) const; 22 | virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; 23 | virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; 24 | virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; 25 | virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; 26 | virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); 27 | virtual bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int = Qt::EditRole); 28 | virtual bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()); 29 | virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); 30 | virtual QModelIndexList match(const QModelIndex & start, int role, const QVariant & value, 31 | int hits = 1, 32 | Qt::MatchFlags flags = Qt::MatchFlags( Qt::MatchStartsWith | Qt::MatchWrap )) const; 33 | 34 | int fieldIndex(const QString &) const; 35 | 36 | virtual void setFilter(const QString &); 37 | QString filter() const; 38 | virtual void setSort(int column, Qt::SortOrder); 39 | void setTable(const QString &); 40 | QString tableName() const; 41 | 42 | bool isDirty() const; 43 | bool isDirty(const QModelIndex &index) const; 44 | 45 | virtual QSqlRecord record() const; 46 | QSqlRecord record(int row) const; 47 | bool setRecord(int row, const QSqlRecord &); 48 | bool appendRecord(const QSqlRecord &); 49 | 50 | QList insertedRecords() const; 51 | QMap updatedRecords() const; 52 | QList removedRecords() const; 53 | 54 | virtual void setLimit(int); 55 | int limit() const; 56 | 57 | QSqlError lastError() const; 58 | QSqlRecord lastRecord() const; 59 | 60 | void setForeignKeyFlag(bool); 61 | bool foreignKeyFlag() const; 62 | 63 | void setCurrentRow(int); // For use with QML 64 | int currentRow() const; 65 | 66 | // Methods that are intended for use with QML 67 | Q_INVOKABLE virtual QVariant field(const QString &columnName) const; 68 | Q_INVOKABLE virtual void customInsert(const QVariantMap &); 69 | Q_INVOKABLE virtual void customUpdate(const QVariantMap &); 70 | 71 | bool isBusy() const; 72 | protected: 73 | void setRecords(const QList &); 74 | void setOriginalRecords(const QList &); 75 | QList records() const; 76 | QList originalRecords() const; 77 | 78 | void setSelectedSignalSuppressed(bool); 79 | bool isSelectedSignalSuppressed() const; 80 | virtual bool validateModel(); 81 | QList insertedRows() const; 82 | QSqlIndex primaryIndex() const; 83 | 84 | void setLastError(const QSqlError &); 85 | void setBusy(bool); 86 | signals: 87 | void execute(const QueryRequest &); 88 | void selected(bool successful); 89 | void submitted(bool successful); 90 | void executed(bool successful); // For custom operations 91 | void busyChanged(bool); 92 | 93 | void currentRowChanged(int); // For use with QML 94 | protected slots: 95 | virtual bool getResults(const QueryResult &); 96 | public slots: 97 | virtual void select(); 98 | virtual void submitAll(); 99 | void revertAll(); 100 | private: 101 | QString filter_; 102 | QString tableName_; 103 | Qt::SortOrder order_; 104 | int sortColumn_; 105 | int limit_; 106 | QSqlIndex primaryIndex_; 107 | QList records_, originalRecords_; 108 | QMap updatedRecordMap_, originalUpdatedRecordMap_; 109 | QList insertedRows_, originalInsertedRows_; 110 | QList removedRows_, originalRemovedRows_; 111 | QSqlRecord emptyRecord_; 112 | bool submitCalled_; 113 | QSqlError error_; 114 | QSqlRecord lastRecord_; 115 | bool foreignKeyFlag_; 116 | 117 | bool selectedSignalSuppressed_; 118 | 119 | int currentRow_; // For use with QML 120 | bool busy_; 121 | }; 122 | } 123 | 124 | #endif // ASYNCSQLTABLEMODEL_H 125 | 126 | -------------------------------------------------------------------------------- /asyncsql/databaseconnection.cpp: -------------------------------------------------------------------------------- 1 | #include "databaseconnection.h" 2 | #include 3 | 4 | using namespace AsyncSql; 5 | 6 | QString DatabaseConnection::defaultHostName = ""; 7 | int DatabaseConnection::defaultPort = 3306; 8 | 9 | QString DatabaseConnection::defaultUserName = ""; 10 | QString DatabaseConnection::defaultPassword = ""; 11 | QString DatabaseConnection::defaultDriverName = ""; 12 | QString DatabaseConnection::defaultDatabaseName = ""; 13 | QString DatabaseConnection::defaultConnectOptions = ""; 14 | DatabaseConnection::Driver DatabaseConnection::defaultDriver = DatabaseConnection::Unknown; 15 | 16 | DatabaseConnection::DatabaseConnection() : 17 | port(0) 18 | { 19 | setDriver(Unknown); 20 | } 21 | 22 | DatabaseConnection::~DatabaseConnection() 23 | { 24 | 25 | } 26 | 27 | void DatabaseConnection::setHostName(const QString &hostName) 28 | { 29 | this->hostName = hostName; 30 | } 31 | 32 | QString DatabaseConnection::getHostName() const 33 | { 34 | return hostName; 35 | } 36 | 37 | void DatabaseConnection::setPort(int port) 38 | { 39 | this->port = port; 40 | } 41 | 42 | int DatabaseConnection::getPort() const 43 | { 44 | return port; 45 | } 46 | 47 | void DatabaseConnection::setUserName(const QString &userName) 48 | { 49 | this->userName = userName; 50 | } 51 | 52 | QString DatabaseConnection::getUserName() const 53 | { 54 | return userName; 55 | } 56 | 57 | void DatabaseConnection::setPassword(const QString &password) 58 | { 59 | this->password = password; 60 | } 61 | 62 | QString DatabaseConnection::getPassword() const 63 | { 64 | return password; 65 | } 66 | 67 | void DatabaseConnection::setDriver(DatabaseConnection::Driver driver) 68 | { 69 | this->driver = driver; 70 | 71 | switch(driver) { 72 | case SQLite: 73 | driverName = "QSQLITE"; 74 | break; 75 | case MySQL: 76 | driverName = "QMYSQL"; 77 | break; 78 | } 79 | } 80 | 81 | DatabaseConnection::Driver DatabaseConnection::getDriver() const 82 | { 83 | return driver; 84 | } 85 | 86 | QString DatabaseConnection::getDriverName() const 87 | { 88 | return driverName; 89 | } 90 | 91 | void DatabaseConnection::setDatabaseName(const QString &databaseName) 92 | { 93 | this->databaseName = databaseName; 94 | } 95 | 96 | QString DatabaseConnection::getDatabaseName() const 97 | { 98 | return databaseName; 99 | } 100 | 101 | void DatabaseConnection::setConnectOptions(const QString &options) 102 | { 103 | this->connectOptions = options; 104 | } 105 | 106 | QString DatabaseConnection::getConnectOptions() const 107 | { 108 | return connectOptions; 109 | } 110 | 111 | void DatabaseConnection::setDefaultHostName(const QString &hostName) 112 | { 113 | DatabaseConnection::defaultHostName = hostName; 114 | } 115 | 116 | QString DatabaseConnection::getDefaultHostName() 117 | { 118 | return defaultHostName; 119 | } 120 | 121 | void DatabaseConnection::setDefaultPort(int port) 122 | { 123 | DatabaseConnection::defaultPort = port; 124 | } 125 | 126 | int DatabaseConnection::getDefaultPort() 127 | { 128 | return defaultPort; 129 | } 130 | 131 | void DatabaseConnection::setDefaultUserName(const QString &userName) 132 | { 133 | DatabaseConnection::defaultUserName = userName; 134 | } 135 | 136 | QString DatabaseConnection::getDefaultUserName() 137 | { 138 | return defaultUserName; 139 | } 140 | 141 | void DatabaseConnection::setDefaultPassword(const QString &password) 142 | { 143 | DatabaseConnection::defaultPassword = password; 144 | } 145 | 146 | QString DatabaseConnection::getDefaultPassword() 147 | { 148 | return defaultPassword; 149 | } 150 | 151 | void DatabaseConnection::setDefaultDriver(DatabaseConnection::Driver driver) 152 | { 153 | DatabaseConnection::defaultDriver = driver; 154 | 155 | switch(driver) { 156 | case SQLite: 157 | defaultDriverName = "QSQLITE"; 158 | break; 159 | case MySQL: 160 | defaultDriverName = "QMYSQL"; 161 | break; 162 | default: 163 | defaultDriverName = ""; 164 | break; 165 | } 166 | } 167 | 168 | DatabaseConnection::Driver DatabaseConnection::getDefaultDriver() 169 | { 170 | return defaultDriver; 171 | } 172 | 173 | QString DatabaseConnection::getDefaultDriverName() 174 | { 175 | return defaultDriverName; 176 | } 177 | 178 | void DatabaseConnection::setDefaultDatabaseName(const QString &databaseName) 179 | { 180 | DatabaseConnection::defaultDatabaseName = databaseName; 181 | } 182 | 183 | QString DatabaseConnection::getDefaultDatabaseName() 184 | { 185 | return defaultDatabaseName; 186 | } 187 | 188 | void DatabaseConnection::setDefaultConnectOptions(const QString &options) 189 | { 190 | DatabaseConnection::defaultConnectOptions = options; 191 | } 192 | 193 | QString DatabaseConnection::getDefaultConnectOptions() 194 | { 195 | return defaultConnectOptions; 196 | } 197 | -------------------------------------------------------------------------------- /asyncsql/databaseconnection.h: -------------------------------------------------------------------------------- 1 | #ifndef DATABASECONNECTION_H 2 | #define DATABASECONNECTION_H 3 | 4 | #include 5 | 6 | namespace AsyncSql { 7 | class DatabaseConnection 8 | { 9 | public: 10 | enum Driver {Unknown, SQLite, MySQL}; 11 | explicit DatabaseConnection(); 12 | ~DatabaseConnection(); 13 | 14 | void setHostName(const QString &hostName); 15 | QString getHostName() const; 16 | 17 | void setPort(int port); 18 | int getPort() const; 19 | 20 | void setUserName(const QString &userName); 21 | QString getUserName() const; 22 | 23 | void setPassword(const QString &password); 24 | QString getPassword() const; 25 | 26 | void setDriver(Driver); 27 | Driver getDriver() const; 28 | QString getDriverName() const; 29 | 30 | void setDatabaseName(const QString &databaseName); 31 | QString getDatabaseName() const; 32 | 33 | void setConnectOptions(const QString &options); 34 | QString getConnectOptions() const; 35 | 36 | 37 | // Default values 38 | static void setDefaultHostName(const QString &hostName); 39 | static QString getDefaultHostName(); 40 | 41 | static void setDefaultPort(int port); 42 | static int getDefaultPort(); 43 | 44 | static void setDefaultUserName(const QString &userName); 45 | static QString getDefaultUserName(); 46 | 47 | static void setDefaultPassword(const QString &password); 48 | static QString getDefaultPassword(); 49 | 50 | static void setDefaultDriver(Driver); 51 | static Driver getDefaultDriver(); 52 | static QString getDefaultDriverName(); 53 | 54 | static void setDefaultDatabaseName(const QString &databaseName); 55 | static QString getDefaultDatabaseName(); 56 | 57 | static void setDefaultConnectOptions(const QString &options); 58 | static QString getDefaultConnectOptions(); 59 | private: 60 | QString userName; 61 | QString password; 62 | QString hostName; 63 | int port; 64 | QString driverName; 65 | QString databaseName; 66 | QString connectOptions; 67 | Driver driver; 68 | 69 | static QString defaultUserName; 70 | static QString defaultPassword; 71 | static QString defaultHostName; 72 | static int defaultPort; 73 | static QString defaultDriverName; 74 | static QString defaultDatabaseName; 75 | static QString defaultConnectOptions; 76 | static Driver defaultDriver; 77 | }; 78 | } 79 | 80 | #endif // DATABASECONNECTION_H 81 | -------------------------------------------------------------------------------- /asyncsql/databaseexception.cpp: -------------------------------------------------------------------------------- 1 | #include "databaseexception.h" 2 | -------------------------------------------------------------------------------- /asyncsql/databaseexception.h: -------------------------------------------------------------------------------- 1 | #ifndef DATABASEEXCEPTION_H 2 | #define DATABASEEXCEPTION_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace AsyncSql { 9 | class DatabaseException : std::exception 10 | { 11 | public: 12 | enum ErrorCode {FailedToCommit = 10000, DuplicateEntry}; 13 | DatabaseException(const QSqlError &e, const QString userMsg = "") : 14 | error(e), 15 | code(e.number()), 16 | msg(e.text()) 17 | { 18 | if(userMsg.isEmpty()) 19 | this->userMsg = msg; 20 | } 21 | 22 | const char *what() const throw() { 23 | return QString(QStringLiteral("Error ") + QString::number(code) + QStringLiteral(": ") + msg) 24 | .toStdString().c_str(); 25 | } 26 | 27 | QSqlError getError() const { return error; } 28 | ~DatabaseException() throw() {} 29 | private: 30 | int code; 31 | QString msg; 32 | QString userMsg; 33 | QSqlError error; 34 | }; 35 | } 36 | 37 | #endif // DATABASEEXCEPTION_H 38 | -------------------------------------------------------------------------------- /asyncsql/queryrequest.cpp: -------------------------------------------------------------------------------- 1 | #include "queryrequest.h" 2 | #include 3 | #include 4 | 5 | using namespace AsyncSql; 6 | 7 | QueryRequest::QueryRequest(QObject *receiver, const QString &query, const QString &tableName, RequestType type) : 8 | receiver(receiver), 9 | query(query), 10 | tableName(tableName), 11 | sortColumn(-1), 12 | type(type) 13 | { 14 | setRunBefore([](QSqlDatabase){}); 15 | setCustomOperation([](QSqlDatabase){}); 16 | setRunAfter([](QSqlDatabase){}); 17 | } 18 | 19 | void QueryRequest::setReceiver(QObject *receiver) { 20 | this->receiver = receiver; 21 | } 22 | 23 | QObject* QueryRequest::getReceiver() const { 24 | return receiver; 25 | } 26 | 27 | void QueryRequest::setTableName(const QString &tableName) { 28 | this->tableName = tableName; 29 | } 30 | 31 | QString QueryRequest::getTableName() const { 32 | return tableName; 33 | } 34 | 35 | void QueryRequest::setQuery(const QString &query) { 36 | this->query = query; 37 | } 38 | 39 | QString QueryRequest::getQuery() const { 40 | return query; 41 | } 42 | 43 | void QueryRequest::setRequestType(RequestType type) { 44 | this->type = type; 45 | } 46 | 47 | QueryRequest::RequestType QueryRequest::getRequestType() const { 48 | return type; 49 | } 50 | 51 | void QueryRequest::setRecords(const QList &records) { 52 | this->records = records; 53 | } 54 | 55 | QList QueryRequest::getRecords() const { 56 | return records; 57 | } 58 | 59 | void QueryRequest::setRecord(const QSqlRecord &record) { 60 | this->records.clear(); 61 | this->records << record; 62 | } 63 | 64 | void QueryRequest::setPrimaryIndex(const QSqlIndex &index) { 65 | primaryIndex = index; 66 | } 67 | 68 | QSqlIndex QueryRequest::getPrimaryIndex() const { 69 | return primaryIndex; 70 | } 71 | 72 | void QueryRequest::setSortColumn(int column) { 73 | sortColumn = column; 74 | } 75 | 76 | int QueryRequest::getSortColumn() const { 77 | return sortColumn; 78 | } 79 | 80 | void QueryRequest::setRunBefore(const std::function &process) 81 | { 82 | runBefore = process; 83 | } 84 | 85 | std::function QueryRequest::getRunBefore() const 86 | { 87 | return runBefore; 88 | } 89 | 90 | void QueryRequest::setCustomOperation(const std::function &process) 91 | { 92 | customOperation = process; 93 | } 94 | 95 | std::function QueryRequest::getCustomOperation() const 96 | { 97 | return customOperation; 98 | } 99 | 100 | void QueryRequest::setConnection(const DatabaseConnection &connection) 101 | { 102 | this->connection = connection; 103 | } 104 | 105 | DatabaseConnection QueryRequest::getConnection() const 106 | { 107 | return connection; 108 | } 109 | 110 | void QueryRequest::setRunAfter(const std::function &process) 111 | { 112 | runAfter = process; 113 | } 114 | 115 | std::function QueryRequest::getRunAfter() const 116 | { 117 | return runAfter; 118 | } 119 | -------------------------------------------------------------------------------- /asyncsql/queryrequest.h: -------------------------------------------------------------------------------- 1 | #ifndef QUERYREQUEST_H 2 | #define QUERYREQUEST_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "databaseconnection.h" 9 | #include 10 | 11 | #include 12 | 13 | namespace AsyncSql { 14 | class QueryRequest 15 | { 16 | public: 17 | enum RequestType {None = -1, Select, Insert, Update, Delete, 18 | BeginTransaction, CommitTransaction, Command, 19 | CustomOperation}; 20 | explicit QueryRequest(QObject *receiver = 0, const QString &query = "", const QString &tableName = "", 21 | RequestType type = Select); 22 | 23 | void setReceiver(QObject *); 24 | QObject* getReceiver() const; 25 | 26 | void setTableName(const QString &); 27 | QString getTableName() const; 28 | 29 | void setQuery(const QString &); 30 | QString getQuery() const; 31 | 32 | void setRequestType(RequestType); 33 | RequestType getRequestType() const; 34 | 35 | void setRecords(const QList &records); 36 | QList getRecords() const; 37 | 38 | void setRecord(const QSqlRecord &record); 39 | 40 | void setPrimaryIndex(const QSqlIndex &); 41 | QSqlIndex getPrimaryIndex() const; 42 | 43 | void setSortColumn(int); 44 | int getSortColumn() const; 45 | 46 | void setRunBefore(const std::function &process); 47 | std::function getRunBefore() const; 48 | 49 | void setRunAfter(const std::function &process); 50 | std::function getRunAfter() const; 51 | 52 | void setCustomOperation(const std::function &process); 53 | std::function getCustomOperation() const; 54 | 55 | void setConnection(const DatabaseConnection &); 56 | DatabaseConnection getConnection() const; 57 | private: 58 | QObject *receiver; 59 | QString tableName; 60 | QString query; 61 | RequestType type; 62 | QList records; 63 | QSqlIndex primaryIndex; 64 | QVariant primaryValue; 65 | int sortColumn; 66 | DatabaseConnection connection; 67 | 68 | std::function runBefore; 69 | std::function customOperation; 70 | std::function runAfter; 71 | }; 72 | } 73 | 74 | #endif // QUERYREQUEST_H 75 | -------------------------------------------------------------------------------- /asyncsql/queryresult.cpp: -------------------------------------------------------------------------------- 1 | #include "queryresult.h" 2 | #include "queryrequest.h" 3 | 4 | using namespace AsyncSql; 5 | 6 | QueryResult::QueryResult(QObject *receiver, const QList &records) : 7 | receiver(receiver), 8 | requestType(QueryRequest::None), 9 | successful(false), 10 | records(records) 11 | { 12 | } 13 | 14 | QueryResult::QueryResult(QObject *receiver, QueryRequest::RequestType type, const QSqlError &error) : 15 | receiver(receiver), 16 | requestType(type), 17 | successful(false), 18 | error(error) 19 | { 20 | } 21 | 22 | void QueryResult::setRequestType(QueryRequest::RequestType type) 23 | { 24 | requestType = type; 25 | } 26 | 27 | QueryRequest::RequestType QueryResult::getRequestType() const 28 | { 29 | return requestType; 30 | } 31 | 32 | void QueryResult::setRecords(const QList &records) { 33 | this->records = records; 34 | } 35 | 36 | QList QueryResult::getRecords() const { 37 | return records; 38 | } 39 | 40 | void QueryResult::setPrimaryIndex(const QSqlIndex &index) { 41 | primaryIndex = index; 42 | } 43 | 44 | QSqlIndex QueryResult::getPrimaryIndex() const { 45 | return primaryIndex; 46 | } 47 | 48 | void QueryResult::setRecord(const QSqlRecord &record) { 49 | this->record = record; 50 | this->record.clearValues(); 51 | } 52 | 53 | QSqlRecord QueryResult::getRecord() const { 54 | return record; 55 | } 56 | 57 | void QueryResult::setError(const QSqlError &error) { 58 | this->error = error; 59 | } 60 | 61 | QSqlError QueryResult::getError() const { 62 | return error; 63 | } 64 | 65 | void QueryResult::setReceiver(QObject *receiver) { 66 | this->receiver = receiver; 67 | } 68 | 69 | QObject *QueryResult::getReceiver() const { 70 | return receiver; 71 | } 72 | 73 | void QueryResult::setObjectName(const QString &name) 74 | { 75 | objectName = name; 76 | } 77 | 78 | QString QueryResult::getObjectName() const 79 | { 80 | return objectName; 81 | } 82 | 83 | void QueryResult::clear() { 84 | receiver = nullptr; 85 | requestType = QueryRequest::None; 86 | objectName = ""; 87 | records.clear(); 88 | record.clear(); 89 | primaryIndex.clear(); 90 | error = QSqlError(); 91 | lastRecord.clear(); 92 | successful = false; 93 | statusMsg = ""; 94 | } 95 | 96 | void QueryResult::setStatus(bool successful, const QString &msg) { 97 | this->successful = successful; 98 | 99 | if(msg.trimmed().isEmpty()) 100 | statusMsg = successful ? "SUCCEEDED" : "FAILED"; 101 | } 102 | 103 | bool QueryResult::isSuccessful() const 104 | { 105 | return successful; 106 | } 107 | 108 | QString QueryResult::getStatusMsg() { 109 | return statusMsg; 110 | } 111 | 112 | void QueryResult::setLastRecord(const QSqlRecord &r) { 113 | lastRecord = r; 114 | } 115 | 116 | QSqlRecord QueryResult::getLastRecord() const { 117 | return lastRecord; 118 | } 119 | 120 | void QueryResult::setLastInsertId(QVariant id) 121 | { 122 | lastInsertId = id; 123 | } 124 | 125 | QVariant QueryResult::getLastInsertId() const 126 | { 127 | return lastInsertId; 128 | } 129 | -------------------------------------------------------------------------------- /asyncsql/queryresult.h: -------------------------------------------------------------------------------- 1 | #ifndef QUERYRESULT_H 2 | #define QUERYRESULT_H 3 | 4 | #include 5 | #include 6 | #include "queryrequest.h" 7 | 8 | namespace AsyncSql { 9 | class QueryResult 10 | { 11 | public: 12 | explicit QueryResult(QObject *receiver = 0, const QList & = QList()); 13 | explicit QueryResult(QObject *receiver, QueryRequest::RequestType type, const QSqlError &); 14 | 15 | void clear(); 16 | 17 | void setReceiver(QObject *); 18 | QObject *getReceiver() const; 19 | 20 | void setRequestType(QueryRequest::RequestType); 21 | QueryRequest::RequestType getRequestType() const; 22 | 23 | void setObjectName(const QString &); 24 | QString getObjectName() const; 25 | 26 | void setRecord(const QSqlRecord &); 27 | QSqlRecord getRecord() const; 28 | void setRecords(const QList &records); 29 | QList getRecords() const; 30 | 31 | void setPrimaryIndex(const QSqlIndex &); 32 | QSqlIndex getPrimaryIndex() const; 33 | 34 | void setError(const QSqlError &); 35 | QSqlError getError() const; 36 | 37 | void setLastRecord(const QSqlRecord &); 38 | QSqlRecord getLastRecord() const; 39 | 40 | void setLastInsertId(QVariant id); 41 | QVariant getLastInsertId() const; 42 | 43 | void setStatus(bool successful, const QString &msg = ""); 44 | bool isSuccessful() const; 45 | QString getStatusMsg(); 46 | private: 47 | QueryRequest::RequestType requestType; 48 | QObject *receiver; 49 | QList records; 50 | QSqlRecord record; 51 | QSqlIndex primaryIndex; 52 | QSqlError error; 53 | bool successful; 54 | QString statusMsg; 55 | QSqlRecord lastRecord; 56 | QString objectName; 57 | QVariant lastInsertId; 58 | }; 59 | } 60 | 61 | #endif // QUERYRESULT_H 62 | -------------------------------------------------------------------------------- /asyncsql/querythread.cpp: -------------------------------------------------------------------------------- 1 | #include "querythread.h" 2 | #include "queryworker.h" 3 | #include 4 | #include "queryrequest.h" 5 | #include "queryresult.h" 6 | #include 7 | 8 | using namespace AsyncSql; 9 | 10 | QueryThread *QueryThread::this_thread = nullptr; 11 | QueryThread::TransactionState QueryThread::transactionState = QueryThread::Unset; 12 | 13 | QueryThread::QueryThread(QObject *parent) : 14 | QThread(parent) 15 | { 16 | } 17 | 18 | const QueryThread &QueryThread::instance() 19 | { 20 | if(!this_thread) 21 | { 22 | this_thread = new QueryThread(); 23 | connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this_thread, SLOT(quit())); 24 | this_thread->start(); 25 | } 26 | 27 | return *this_thread; 28 | } 29 | 30 | void QueryThread::run() { 31 | qDebug() << "Query thread started."; 32 | // Create worker object within the context of the new thread 33 | m_worker = new QueryWorker(); 34 | 35 | // forward to the worker: a 'queued connection'! 36 | connect(this, SIGNAL(queue(const QueryRequest &)), 37 | m_worker, SLOT(execute(const QueryRequest &))); 38 | 39 | // first, make the object system aware 40 | qRegisterMetaType("QueryRequest"); 41 | qRegisterMetaType("QueryResult"); 42 | // now set up the queued connection 43 | // forward a signal back out 44 | connect(m_worker, SIGNAL(resultsReady(const QueryResult &)), 45 | this, SIGNAL(queryFinished(const QueryResult &))); 46 | 47 | exec(); // start our own event loop 48 | } 49 | 50 | void QueryThread::execute(const QueryRequest &request) 51 | { 52 | qDebug() << "QueryThread::execute() got this query: " << request.getQuery(); 53 | emit queue(request); // queues to worker 54 | } 55 | 56 | QString QueryThread::getSqliteConnectionName() 57 | { 58 | return QueryWorker::getSqliteConnectionName(); 59 | } 60 | 61 | QString QueryThread::getMysqlConnectionName() 62 | { 63 | return QueryWorker::getMysqlConnectionName(); 64 | } 65 | 66 | void QueryThread::setTransactionState(QueryThread::TransactionState state) 67 | { 68 | transactionState = state; 69 | } 70 | 71 | QueryThread::TransactionState QueryThread::getTransactionState() 72 | { 73 | return transactionState; 74 | } 75 | -------------------------------------------------------------------------------- /asyncsql/querythread.h: -------------------------------------------------------------------------------- 1 | #ifndef QUERYTHREAD_H 2 | #define QUERYTHREAD_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "queryrequest.h" 8 | #include "queryresult.h" 9 | 10 | namespace AsyncSql { 11 | class QueryWorker; 12 | 13 | class QueryThread : public QThread 14 | { 15 | friend class QueryWorker; 16 | 17 | Q_OBJECT 18 | public: 19 | /* Transaction state represents the state of a started transaction. 20 | * -1 signifies that the transaction failed. 21 | * 0 signifies that there is no current transaction. 22 | * 1 signifies that there is a current transaction. 23 | */ 24 | enum TransactionState {Failed = -1, Unset, Set}; 25 | static const QueryThread &instance(); 26 | static QString getSqliteConnectionName(); 27 | static QString getMysqlConnectionName(); 28 | 29 | static TransactionState getTransactionState(); 30 | signals: 31 | void queryFinished(const QueryResult &); 32 | void queue(const QueryRequest &); 33 | public slots: 34 | void execute(const QueryRequest &); 35 | protected: 36 | virtual void run(); 37 | private: 38 | explicit QueryThread(QObject *parent = 0); 39 | static TransactionState transactionState; 40 | static void setTransactionState(TransactionState state); 41 | 42 | QueryWorker *m_worker; 43 | static QueryThread *this_thread; 44 | }; 45 | } 46 | 47 | #endif // QUERYTHREAD_H 48 | -------------------------------------------------------------------------------- /asyncsql/queryworker.cpp: -------------------------------------------------------------------------------- 1 | #include "queryworker.h" 2 | #include 3 | #include "databaseconnection.h" 4 | #include "queryresult.h" 5 | #include "queryrequest.h" 6 | #include "querythread.h" 7 | #include 8 | 9 | using namespace AsyncSql; 10 | 11 | const QString sqliteConnectionName = "query_thread_sqlite"; 12 | const QString mysqlConnectionName = "query_thread_mysql"; 13 | 14 | QueryWorker::QueryWorker(QObject *parent) : 15 | QObject(parent) 16 | { 17 | qDebug() << "QueryWorker thread started."; 18 | } 19 | 20 | void QueryWorker::execute(const QueryRequest &request) 21 | { 22 | QTime t; 23 | t.start(); 24 | 25 | try 26 | { 27 | openConnection(request); 28 | 29 | error = QSqlError(); 30 | result.setReceiver(request.getReceiver()); 31 | result.setRequestType(request.getRequestType()); 32 | 33 | QueryThread::setTransactionState(QueryThread::Unset); 34 | 35 | request.getRunBefore()(m_database); 36 | 37 | switch(request.getRequestType()) { 38 | case QueryRequest::Select: 39 | select(request); 40 | break; 41 | case QueryRequest::Insert: 42 | insert(request); 43 | break; 44 | case QueryRequest::Update: 45 | update(request); 46 | break; 47 | case QueryRequest::Delete: 48 | remove(request); 49 | break; 50 | case QueryRequest::BeginTransaction: 51 | startTransaction(); 52 | break; 53 | case QueryRequest::CommitTransaction: 54 | commitTransaction(); 55 | break; 56 | case QueryRequest::Command: 57 | executeCommand(request); 58 | break; 59 | case QueryRequest::CustomOperation: 60 | request.getCustomOperation()(m_database); 61 | break; 62 | } 63 | 64 | qDebug() << "QueryWorker: Results processed in " << t.elapsed() << " milliseconds."; 65 | 66 | setLastRecord(request); 67 | 68 | request.getRunAfter()(m_database); 69 | 70 | result.setRequestType(request.getRequestType()); 71 | result.setStatus(true); 72 | emit resultsReady(result); 73 | result.clear(); 74 | } 75 | catch (DatabaseException &e) 76 | { 77 | if(!error.isValid()) 78 | error = e.getError(); 79 | 80 | qDebug() << "Emitting failure results for " << request.getTableName(); 81 | qDebug() << e.getError(); 82 | emit resultsReady(QueryResult(request.getReceiver(), request.getRequestType(), error)); 83 | result.clear(); 84 | 85 | m_database.rollback(); 86 | //unlockTables(); 87 | } 88 | catch(std::exception &e) 89 | { 90 | qDebug() << "Unusual exception: " << e.what(); 91 | } 92 | catch(...) 93 | { 94 | qDebug() << "Uncaught exception thrown...."; 95 | } 96 | } 97 | 98 | void QueryWorker::openConnection(const QueryRequest &request) 99 | { 100 | const QString hostName = request.getConnection().getHostName().trimmed().isEmpty() ? 101 | DatabaseConnection::getDefaultHostName() : request.getConnection().getHostName().trimmed(); 102 | const int port = request.getConnection().getPort() == 0 ? 103 | DatabaseConnection::getDefaultPort() : request.getConnection().getPort(); 104 | const QString userName = request.getConnection().getUserName().trimmed().isEmpty() ? 105 | DatabaseConnection::getDefaultUserName() : request.getConnection().getUserName().trimmed(); 106 | const QString password = request.getConnection().getPassword().trimmed().isEmpty() ? 107 | DatabaseConnection::getDefaultPassword() : request.getConnection().getPassword().trimmed(); 108 | const QString connectOptions = request.getConnection().getConnectOptions().trimmed().isEmpty() ? 109 | DatabaseConnection::getDefaultConnectOptions() : request.getConnection().getConnectOptions().trimmed(); 110 | const QString driverName = request.getConnection().getDriverName().trimmed().isEmpty() ? 111 | DatabaseConnection::getDefaultDriverName() : request.getConnection().getDriverName().trimmed(); 112 | const QString databaseName = request.getConnection().getDatabaseName().trimmed().isEmpty() ? 113 | DatabaseConnection::getDefaultDatabaseName() : request.getConnection().getDatabaseName().trimmed(); 114 | 115 | if(m_database.hostName() != hostName 116 | || m_database.userName() != userName 117 | || m_database.password() != password 118 | || m_database.port() != port 119 | || m_database.databaseName() != databaseName 120 | || m_database.driverName() != driverName 121 | || m_database.connectOptions() != connectOptions) 122 | m_database.close(); 123 | 124 | connectionName = (driverName == "QSQLITE" ? sqliteConnectionName : mysqlConnectionName); 125 | 126 | if(!QSqlDatabase::contains(connectionName)) 127 | m_database = QSqlDatabase::addDatabase(driverName, connectionName); 128 | else 129 | m_database = QSqlDatabase::database(connectionName); 130 | 131 | m_database.setHostName(hostName); 132 | m_database.setUserName(userName); 133 | m_database.setPassword(password); 134 | m_database.setPort(port); 135 | m_database.setDatabaseName(databaseName); 136 | m_database.setConnectOptions(connectOptions); 137 | //m_database.setConnectOptions("MYSQL_OPT_RECONNECT = 1;"); 138 | 139 | if(!m_database.open()) 140 | throw DatabaseException(m_database.lastError()); 141 | } 142 | 143 | void QueryWorker::select(const QueryRequest &request) 144 | { 145 | QSqlQuery qry(m_database); 146 | 147 | if(request.getSortColumn() >= 0) { 148 | qry.prepare(QString("SELECT * FROM %1 LIMIT 0").arg(request.getTableName())); 149 | 150 | if(!qry.exec()) 151 | throw DatabaseException(qry.lastError()); 152 | 153 | QSqlRecord record(qry.record()); 154 | 155 | qry.prepare(request.getQuery().arg(record.fieldName(request.getSortColumn()))); 156 | } 157 | else 158 | qry.prepare(request.getQuery()); 159 | 160 | if(!qry.exec()) { 161 | qDebug() << "Failed to execute query in query worker. The query was " << qry.lastQuery(); 162 | qDebug() << "For table " << request.getTableName(); 163 | qDebug() << qry.lastError(); 164 | 165 | throw DatabaseException(qry.lastError()); 166 | } 167 | 168 | result.setRecord(qry.record()); 169 | result.setPrimaryIndex(m_database.primaryIndex(request.getTableName())); 170 | 171 | QList records; 172 | while(qry.next()) { 173 | QSqlRecord record(qry.record()); 174 | for(int i = 0; i < record.count(); ++i) 175 | record.setGenerated(i, false); 176 | 177 | records << record; 178 | } 179 | 180 | result.setRecords(records); 181 | } 182 | 183 | void QueryWorker::update(const QueryRequest &request) 184 | { 185 | QSqlQuery qry(m_database); 186 | 187 | for(int i = 0; i < request.getRecords().count(); ++i) { 188 | QSqlRecord record(request.getRecords().at(i)); 189 | QString query; 190 | 191 | for(int j = 0; j < record.count(); ++j) { 192 | if(!record.isGenerated(j)) 193 | continue; 194 | 195 | query = QString("UPDATE %1 SET %2 = ? WHERE %3 = ?").arg(request.getTableName(), 196 | record.fieldName(j), 197 | request.getPrimaryIndex().fieldName(0)); 198 | qry.prepare(query); 199 | qry.addBindValue(record.value(j)); 200 | qry.addBindValue(record.value(request.getPrimaryIndex().fieldName(0))); 201 | 202 | if(!qry.exec()) { 203 | qDebug() << "Failed to update record in query worker."; 204 | qDebug() << qry.lastError(); 205 | throw DatabaseException(qry.lastError()); 206 | } 207 | } 208 | } 209 | 210 | qDebug() << "Update successful."; 211 | } 212 | 213 | void QueryWorker::insert(const QueryRequest &req) 214 | { 215 | QueryRequest request(req); 216 | QSqlQuery qry(m_database); 217 | 218 | QList records(request.getRecords()); 219 | for(const QSqlRecord &record : records) { 220 | QString query; 221 | query = QString("INSERT INTO %1 (").arg(request.getTableName()); 222 | 223 | for(int j = 0; j < record.count(); ++j) { 224 | query += record.fieldName(j); 225 | 226 | if(j != record.count() - 1) 227 | query += ", "; 228 | else 229 | query += ") "; 230 | } 231 | 232 | query += "VALUES ("; 233 | 234 | // Prepare query 235 | for(int j = 0; j < record.count(); ++j) { 236 | query += "?"; 237 | 238 | if(j != record.count() - 1) 239 | query += ", "; 240 | else 241 | query += ")"; 242 | } 243 | 244 | qDebug() << "Prepared query for" << request.getTableName() << "in query worker? " << query; 245 | 246 | qry.prepare(query); 247 | 248 | for(int j = 0; j < record.count(); ++j) 249 | qry.addBindValue(record.value(j)); 250 | 251 | if(!qry.exec()) { 252 | qDebug() << "Failed to insert record in query worker."; 253 | qDebug() << qry.lastError(); 254 | throw DatabaseException(qry.lastError()); 255 | } 256 | } 257 | 258 | qDebug() << "Insertion successful."; 259 | result.setLastInsertId(qry.lastInsertId()); 260 | } 261 | 262 | void QueryWorker::remove(const QueryRequest &request) 263 | { 264 | QSqlQuery qry(m_database); 265 | 266 | // Delete code 267 | for(int i = 0; i < request.getRecords().count(); ++i) { 268 | QSqlRecord record(request.getRecords().at(i)); 269 | QString query; 270 | 271 | query = QString("DELETE FROM %1 WHERE %2 = ?").arg(request.getTableName(), 272 | request.getPrimaryIndex().fieldName(0)); 273 | 274 | qry.prepare(query); 275 | qry.addBindValue(record.value(request.getPrimaryIndex().fieldName(0))); 276 | 277 | if(!qry.exec()) { 278 | qDebug() << "Failed to delete record in query worker."; 279 | qDebug() << "The delete query in query worker for " << request.getTableName() << "? " << query; 280 | 281 | qDebug() << qry.lastError(); 282 | throw DatabaseException(qry.lastError()); 283 | } 284 | } 285 | 286 | qDebug() << "Delete successful."; 287 | } 288 | 289 | void QueryWorker::startTransaction() 290 | { 291 | QSqlQuery q(m_database); 292 | q.prepare("SET autocommit = 0"); 293 | 294 | if(!q.exec()) { 295 | qDebug() << "Failed to set autocommit to false."; 296 | throw DatabaseException(m_database.lastError()); 297 | } 298 | 299 | if(!m_database.transaction()) { 300 | qDebug() << "Failed to begin transaction in QueryWorker::execute()."; 301 | error = m_database.lastError(); 302 | throw DatabaseException(m_database.lastError()); 303 | } 304 | } 305 | 306 | void QueryWorker::commitTransaction() 307 | { 308 | 309 | try { 310 | if(!m_database.commit()) { 311 | qDebug() << "Failed to commit transaction in QueryWorker::execute()."; 312 | throw DatabaseException(m_database.lastError()); 313 | } 314 | 315 | QSqlQuery q(m_database); 316 | q.prepare("SET autocommit = 1"); 317 | 318 | if(!q.exec()) { 319 | qDebug() << "Failed to set autocommit to true."; 320 | throw DatabaseException(m_database.lastError()); 321 | } 322 | 323 | //unlockTables(); 324 | } 325 | catch(DatabaseException &) { 326 | //unlockTables(); 327 | throw; 328 | } 329 | } 330 | 331 | void QueryWorker::executeCommand(const QueryRequest &request) 332 | { 333 | try { 334 | QSqlQuery q(m_database); 335 | q.prepare(request.getQuery()); 336 | 337 | if(!q.exec()) 338 | throw DatabaseException(q.lastError()); 339 | } 340 | catch(DatabaseException &) { 341 | throw; 342 | } 343 | } 344 | 345 | void QueryWorker::setLastRecord(const QueryRequest &request) { 346 | if(request.getTableName().trimmed().isEmpty()) 347 | return; 348 | QSqlQuery q(m_database); 349 | q.prepare(QString("SELECT * FROM %1 ORDER BY %2 DESC LIMIT 1") 350 | .arg(request.getTableName(), 351 | m_database.primaryIndex(request.getTableName()).fieldName(0))); 352 | if(!q.exec()) { 353 | qDebug() << "Failed to get last record for table " << request.getTableName(); 354 | qDebug() << q.lastError(); 355 | qDebug() << "Last query: " << q.lastQuery(); 356 | } 357 | 358 | if(q.first()) 359 | result.setLastRecord(q.record()); 360 | } 361 | 362 | void QueryWorker::lockTables(const QueryRequest &request) 363 | { 364 | try { 365 | QSqlQuery qry(m_database); 366 | qry.prepare(QString("LOCK TABLES %1 WRITE").arg(request.getTableName())); 367 | 368 | if(!qry.exec()) { 369 | throw DatabaseException(qry.lastError()); 370 | } 371 | } 372 | catch(DatabaseException &) { 373 | throw; 374 | } 375 | } 376 | 377 | void QueryWorker::unlockTables() 378 | { 379 | try { 380 | QSqlQuery qry(m_database); 381 | qry.prepare(QString("UNLOCK TABLES")); 382 | 383 | if(!qry.exec()) { 384 | throw DatabaseException(qry.lastError()); 385 | } 386 | } 387 | catch(DatabaseException &) { 388 | throw; 389 | } 390 | } 391 | 392 | QueryWorker::~QueryWorker() { 393 | 394 | } 395 | 396 | QString QueryWorker::getSqliteConnectionName() 397 | { 398 | return sqliteConnectionName; 399 | } 400 | 401 | QString QueryWorker::getMysqlConnectionName() 402 | { 403 | return mysqlConnectionName; 404 | } 405 | -------------------------------------------------------------------------------- /asyncsql/queryworker.h: -------------------------------------------------------------------------------- 1 | #ifndef QUERYWORKER_H 2 | #define QUERYWORKER_H 3 | 4 | #include 5 | #include 6 | #include "databaseexception.h" 7 | 8 | #include "queryrequest.h" 9 | #include "queryresult.h" 10 | 11 | namespace AsyncSql { 12 | class QueryWorker : public QObject 13 | { 14 | Q_OBJECT 15 | public: 16 | explicit QueryWorker(QObject *parent = 0); 17 | ~QueryWorker(); 18 | 19 | static QString getSqliteConnectionName(); 20 | static QString getMysqlConnectionName(); 21 | signals: 22 | void resultsReady(const QueryResult &); 23 | public slots: 24 | void execute(const QueryRequest &); 25 | private: 26 | QSqlDatabase m_database; 27 | QString connectionName; 28 | QueryResult result; 29 | QSqlError error; 30 | 31 | void openConnection(const QueryRequest &); 32 | 33 | void select(const QueryRequest &); 34 | void update(const QueryRequest &); 35 | void insert(const QueryRequest &); 36 | void remove(const QueryRequest &); 37 | void startTransaction(); 38 | void commitTransaction(); 39 | void executeCommand(const QueryRequest &); 40 | void setLastRecord(const QueryRequest &); 41 | 42 | void lockTables(const QueryRequest &); 43 | void unlockTables(); 44 | }; 45 | } 46 | 47 | #endif // QUERYWORKER_H 48 | -------------------------------------------------------------------------------- /databases/example.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/obeezzy/AsyncSql/88a0cec9270a41550c5cc2054742d4f68d40b6a6/databases/example.db -------------------------------------------------------------------------------- /examples/databaseviewer/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication a(argc, argv); 7 | MainWindow w; 8 | w.show(); 9 | 10 | return a.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /examples/databaseviewer/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "ui_mainwindow.h" 3 | #include "asyncsql/asyncsqltablemodel.h" 4 | #include "asyncsql/databaseconnection.h" 5 | #include 6 | #include "optionsdialog.h" 7 | 8 | using namespace AsyncSql; 9 | 10 | MainWindow::MainWindow(QWidget *parent) : 11 | QMainWindow(parent), 12 | ui(new Ui::MainWindow) 13 | { 14 | ui->setupUi(this); 15 | 16 | DatabaseConnection::setDefaultDatabaseName("databases/example.db"); 17 | DatabaseConnection::setDefaultDriver(DatabaseConnection::SQLite); 18 | OptionsDialog::setTableName("sales"); 19 | 20 | model = new AsyncSqlTableModel(this); 21 | model->setTable(OptionsDialog::getTableName()); 22 | ui->tableView->setModel(model); 23 | 24 | connect(ui->actionDatabase_options, SIGNAL(triggered()), this, SLOT(showOptions())); 25 | connect(ui->actionExit, SIGNAL(triggered()), qApp, SLOT(quit())); 26 | 27 | connect(ui->selectButton, SIGNAL(clicked()), this, SLOT(select())); 28 | connect(model, SIGNAL(selected(bool)), this, SLOT(onSelected(bool))); 29 | connect(ui->submitButton, SIGNAL(clicked()), this, SLOT(submit())); 30 | connect(model, SIGNAL(submitted(bool)), this, SLOT(onSubmitted(bool))); 31 | connect(ui->revertButton, SIGNAL(clicked()), this, SLOT(revert())); 32 | 33 | setWindowTitle("Async SQL"); 34 | } 35 | 36 | void MainWindow::select() 37 | { 38 | if(ui->selectOptionsGroupBox->isChecked()) 39 | { 40 | model->setFilter(ui->filterLineEdit->text().trimmed()); 41 | model->setLimit(ui->limitSpinBox->value()); 42 | 43 | if(ui->sortOptionsGroupBox->isChecked()) 44 | { 45 | Qt::SortOrder order = ui->sortOrderComboBox->currentText() == "Ascending" ? 46 | Qt::AscendingOrder : Qt::DescendingOrder; 47 | 48 | model->setSort(ui->sortColumnSpinBox->value(), order); 49 | } 50 | } 51 | else 52 | { 53 | // Reset everything 54 | model->setFilter(""); 55 | model->setLimit(-1); 56 | model->setSort(-1, Qt::AscendingOrder); 57 | } 58 | 59 | model->select(); 60 | ui->statusBar->showMessage(tr("Selecting...")); 61 | } 62 | 63 | void MainWindow::onSelected(bool successful) 64 | { 65 | if(successful) 66 | ui->statusBar->showMessage(tr("Selecting done!"), 5000); 67 | else 68 | QMessageBox::critical(this, tr("Model status"), tr("Error: ") + model->lastError().text()); 69 | } 70 | 71 | void MainWindow::submit() 72 | { 73 | if(!model->isDirty()) 74 | { 75 | ui->statusBar->showMessage(tr("No changes made."), 5000); 76 | } 77 | else 78 | { 79 | model->submitAll(); 80 | ui->statusBar->showMessage(tr("Submitting...")); 81 | } 82 | } 83 | 84 | void MainWindow::onSubmitted(bool successful) 85 | { 86 | if(successful) 87 | ui->statusBar->showMessage(tr("Submitting done!"), 5000); 88 | else 89 | QMessageBox::critical(this, tr("Model status"), tr("Error: ") + model->lastError().text()); 90 | } 91 | 92 | void MainWindow::revert() 93 | { 94 | if(!model->isDirty()) 95 | { 96 | ui->statusBar->showMessage(tr("Nothing to revert."), 5000); 97 | } 98 | else 99 | { 100 | model->revertAll(); 101 | ui->statusBar->showMessage(tr("Reverted changes!"), 5000); 102 | } 103 | } 104 | 105 | void MainWindow::showOptions() 106 | { 107 | OptionsDialog dialog(this); 108 | 109 | if(dialog.exec()) 110 | { 111 | model->setTable(OptionsDialog::getTableName()); 112 | model->select(); 113 | } 114 | } 115 | 116 | MainWindow::~MainWindow() 117 | { 118 | delete ui; 119 | } 120 | -------------------------------------------------------------------------------- /examples/databaseviewer/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include "asyncsql/asyncsqltablemodel.h" 6 | 7 | namespace Ui { 8 | class MainWindow; 9 | } 10 | 11 | class MainWindow : public QMainWindow 12 | { 13 | Q_OBJECT 14 | 15 | public: 16 | explicit MainWindow(QWidget *parent = 0); 17 | ~MainWindow(); 18 | private slots: 19 | void select(); 20 | void onSelected(bool successful); 21 | void submit(); 22 | void onSubmitted(bool successful); 23 | void revert(); 24 | void showOptions(); 25 | private: 26 | Ui::MainWindow *ui; 27 | AsyncSql::AsyncSqlTableModel *model; 28 | }; 29 | 30 | #endif // MAINWINDOW_H 31 | -------------------------------------------------------------------------------- /examples/databaseviewer/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 456 10 | 515 11 | 12 | 13 | 14 | MainWindow 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | Qt::Horizontal 31 | 32 | 33 | 34 | 40 35 | 20 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | Submit changes 44 | 45 | 46 | 47 | 48 | 49 | 50 | Revert changes 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | Select options 62 | 63 | 64 | true 65 | 66 | 67 | false 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | Filter: 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | Limit: 88 | 89 | 90 | 91 | 92 | 93 | 94 | 1000000000 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | Sort options 104 | 105 | 106 | true 107 | 108 | 109 | false 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 0 119 | 0 120 | 121 | 122 | 123 | Sort column: 124 | 125 | 126 | 127 | 128 | 129 | 130 | 1000000000 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 0 139 | 0 140 | 141 | 142 | 143 | Sort order: 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | Ascending 152 | 153 | 154 | 155 | 156 | Descending 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | Qt::Horizontal 178 | 179 | 180 | 181 | 40 182 | 20 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | Select 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 0 204 | 0 205 | 456 206 | 21 207 | 208 | 209 | 210 | 211 | File 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | Database options... 222 | 223 | 224 | 225 | 226 | Exit 227 | 228 | 229 | 230 | 231 | Database options... 232 | 233 | 234 | 235 | 236 | Exit 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | -------------------------------------------------------------------------------- /examples/databaseviewer/optionsdialog.cpp: -------------------------------------------------------------------------------- 1 | #include "optionsdialog.h" 2 | #include "ui_optionsdialog.h" 3 | #include "asyncsql/databaseconnection.h" 4 | #include 5 | #include 6 | 7 | using namespace AsyncSql; 8 | 9 | QString OptionsDialog::tableName; 10 | 11 | OptionsDialog::OptionsDialog(QWidget *parent) : 12 | QDialog(parent), 13 | ui(new Ui::OptionsDialog) 14 | { 15 | ui->setupUi(this); 16 | 17 | load(); 18 | setWindowTitle(tr("Choose database options")); 19 | 20 | connect(ui->saveButton, SIGNAL(clicked()), this, SLOT(save())); 21 | connect(ui->closeButton, SIGNAL(clicked()), this, SLOT(reject())); 22 | 23 | connect(ui->browseButton, SIGNAL(clicked()), this, SLOT(browse())); 24 | } 25 | 26 | OptionsDialog::~OptionsDialog() 27 | { 28 | delete ui; 29 | } 30 | 31 | void OptionsDialog::setTableName(const QString &name) 32 | { 33 | tableName = name; 34 | } 35 | 36 | QString OptionsDialog::getTableName() 37 | { 38 | return tableName; 39 | } 40 | 41 | void OptionsDialog::load() 42 | { 43 | ui->hostNameLineEdit->setText(DatabaseConnection::getDefaultHostName()); 44 | ui->portSpinBox->setValue(DatabaseConnection::getDefaultPort()); 45 | ui->userNameLineEdit->setText(DatabaseConnection::getDefaultUserName()); 46 | ui->passwordLineEdit->setText(DatabaseConnection::getDefaultPassword()); 47 | ui->databaseNameLineEdit->setText(DatabaseConnection::getDefaultDatabaseName()); 48 | ui->connectOptionsLineEdit->setText(DatabaseConnection::getDefaultConnectOptions()); 49 | ui->tableNameLineEdit->setText(tableName); 50 | 51 | if(DatabaseConnection::getDefaultDriver() == DatabaseConnection::SQLite) 52 | ui->sqlDriverComboBox->setCurrentIndex(0); 53 | else if(DatabaseConnection::getDefaultDriver() == DatabaseConnection::MySQL) 54 | ui->sqlDriverComboBox->setCurrentIndex(1); 55 | } 56 | 57 | void OptionsDialog::save() 58 | { 59 | DatabaseConnection::setDefaultHostName(ui->hostNameLineEdit->text().trimmed()); 60 | DatabaseConnection::setDefaultPort(ui->portSpinBox->value()); 61 | DatabaseConnection::setDefaultUserName(ui->userNameLineEdit->text().trimmed()); 62 | DatabaseConnection::setDefaultPassword(ui->passwordLineEdit->text().trimmed()); 63 | DatabaseConnection::setDefaultDatabaseName(ui->databaseNameLineEdit->text().trimmed()); 64 | DatabaseConnection::setDefaultConnectOptions(ui->connectOptionsLineEdit->text().trimmed()); 65 | tableName = ui->tableNameLineEdit->text().trimmed(); 66 | 67 | if(ui->sqlDriverComboBox->currentText().trimmed() == "SQLite") 68 | DatabaseConnection::setDefaultDriver(DatabaseConnection::SQLite); 69 | else if(ui->sqlDriverComboBox->currentText().trimmed() == "MySQL") 70 | DatabaseConnection::setDefaultDriver(DatabaseConnection::MySQL); 71 | 72 | accept(); 73 | } 74 | 75 | void OptionsDialog::browse() 76 | { 77 | ui->databaseNameLineEdit->setText( 78 | QFileDialog::getOpenFileName(this, tr("Open File"), 79 | QStandardPaths::writableLocation(QStandardPaths::HomeLocation), 80 | tr("Databases (*.db *.sqlite *.sqlite.db)"))); 81 | } 82 | -------------------------------------------------------------------------------- /examples/databaseviewer/optionsdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef OPTIONSDIALOG_H 2 | #define OPTIONSDIALOG_H 3 | 4 | #include 5 | 6 | namespace Ui { 7 | class OptionsDialog; 8 | } 9 | 10 | class OptionsDialog : public QDialog 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit OptionsDialog(QWidget *parent = 0); 16 | ~OptionsDialog(); 17 | 18 | static void setTableName(const QString &); 19 | static QString getTableName(); 20 | private slots: 21 | void save(); 22 | void browse(); 23 | private: 24 | Ui::OptionsDialog *ui; 25 | static QString tableName; 26 | 27 | void load(); 28 | }; 29 | 30 | #endif // OPTIONSDIALOG_H 31 | -------------------------------------------------------------------------------- /examples/databaseviewer/optionsdialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | OptionsDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 423 10 | 260 11 | 12 | 13 | 14 | Dialog 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | Host name: 25 | 26 | 27 | 28 | 29 | 30 | 31 | QAbstractSpinBox::NoButtons 32 | 33 | 34 | 65535 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | Enter host name 45 | 46 | 47 | 48 | 49 | 50 | 51 | User name: 52 | 53 | 54 | 55 | 56 | 57 | 58 | Port: 59 | 60 | 61 | 62 | 63 | 64 | 65 | Enter user name 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | SQLite 74 | 75 | 76 | 77 | 78 | MySQL 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | SQL Driver: 87 | 88 | 89 | 90 | 91 | 92 | 93 | Set connect options 94 | 95 | 96 | 97 | 98 | 99 | 100 | Password: 101 | 102 | 103 | 104 | 105 | 106 | 107 | QLineEdit::Password 108 | 109 | 110 | Enter password 111 | 112 | 113 | 114 | 115 | 116 | 117 | Database name: 118 | 119 | 120 | 121 | 122 | 123 | 124 | Connect options: 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | Database name 134 | 135 | 136 | 137 | 138 | 139 | 140 | Browse... 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | Enter table name 150 | 151 | 152 | 153 | 154 | 155 | 156 | Table name: 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | Qt::Horizontal 168 | 169 | 170 | 171 | 40 172 | 20 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | Save 181 | 182 | 183 | 184 | 185 | 186 | 187 | Close 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | hostNameLineEdit 199 | portSpinBox 200 | userNameLineEdit 201 | passwordLineEdit 202 | sqlDriverComboBox 203 | connectOptionsLineEdit 204 | tableNameLineEdit 205 | databaseNameLineEdit 206 | browseButton 207 | saveButton 208 | closeButton 209 | 210 | 211 | 212 | 213 | -------------------------------------------------------------------------------- /examples/databaseviewer/ui_mainwindow.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | ** Form generated from reading UI file 'mainwindow.ui' 3 | ** 4 | ** Created by: Qt User Interface Compiler version 5.7.0 5 | ** 6 | ** WARNING! All changes made in this file will be lost when recompiling UI file! 7 | ********************************************************************************/ 8 | 9 | #ifndef UI_MAINWINDOW_H 10 | #define UI_MAINWINDOW_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | QT_BEGIN_NAMESPACE 35 | 36 | class Ui_MainWindow 37 | { 38 | public: 39 | QAction *action_Database_options; 40 | QAction *actionExit; 41 | QAction *actionDatabase_options; 42 | QAction *actionExit_2; 43 | QWidget *centralWidget; 44 | QVBoxLayout *verticalLayout_5; 45 | QVBoxLayout *verticalLayout_4; 46 | QVBoxLayout *verticalLayout; 47 | QTableView *tableView; 48 | QHBoxLayout *horizontalLayout; 49 | QSpacerItem *horizontalSpacer; 50 | QPushButton *submitButton; 51 | QPushButton *revertButton; 52 | QGroupBox *selectOptionsGroupBox; 53 | QVBoxLayout *verticalLayout_3; 54 | QVBoxLayout *verticalLayout_2; 55 | QHBoxLayout *horizontalLayout_2; 56 | QLabel *label; 57 | QLineEdit *filterLineEdit; 58 | QLabel *label_2; 59 | QSpinBox *limitSpinBox; 60 | QGroupBox *sortOptionsGroupBox; 61 | QGridLayout *gridLayout_2; 62 | QGridLayout *gridLayout; 63 | QLabel *label_3; 64 | QSpinBox *sortColumnSpinBox; 65 | QLabel *label_4; 66 | QComboBox *sortOrderComboBox; 67 | QHBoxLayout *horizontalLayout_3; 68 | QSpacerItem *horizontalSpacer_2; 69 | QPushButton *selectButton; 70 | QMenuBar *menuBar; 71 | QMenu *menuFile; 72 | QStatusBar *statusBar; 73 | 74 | void setupUi(QMainWindow *MainWindow) 75 | { 76 | if (MainWindow->objectName().isEmpty()) 77 | MainWindow->setObjectName(QStringLiteral("MainWindow")); 78 | MainWindow->resize(456, 515); 79 | action_Database_options = new QAction(MainWindow); 80 | action_Database_options->setObjectName(QStringLiteral("action_Database_options")); 81 | actionExit = new QAction(MainWindow); 82 | actionExit->setObjectName(QStringLiteral("actionExit")); 83 | actionDatabase_options = new QAction(MainWindow); 84 | actionDatabase_options->setObjectName(QStringLiteral("actionDatabase_options")); 85 | actionExit_2 = new QAction(MainWindow); 86 | actionExit_2->setObjectName(QStringLiteral("actionExit_2")); 87 | centralWidget = new QWidget(MainWindow); 88 | centralWidget->setObjectName(QStringLiteral("centralWidget")); 89 | verticalLayout_5 = new QVBoxLayout(centralWidget); 90 | verticalLayout_5->setSpacing(6); 91 | verticalLayout_5->setContentsMargins(11, 11, 11, 11); 92 | verticalLayout_5->setObjectName(QStringLiteral("verticalLayout_5")); 93 | verticalLayout_4 = new QVBoxLayout(); 94 | verticalLayout_4->setSpacing(6); 95 | verticalLayout_4->setObjectName(QStringLiteral("verticalLayout_4")); 96 | verticalLayout = new QVBoxLayout(); 97 | verticalLayout->setSpacing(6); 98 | verticalLayout->setObjectName(QStringLiteral("verticalLayout")); 99 | tableView = new QTableView(centralWidget); 100 | tableView->setObjectName(QStringLiteral("tableView")); 101 | 102 | verticalLayout->addWidget(tableView); 103 | 104 | horizontalLayout = new QHBoxLayout(); 105 | horizontalLayout->setSpacing(6); 106 | horizontalLayout->setObjectName(QStringLiteral("horizontalLayout")); 107 | horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); 108 | 109 | horizontalLayout->addItem(horizontalSpacer); 110 | 111 | submitButton = new QPushButton(centralWidget); 112 | submitButton->setObjectName(QStringLiteral("submitButton")); 113 | 114 | horizontalLayout->addWidget(submitButton); 115 | 116 | revertButton = new QPushButton(centralWidget); 117 | revertButton->setObjectName(QStringLiteral("revertButton")); 118 | 119 | horizontalLayout->addWidget(revertButton); 120 | 121 | 122 | verticalLayout->addLayout(horizontalLayout); 123 | 124 | 125 | verticalLayout_4->addLayout(verticalLayout); 126 | 127 | selectOptionsGroupBox = new QGroupBox(centralWidget); 128 | selectOptionsGroupBox->setObjectName(QStringLiteral("selectOptionsGroupBox")); 129 | selectOptionsGroupBox->setCheckable(true); 130 | selectOptionsGroupBox->setChecked(false); 131 | verticalLayout_3 = new QVBoxLayout(selectOptionsGroupBox); 132 | verticalLayout_3->setSpacing(6); 133 | verticalLayout_3->setContentsMargins(11, 11, 11, 11); 134 | verticalLayout_3->setObjectName(QStringLiteral("verticalLayout_3")); 135 | verticalLayout_2 = new QVBoxLayout(); 136 | verticalLayout_2->setSpacing(6); 137 | verticalLayout_2->setObjectName(QStringLiteral("verticalLayout_2")); 138 | horizontalLayout_2 = new QHBoxLayout(); 139 | horizontalLayout_2->setSpacing(6); 140 | horizontalLayout_2->setObjectName(QStringLiteral("horizontalLayout_2")); 141 | label = new QLabel(selectOptionsGroupBox); 142 | label->setObjectName(QStringLiteral("label")); 143 | 144 | horizontalLayout_2->addWidget(label); 145 | 146 | filterLineEdit = new QLineEdit(selectOptionsGroupBox); 147 | filterLineEdit->setObjectName(QStringLiteral("filterLineEdit")); 148 | 149 | horizontalLayout_2->addWidget(filterLineEdit); 150 | 151 | label_2 = new QLabel(selectOptionsGroupBox); 152 | label_2->setObjectName(QStringLiteral("label_2")); 153 | 154 | horizontalLayout_2->addWidget(label_2); 155 | 156 | limitSpinBox = new QSpinBox(selectOptionsGroupBox); 157 | limitSpinBox->setObjectName(QStringLiteral("limitSpinBox")); 158 | limitSpinBox->setMaximum(1000000000); 159 | 160 | horizontalLayout_2->addWidget(limitSpinBox); 161 | 162 | 163 | verticalLayout_2->addLayout(horizontalLayout_2); 164 | 165 | sortOptionsGroupBox = new QGroupBox(selectOptionsGroupBox); 166 | sortOptionsGroupBox->setObjectName(QStringLiteral("sortOptionsGroupBox")); 167 | sortOptionsGroupBox->setCheckable(true); 168 | sortOptionsGroupBox->setChecked(false); 169 | gridLayout_2 = new QGridLayout(sortOptionsGroupBox); 170 | gridLayout_2->setSpacing(6); 171 | gridLayout_2->setContentsMargins(11, 11, 11, 11); 172 | gridLayout_2->setObjectName(QStringLiteral("gridLayout_2")); 173 | gridLayout = new QGridLayout(); 174 | gridLayout->setSpacing(6); 175 | gridLayout->setObjectName(QStringLiteral("gridLayout")); 176 | label_3 = new QLabel(sortOptionsGroupBox); 177 | label_3->setObjectName(QStringLiteral("label_3")); 178 | QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); 179 | sizePolicy.setHorizontalStretch(0); 180 | sizePolicy.setVerticalStretch(0); 181 | sizePolicy.setHeightForWidth(label_3->sizePolicy().hasHeightForWidth()); 182 | label_3->setSizePolicy(sizePolicy); 183 | 184 | gridLayout->addWidget(label_3, 0, 0, 1, 1); 185 | 186 | sortColumnSpinBox = new QSpinBox(sortOptionsGroupBox); 187 | sortColumnSpinBox->setObjectName(QStringLiteral("sortColumnSpinBox")); 188 | sortColumnSpinBox->setMaximum(1000000000); 189 | 190 | gridLayout->addWidget(sortColumnSpinBox, 0, 1, 1, 1); 191 | 192 | label_4 = new QLabel(sortOptionsGroupBox); 193 | label_4->setObjectName(QStringLiteral("label_4")); 194 | sizePolicy.setHeightForWidth(label_4->sizePolicy().hasHeightForWidth()); 195 | label_4->setSizePolicy(sizePolicy); 196 | 197 | gridLayout->addWidget(label_4, 1, 0, 1, 1); 198 | 199 | sortOrderComboBox = new QComboBox(sortOptionsGroupBox); 200 | sortOrderComboBox->setObjectName(QStringLiteral("sortOrderComboBox")); 201 | 202 | gridLayout->addWidget(sortOrderComboBox, 1, 1, 1, 1); 203 | 204 | 205 | gridLayout_2->addLayout(gridLayout, 0, 0, 1, 1); 206 | 207 | 208 | verticalLayout_2->addWidget(sortOptionsGroupBox); 209 | 210 | 211 | verticalLayout_3->addLayout(verticalLayout_2); 212 | 213 | 214 | verticalLayout_4->addWidget(selectOptionsGroupBox); 215 | 216 | horizontalLayout_3 = new QHBoxLayout(); 217 | horizontalLayout_3->setSpacing(6); 218 | horizontalLayout_3->setObjectName(QStringLiteral("horizontalLayout_3")); 219 | horizontalSpacer_2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); 220 | 221 | horizontalLayout_3->addItem(horizontalSpacer_2); 222 | 223 | selectButton = new QPushButton(centralWidget); 224 | selectButton->setObjectName(QStringLiteral("selectButton")); 225 | 226 | horizontalLayout_3->addWidget(selectButton); 227 | 228 | 229 | verticalLayout_4->addLayout(horizontalLayout_3); 230 | 231 | 232 | verticalLayout_5->addLayout(verticalLayout_4); 233 | 234 | MainWindow->setCentralWidget(centralWidget); 235 | menuBar = new QMenuBar(MainWindow); 236 | menuBar->setObjectName(QStringLiteral("menuBar")); 237 | menuBar->setGeometry(QRect(0, 0, 456, 21)); 238 | menuFile = new QMenu(menuBar); 239 | menuFile->setObjectName(QStringLiteral("menuFile")); 240 | MainWindow->setMenuBar(menuBar); 241 | statusBar = new QStatusBar(MainWindow); 242 | statusBar->setObjectName(QStringLiteral("statusBar")); 243 | MainWindow->setStatusBar(statusBar); 244 | 245 | menuBar->addAction(menuFile->menuAction()); 246 | menuFile->addAction(actionDatabase_options); 247 | menuFile->addAction(actionExit_2); 248 | 249 | retranslateUi(MainWindow); 250 | 251 | QMetaObject::connectSlotsByName(MainWindow); 252 | } // setupUi 253 | 254 | void retranslateUi(QMainWindow *MainWindow) 255 | { 256 | MainWindow->setWindowTitle(QApplication::translate("MainWindow", "MainWindow", 0)); 257 | action_Database_options->setText(QApplication::translate("MainWindow", "Database options...", 0)); 258 | actionExit->setText(QApplication::translate("MainWindow", "Exit", 0)); 259 | actionDatabase_options->setText(QApplication::translate("MainWindow", "Database options...", 0)); 260 | actionExit_2->setText(QApplication::translate("MainWindow", "Exit", 0)); 261 | submitButton->setText(QApplication::translate("MainWindow", "Submit changes", 0)); 262 | revertButton->setText(QApplication::translate("MainWindow", "Revert changes", 0)); 263 | selectOptionsGroupBox->setTitle(QApplication::translate("MainWindow", "Select options", 0)); 264 | label->setText(QApplication::translate("MainWindow", "Filter:", 0)); 265 | label_2->setText(QApplication::translate("MainWindow", "Limit:", 0)); 266 | sortOptionsGroupBox->setTitle(QApplication::translate("MainWindow", "Sort options", 0)); 267 | label_3->setText(QApplication::translate("MainWindow", "Sort column:", 0)); 268 | label_4->setText(QApplication::translate("MainWindow", "Sort order:", 0)); 269 | sortOrderComboBox->clear(); 270 | sortOrderComboBox->insertItems(0, QStringList() 271 | << QApplication::translate("MainWindow", "Ascending", 0) 272 | << QApplication::translate("MainWindow", "Descending", 0) 273 | ); 274 | selectButton->setText(QApplication::translate("MainWindow", "Select", 0)); 275 | menuFile->setTitle(QApplication::translate("MainWindow", "File", 0)); 276 | } // retranslateUi 277 | 278 | }; 279 | 280 | namespace Ui { 281 | class MainWindow: public Ui_MainWindow {}; 282 | } // namespace Ui 283 | 284 | QT_END_NAMESPACE 285 | 286 | #endif // UI_MAINWINDOW_H 287 | -------------------------------------------------------------------------------- /examples/databaseviewer/ui_optionsdialog.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | ** Form generated from reading UI file 'optionsdialog.ui' 3 | ** 4 | ** Created by: Qt User Interface Compiler version 5.7.0 5 | ** 6 | ** WARNING! All changes made in this file will be lost when recompiling UI file! 7 | ********************************************************************************/ 8 | 9 | #ifndef UI_OPTIONSDIALOG_H 10 | #define UI_OPTIONSDIALOG_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | QT_BEGIN_NAMESPACE 29 | 30 | class Ui_OptionsDialog 31 | { 32 | public: 33 | QVBoxLayout *verticalLayout_2; 34 | QVBoxLayout *verticalLayout; 35 | QGridLayout *gridLayout; 36 | QLabel *label; 37 | QSpinBox *portSpinBox; 38 | QLineEdit *hostNameLineEdit; 39 | QLabel *label_2; 40 | QLabel *label_6; 41 | QLineEdit *userNameLineEdit; 42 | QComboBox *sqlDriverComboBox; 43 | QLabel *label_4; 44 | QLineEdit *connectOptionsLineEdit; 45 | QLabel *label_3; 46 | QLineEdit *passwordLineEdit; 47 | QLabel *label_5; 48 | QLabel *label_7; 49 | QHBoxLayout *horizontalLayout; 50 | QLineEdit *databaseNameLineEdit; 51 | QPushButton *browseButton; 52 | QLineEdit *tableNameLineEdit; 53 | QLabel *label_8; 54 | QHBoxLayout *horizontalLayout_2; 55 | QSpacerItem *horizontalSpacer; 56 | QPushButton *saveButton; 57 | QPushButton *closeButton; 58 | 59 | void setupUi(QDialog *OptionsDialog) 60 | { 61 | if (OptionsDialog->objectName().isEmpty()) 62 | OptionsDialog->setObjectName(QStringLiteral("OptionsDialog")); 63 | OptionsDialog->resize(423, 260); 64 | verticalLayout_2 = new QVBoxLayout(OptionsDialog); 65 | verticalLayout_2->setObjectName(QStringLiteral("verticalLayout_2")); 66 | verticalLayout = new QVBoxLayout(); 67 | verticalLayout->setObjectName(QStringLiteral("verticalLayout")); 68 | gridLayout = new QGridLayout(); 69 | gridLayout->setObjectName(QStringLiteral("gridLayout")); 70 | label = new QLabel(OptionsDialog); 71 | label->setObjectName(QStringLiteral("label")); 72 | 73 | gridLayout->addWidget(label, 0, 0, 1, 1); 74 | 75 | portSpinBox = new QSpinBox(OptionsDialog); 76 | portSpinBox->setObjectName(QStringLiteral("portSpinBox")); 77 | portSpinBox->setButtonSymbols(QAbstractSpinBox::NoButtons); 78 | portSpinBox->setMaximum(65535); 79 | 80 | gridLayout->addWidget(portSpinBox, 1, 1, 1, 1); 81 | 82 | hostNameLineEdit = new QLineEdit(OptionsDialog); 83 | hostNameLineEdit->setObjectName(QStringLiteral("hostNameLineEdit")); 84 | 85 | gridLayout->addWidget(hostNameLineEdit, 0, 1, 1, 1); 86 | 87 | label_2 = new QLabel(OptionsDialog); 88 | label_2->setObjectName(QStringLiteral("label_2")); 89 | 90 | gridLayout->addWidget(label_2, 2, 0, 1, 1); 91 | 92 | label_6 = new QLabel(OptionsDialog); 93 | label_6->setObjectName(QStringLiteral("label_6")); 94 | 95 | gridLayout->addWidget(label_6, 1, 0, 1, 1); 96 | 97 | userNameLineEdit = new QLineEdit(OptionsDialog); 98 | userNameLineEdit->setObjectName(QStringLiteral("userNameLineEdit")); 99 | 100 | gridLayout->addWidget(userNameLineEdit, 2, 1, 1, 1); 101 | 102 | sqlDriverComboBox = new QComboBox(OptionsDialog); 103 | sqlDriverComboBox->setObjectName(QStringLiteral("sqlDriverComboBox")); 104 | 105 | gridLayout->addWidget(sqlDriverComboBox, 4, 1, 1, 1); 106 | 107 | label_4 = new QLabel(OptionsDialog); 108 | label_4->setObjectName(QStringLiteral("label_4")); 109 | 110 | gridLayout->addWidget(label_4, 4, 0, 1, 1); 111 | 112 | connectOptionsLineEdit = new QLineEdit(OptionsDialog); 113 | connectOptionsLineEdit->setObjectName(QStringLiteral("connectOptionsLineEdit")); 114 | 115 | gridLayout->addWidget(connectOptionsLineEdit, 5, 1, 1, 1); 116 | 117 | label_3 = new QLabel(OptionsDialog); 118 | label_3->setObjectName(QStringLiteral("label_3")); 119 | 120 | gridLayout->addWidget(label_3, 3, 0, 1, 1); 121 | 122 | passwordLineEdit = new QLineEdit(OptionsDialog); 123 | passwordLineEdit->setObjectName(QStringLiteral("passwordLineEdit")); 124 | passwordLineEdit->setEchoMode(QLineEdit::Password); 125 | 126 | gridLayout->addWidget(passwordLineEdit, 3, 1, 1, 1); 127 | 128 | label_5 = new QLabel(OptionsDialog); 129 | label_5->setObjectName(QStringLiteral("label_5")); 130 | 131 | gridLayout->addWidget(label_5, 7, 0, 1, 1); 132 | 133 | label_7 = new QLabel(OptionsDialog); 134 | label_7->setObjectName(QStringLiteral("label_7")); 135 | 136 | gridLayout->addWidget(label_7, 5, 0, 1, 1); 137 | 138 | horizontalLayout = new QHBoxLayout(); 139 | horizontalLayout->setObjectName(QStringLiteral("horizontalLayout")); 140 | databaseNameLineEdit = new QLineEdit(OptionsDialog); 141 | databaseNameLineEdit->setObjectName(QStringLiteral("databaseNameLineEdit")); 142 | 143 | horizontalLayout->addWidget(databaseNameLineEdit); 144 | 145 | browseButton = new QPushButton(OptionsDialog); 146 | browseButton->setObjectName(QStringLiteral("browseButton")); 147 | 148 | horizontalLayout->addWidget(browseButton); 149 | 150 | 151 | gridLayout->addLayout(horizontalLayout, 7, 1, 1, 1); 152 | 153 | tableNameLineEdit = new QLineEdit(OptionsDialog); 154 | tableNameLineEdit->setObjectName(QStringLiteral("tableNameLineEdit")); 155 | 156 | gridLayout->addWidget(tableNameLineEdit, 6, 1, 1, 1); 157 | 158 | label_8 = new QLabel(OptionsDialog); 159 | label_8->setObjectName(QStringLiteral("label_8")); 160 | 161 | gridLayout->addWidget(label_8, 6, 0, 1, 1); 162 | 163 | 164 | verticalLayout->addLayout(gridLayout); 165 | 166 | horizontalLayout_2 = new QHBoxLayout(); 167 | horizontalLayout_2->setObjectName(QStringLiteral("horizontalLayout_2")); 168 | horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); 169 | 170 | horizontalLayout_2->addItem(horizontalSpacer); 171 | 172 | saveButton = new QPushButton(OptionsDialog); 173 | saveButton->setObjectName(QStringLiteral("saveButton")); 174 | 175 | horizontalLayout_2->addWidget(saveButton); 176 | 177 | closeButton = new QPushButton(OptionsDialog); 178 | closeButton->setObjectName(QStringLiteral("closeButton")); 179 | 180 | horizontalLayout_2->addWidget(closeButton); 181 | 182 | 183 | verticalLayout->addLayout(horizontalLayout_2); 184 | 185 | 186 | verticalLayout_2->addLayout(verticalLayout); 187 | 188 | QWidget::setTabOrder(hostNameLineEdit, portSpinBox); 189 | QWidget::setTabOrder(portSpinBox, userNameLineEdit); 190 | QWidget::setTabOrder(userNameLineEdit, passwordLineEdit); 191 | QWidget::setTabOrder(passwordLineEdit, sqlDriverComboBox); 192 | QWidget::setTabOrder(sqlDriverComboBox, connectOptionsLineEdit); 193 | QWidget::setTabOrder(connectOptionsLineEdit, tableNameLineEdit); 194 | QWidget::setTabOrder(tableNameLineEdit, databaseNameLineEdit); 195 | QWidget::setTabOrder(databaseNameLineEdit, browseButton); 196 | QWidget::setTabOrder(browseButton, saveButton); 197 | QWidget::setTabOrder(saveButton, closeButton); 198 | 199 | retranslateUi(OptionsDialog); 200 | 201 | QMetaObject::connectSlotsByName(OptionsDialog); 202 | } // setupUi 203 | 204 | void retranslateUi(QDialog *OptionsDialog) 205 | { 206 | OptionsDialog->setWindowTitle(QApplication::translate("OptionsDialog", "Dialog", 0)); 207 | label->setText(QApplication::translate("OptionsDialog", "Host name:", 0)); 208 | hostNameLineEdit->setText(QString()); 209 | hostNameLineEdit->setPlaceholderText(QApplication::translate("OptionsDialog", "Enter host name", 0)); 210 | label_2->setText(QApplication::translate("OptionsDialog", "User name:", 0)); 211 | label_6->setText(QApplication::translate("OptionsDialog", "Port:", 0)); 212 | userNameLineEdit->setPlaceholderText(QApplication::translate("OptionsDialog", "Enter user name", 0)); 213 | sqlDriverComboBox->clear(); 214 | sqlDriverComboBox->insertItems(0, QStringList() 215 | << QApplication::translate("OptionsDialog", "SQLite", 0) 216 | << QApplication::translate("OptionsDialog", "MySQL", 0) 217 | ); 218 | label_4->setText(QApplication::translate("OptionsDialog", "SQL Driver:", 0)); 219 | connectOptionsLineEdit->setPlaceholderText(QApplication::translate("OptionsDialog", "Set connect options", 0)); 220 | label_3->setText(QApplication::translate("OptionsDialog", "Password:", 0)); 221 | passwordLineEdit->setPlaceholderText(QApplication::translate("OptionsDialog", "Enter password", 0)); 222 | label_5->setText(QApplication::translate("OptionsDialog", "Database name:", 0)); 223 | label_7->setText(QApplication::translate("OptionsDialog", "Connect options:", 0)); 224 | databaseNameLineEdit->setPlaceholderText(QApplication::translate("OptionsDialog", "Database name", 0)); 225 | browseButton->setText(QApplication::translate("OptionsDialog", "Browse...", 0)); 226 | tableNameLineEdit->setPlaceholderText(QApplication::translate("OptionsDialog", "Enter table name", 0)); 227 | label_8->setText(QApplication::translate("OptionsDialog", "Table name:", 0)); 228 | saveButton->setText(QApplication::translate("OptionsDialog", "Save", 0)); 229 | closeButton->setText(QApplication::translate("OptionsDialog", "Close", 0)); 230 | } // retranslateUi 231 | 232 | }; 233 | 234 | namespace Ui { 235 | class OptionsDialog: public Ui_OptionsDialog {}; 236 | } // namespace Ui 237 | 238 | QT_END_NAMESPACE 239 | 240 | #endif // UI_OPTIONSDIALOG_H 241 | --------------------------------------------------------------------------------