File: sqlquerymodel.h

package info (click to toggle)
sqlitestudio 3.4.21%2Bds-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 54,880 kB
  • sloc: ansic: 406,208; cpp: 123,872; yacc: 2,692; tcl: 497; sh: 462; xml: 426; makefile: 19
file content (635 lines) | stat: -rw-r--r-- 27,962 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
#ifndef SQLQUERYMODEL_H
#define SQLQUERYMODEL_H

#include "db/db.h"
#include "db/sqlquery.h"
#include "db/queryexecutor.h"
#include "sqlquerymodelcolumn.h"
#include "parser/ast/sqlitecreatetable.h"
#include "common/column.h"
#include "guiSQLiteStudio_global.h"
#include "sqlqueryitemdelegate.h"
#include "common/strhash.h"
#include <QStandardItemModel>
#include <QItemSelection>

class SqlQueryItem;
class FormView;
class SqlQueryView;
class SqlQueryRowNumModel;

class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel
{
        Q_OBJECT

    public:
        enum Feature
        {
            INSERT_ROW = 0x01,
            DELETE_ROW = 0x02,
            FILTERING = 0x04
        };
        Q_DECLARE_FLAGS(Features, Feature)

        typedef std::function<void()> CommitSuccessfulHandler;

        friend class SqlQueryItemDelegate;

        explicit SqlQueryModel(QObject *parent = 0);
        virtual ~SqlQueryModel();

        static void staticInit();

        QString getQuery() const;
        void setQuery(const QString &value);
        void setExplainMode(bool explain);
        void setParams(const QHash<QString, QVariant>& params);
        Db* getDb() const;
        void setDb(Db* value);
        qint64 getExecutionTime();
        qint64 getTotalRowsReturned();
        qint64 getTotalRowsAffected();
        qint64 getTotalPages();
        QList<SqlQueryModelColumnPtr> getColumns();
        SqlQueryItem* itemFromIndex(const QModelIndex& index) const;
        SqlQueryItem* itemFromIndex(int row, int column) const;
        QModelIndexList findIndexes(int role, const QVariant &value, int hits = -1) const;
        QModelIndexList findIndexes(const QModelIndex &start, const QModelIndex& end, int role, const QVariant &value, int hits = -1, bool stringApproximation = false) const;
        QList<SqlQueryItem*> findItems(int role, const QVariant &value, int hits = -1) const;
        QList<SqlQueryItem*> findItems(const QModelIndex &start, const QModelIndex& end, int role, const QVariant &value, int hits = -1) const;
        SqlQueryItem* findAnyInColumn(int column, int role, const QVariant &value) const;
        QList<SqlQueryItem*> getUncommittedItems() const;
        QList<SqlQueryItem*> getRow(int row);
        int columnCount(const QModelIndex& parent = QModelIndex()) const;
        QVariant headerData(int section, Qt::Orientation orientation, int role) const;
        bool isExecutionInProgress() const;
        StrHash<QString> attachDependencyTables();
        void detachDependencyTables();
        void rememberFocusedCell();
        void forgetFocusedCell();

        /**
         * @brief Disables or re-enables async query execution
         * @param enabled True to set async mode enabled, false to set synchronous mode.
         *
         * This option is forwarded directly to the query executor.
         *
         * By default mode is asynchronous, but in some cases synchronous mode may be useful (like in FK combobox).
         */
        void setAsyncMode(bool enabled);
        virtual QString generateSelectQueryForItems(const QList<SqlQueryItem*>& items);
        virtual QString generateInsertQueryForItems(const QList<SqlQueryItem*>& items);
        virtual QString generateUpdateQueryForItems(const QList<SqlQueryItem*>& items);
        virtual QString generateDeleteQueryForItems(const QList<SqlQueryItem*>& items);

        virtual Features features() const;

        /**
         * @brief Request for applying SQL expression filtering on a dataset.
         * @param value Filter expression.
         * Default implementation does nothing. Working implementation (i.e. for a table)
         * should set the query to temporary value which respects given filter and reload the data.
         * Filter passed to this method is meant to be treated as SQL expression to be placed after WHERE clause.
         */
        virtual void applySqlFilter(const QString& value);

        /**
         * @brief Request for applying an "equals" filtering on a dataset.
         * @param value Filter expression.
         * Default implementation does nothing. Working implementation (i.e. for a table)
         * should set the query to temporary value which respects given filter and reload the data.
         * Filter passed to this method is meant to be treated as strict value to be compared against.
         */
        virtual void applyStrictFilter(const QString& value);

        /**
         * @brief Request for applying "LIKE" filtering on a dataset.
         * @param value Filter expression.
         * Default implementation does nothing. Working implementation (i.e. for a table)
         * should set the query to temporary value which respects given filter and reload the data.
         * Filter passed to this method is meant to be treated as plain text to be matched in any column.
         */
        virtual void applyStringFilter(const QString& value);

        /**
         * @brief Request for applying Regular Expression filtering on a dataset.
         * @param value Filter expression.
         * Default implementation does nothing. Working implementation (i.e. for a table)
         * should set the query to temporary value which respects given filter and reload the data.
         * Filter passed to this method is meant to be treated as regular expression to be matched in any column.
         */
        virtual void applyRegExpFilter(const QString& value);

        /**
         * @brief Request for applying "LIKE" filtering on a dataset.
         * @param values Filter expressions per column.
         * This is the same as applyStringFilter(const QString&), but is used for per-column filtering,
         * when user enters filtering expressions for each column sparately.
         */
        virtual void applyStringFilter(const QStringList& values);

        /**
         * @brief Request for applying an "equals" filtering on a dataset.
         * @param values Filter expressions per column.
         * This is the same as applyStrictFilter(const QString&), but is used for per-column filtering,
         * when user enters filtering expressions for each column sparately.
         */
        virtual void applyStrictFilter(const QStringList& values);

        /**
         * @brief Request for applying Regular Expression filtering on a dataset.
         * @param values Filter expressions per column.
         * This is the same as applyRegExpFilter(const QString&), but is used for per-column filtering,
         * when user enters filtering expressions for each column sparately.
         */
        virtual void applyRegExpFilter(const QStringList& values);

        /**
         * @brief resetFilter
         * Default implementation does nothing. Working implementation (i.e. for a table)
         * should resets filter, so the data is no longer filtered.
         */
        virtual void resetFilter();

        /**
         * @brief getCurrentPage Gets number of current results page
         * @param includeOneBeingLoaded If true, then also the page that is currently being loaded (but not yet done) will returned over the currently presented page.
         * @return Current page as 0-based index. If current page is not yet defined or paging is disabled, then this method returns 0.
         * This method returns always the page that is currently presented in results, not the one that might be currently being queried.
         * If you need to include the one being loaded (if any), then use getLoadingPage().
         */
        int getCurrentPage(bool includeOneBeingLoaded = false) const;
        void gotoPage(int newPage);
        bool canReload();
        virtual bool supportsModifyingQueriesInMenu() const;
        Qt::Alignment findValueAlignment(const QVariant& value, SqlQueryModelColumn* column);

        QueryExecutor::SortList getSortOrder() const;
        void setSortOrder(const QueryExecutor::SortList& newSortOrder);

        /**
         * @brief Tells if database schema was modified by last query executed.
         * @return true if schema was modified, or false if not.
         */
        bool wasSchemaModified() const;
        bool wasDataModifyingQuery() const;

        SqlQueryView* getView() const;
        void setView(SqlQueryView* value);

        static QList<QList<SqlQueryItem*>> groupItemsByRows(const QList<SqlQueryItem*>& items);
        static QHash<AliasedTable, QList<SqlQueryItem*> > groupItemsByTable(const QList<SqlQueryItem*>& items);
        static QHash<AliasedTable, QVector<SqlQueryModelColumn*> > groupColumnsByTable(const QVector<SqlQueryModelColumn*>& columns);

        bool getSimpleExecutionMode() const;
        void setSimpleExecutionMode(bool value);

        int getHardRowLimit() const;
        void setHardRowLimit(int value);

        bool isAllDataLoaded() const;

        bool isStructureOutOfDate() const;

        int getQueryCountLimitForSmartMode() const;
        void setQueryCountLimitForSmartMode(int value);

        void insertCustomRow(const QList<QVariant>& values, int insertionIndex);

        void setDesiredColumnWidth(int colIdx, int width);
        int getDesiredColumnWidth(int colIdx);

        void setCellDataLengthLimit(int value);
        int getCellDataLengthLimit();

    protected:
        class CommitUpdateQueryBuilder : public RowIdConditionBuilder
        {
            public:
                void clear();

                void setDatabase(const QString& database);
                void setTable(const QString& table);
                void setColumn(const QString& column);
                void addColumn(const QString& column);

                QString build();
                QStringList getAssignmentArgs() const;

            protected:
                QString database;
                QString table;
                QStringList columns;
                QStringList assignmentArgs;
        };

        class SelectCellsQueryBuilder : public RowIdConditionBuilder
        {
            public:
                void addRowId(const RowId& rowId);
                QString build();
                void clear();
                void setDatabase(const QString& database);
                void setTable(const QString& table);
                QString getDatabase() const;
                QString getTable() const;
                void addColumn(const QString& column);
                RowId readRowId(SqlResultsRowPtr row) const;
                int getColumnCount() const;

            protected:
                QSet<QString> rowIdColumns;
                QString database;
                QString table;
                QSet<QString> columns;
                QSet<RowId> includedRowIds;
                int argSquence = 0;
        };

        struct StoredFocus
        {
            int row = -1;
            int column = -1;
            int forPage = -1;
            int forRowsPerPage = -1;
            QString forFilter;

            bool isValid();
            void reset();
        };

        /**
         * @brief commitAddedRow Inserts new row to a table.
         * @param itemsInRow All cells for the new row.
         * @return true on success, false on failure.
         * Default implementation does nothing and returns false, because inserting for custom query results is not possible.
         * Inheriting class can reimplement this, so for example model specialized for single table can add rows.
         * The method implementation should take items that are in model (and are passed to this method)
         * and insert them into the actual database table. It also has to update items in the model,
         * so they are no longer "new" and have the same data as inserted into the database.
         */
        virtual bool commitAddedRow(const QList<SqlQueryItem*>& itemsInRow, QList<CommitSuccessfulHandler>& successfulCommitHandlers);

        /**
         * @brief commitEditedRow Updates table row with new values.
         * @param itemsInRow Modified cell values.
         * @return true on success, false on failure.
         * Default implementation should be okay for most cases. It takes all modified cells and updates their
         * values in table basing on the ROWID, database, table and column names - which are all available,
         * unless the cell doesn't referr to the table, but in that case the cell should not be editable for user anyway.
         * <b>Important</b> thing to pay attention to is that the item list passed in arguments contains <b>only modified items</b>.
         */
        virtual bool commitEditedRow(const QList<SqlQueryItem*>& itemsInRow, QList<CommitSuccessfulHandler>& successfulCommitHandlers);

        /**
         * @brief commitDeletedRow Deletes row from the table.
         * @param itemsInRow All cells for the deleted row.
         * @return true on success, false on failure.
         * Default implementation gets rid of row items from the model and that's all.
         * Inheriting class can reimplement this, so for example model specialized for single table can delete rows.
         * The method implementation should delete the row from the database.
         */
        virtual bool commitDeletedRow(const QList<SqlQueryItem*>& itemsInRow, QList<CommitSuccessfulHandler>& successfulCommitHandlers);

        /**
         * @brief rollbackAddedRow
         * @param itemsInRow All cells for the new row.
         * Default implementation gets rid of row items from the model and that's all.
         */
        virtual void rollbackAddedRow(const QList<SqlQueryItem*>& itemsInRow);

        /**
         * @brief rollbackEditedRow
         * @param itemsInRow All cells for the deleted row.
         * Restores original values in items.
         */
        virtual void rollbackEditedRow(const QList<SqlQueryItem*>& itemsInRow);

        /**
         * @brief rollbackDeletedRow
         * @param itemsInRow Modified cell values.
         * The implementation should restore original values to items in the model.
         * The default implementation is pretty much complete. It restores original state of row items.
         */
        virtual void rollbackDeletedRow(const QList<SqlQueryItem*>& itemsInRow);

        SqlQueryModelColumnPtr getColumnModel(const QString& database, const QString& table, const QString& column);
        SqlQueryModelColumnPtr getColumnModel(const QString& table, const QString& column);
        QList<SqlQueryModelColumnPtr> getTableColumnModels(const QString& database, const QString& table);
        QList<SqlQueryModelColumnPtr> getTableColumnModels(const QString& table);
        void updateItem(SqlQueryItem* item, const QVariant& value, const SqlQueryModelColumnPtr &column, const RowId& rowId, SqlResultsRowPtr row, const BiStrHash& typeColumnToResColumn);
        void updateItem(SqlQueryItem* item, const QVariant& value, const SqlQueryModelColumnPtr &column, const RowId& rowId);
        void updateItem(SqlQueryItem* item, const QVariant& value, const SqlQueryModelColumnPtr &column, const RowId& rowId, Qt::Alignment alignment);
        RowId getNewRowId(const RowId& currentRowId, const QList<SqlQueryItem*> items);
        void updateRowIdForAllItems(const AliasedTable& table, const RowId& rowId, const RowId& newRowId);
        QHash<QString, QVariantList> toValuesGroupedByColumns(const QList<SqlQueryItem*>& items);
        void refreshGeneratedColumns(const QList<SqlQueryItem*>& items);
        void refreshGeneratedColumns(const QList<SqlQueryItem*>& items, QHash<SqlQueryItem*, QVariant>& values, const RowId& insertedRowId);

        QueryExecutor* queryExecutor = nullptr;
        Db* db = nullptr;
        QList<SqlQueryModelColumnPtr> columns;
        StoredFocus storedFocus;

        /**
         * @brief tablesInUse
         * List of tables that are used in currently presented data set.
         * Database in those tables (if not empty) is a symbolic name, as listed on database list.
         * If database is empty, then it was not explicitly typed in the query, so it's the local main db.
         */
        QList<DbAndTable> tablesInUse;

        /**
         * @brief Limit of data length in loaded cells.
         *
         * Bytes or utf-8 characters.
         * Having this set to 10000 gives about 290 MB of memory consumption
         * while having 30 columns and 1000 result rows loaded, all with 10000 bytes.
         */
        int cellDataLengthLimit = 100;

    private:
        struct TableDetails
        {
            struct ColumnDetails
            {
                SqliteColumnTypePtr type;
                QList<SqliteCreateTable::Column::ConstraintPtr> constraints;
            };

            QHash<QString,ColumnDetails> columns;
            QList<SqliteCreateTable::ConstraintPtr> constraints;
        };

        /**
         * @brief Loads data from queyr execution into UI cells.
         * @param results Execution results from query executor.
         * @return Whether to continue execution or not.
         */
        bool loadData(SqlQueryPtr results);

        QList<QStandardItem*> loadRow(SqlResultsRowPtr row);
        RowId getRowIdValue(SqlResultsRowPtr row, const SqlQueryModelColumnPtr &column);
        bool readColumns();
        void readColumnDetails();
        void updateColumnsHeader();
        void updateColumnHeaderLabels();
        void executeQueryInternal();
        void internalExecutionStopped();
        QHash<AliasedTable,TableDetails> readTableDetails();
        QList<SqlQueryItem*> toItemList(const QModelIndexList& indexes) const;
        bool commitRow(const QList<SqlQueryItem*>& itemsInRow, QList<CommitSuccessfulHandler>& successfulCommitHandlers);
        void rollbackRow(const QList<SqlQueryItem*>& itemsInRow);
        QHash<SqlQueryItem*, QVariant> readCellValues(SelectCellsQueryBuilder& queryBuilder, const QHash<RowId, QSet<SqlQueryItem*> >& itemsPerRowId);
        void storeStep1NumbersFromExecution();
        void storeStep2NumbersFromExecution();
        void restoreNumbersToQueryExecutor();
        QList<SqlQueryItem*> filterOutCommittedItems(const QList<SqlQueryItem*>& items);
        void commitInternal(const QList<SqlQueryItem*>& items);
        void rollbackInternal(const QList<SqlQueryItem*>& items);
        void reloadInternal();
        void addNewRowInternal(int rowIdx);
        Icon& getIconForIdx(int idx) const;
        void detachDatabases();
        QString getDatabaseForCommit(const QString& database);
        void recalculateRowsAndPages(int rowsDelta);
        int getInsertRowIndex();
        void notifyItemEditionEnded(const QModelIndex& idx);
        int getRowsPerPage() const;
        bool isEmptyQuery() const;
        void restoreFocusedCell();

        QString query;
        QHash<QString, QVariant> queryParams;
        bool explain = false;
        bool simpleExecutionMode = false;

        /**
         * @brief reloadAvailable
         * This value is false by default and is changed only once - after first successful
         * query execution. It's designed to report proper status by canReload().
         * Data reloading is available to user practically after any query was executed.
         */
        bool reloadAvailable = false;

        /**
         * @brief reloading
         * This switch tells you if model is in the process of data reloading (true value)
         * or initial query execution (then it's false). Data reloading takes place in any case
         * when page is changed, order is changed, or simply user calls the data reloading.
         * The initial query execution takes place when user calls "Execute query",
         * which is translated to call to executeQuery().
         */
        bool reloading = false;

        /**
         * @brief lastExecutionTime
         * Keeps number of milliseconds that recently successfully executed query took to complete.
         * If there was no such query executed, this will be 0.
         */
        quint64 lastExecutionTime = 0;

        /**
         * @brief totalRowsReturned
         * Keeps number of rows returned from recently successfully executed query.
         * If there was no such query executed, this will be 0.
         */
        quint64 totalRowsReturned = 0;

        /**
         * @brief rowsAffected
         * Keeps number of rows affected by recently successfully executed query.
         * If there was no such query executed, this will be 0.
         */
        quint64 rowsAffected = 0;

        /**
         * @brief totalPages
         * Keeps number of pages available in recently successfully executed query.
         * If there was no such query executed, this will be -1.
         */
        int totalPages = -1;

        /**
         * @brief page
         * The page variable keeps page of recently sucessfly loaded data.
         * If there was no successful data load, or when paging is disabled, then this will be -1.
         */
        int page = -1;

        /**
         * @brief sortOrder
         * The sortOrder variable keeps sorting order of recently sucessfly loaded data.
         * If column member of the sort object is -1, then no sorting is being aplied.
         */
        QueryExecutor::SortList sortOrder;

        QHash<Column,SqlQueryModelColumnPtr> columnMap;
        QHash<AliasedColumn,int> columnWidths;
        QHash<AliasedTable,QHash<QString,QString>> tableToRowIdColumn;
        QStringList headerColumns;
        int rowNumBase = 0;
        SqlQueryView* view = nullptr;
        quint32 resultsCountingAsyncId = 0;
        QStringList requiredDbAttaches;
        StrHash<QString> dbNameToAttachNameMapForCommit;
        QList<Db*> dbListToDetach;

        /**
         * @brief Sets row count limit, despite user configured limit.
         *
         * -1 to not apply hard limit (use user configured row limit), any other value is the limit.
         */
        int hardRowLimit = -1;

        /**
         * @brief Limit for rows in case there is many columns.
         *
         * -1 to not apply the limit. This is set during reading columns. If there is many columns,
         * we need to keep maximum limit of rows at pace, so we don't overuse the RAM.
         * This limit is soft, meaning it applies only if it's smaller than configured limit or hardRowLimit.
         * If any of two limits mentioned above are smaller, this limit will not come to the play.
         */
        int columnRatioBasedRowLimit = -1;

        /**
         * @brief tablesForColumns
         * Map of queru executor alias to tables associated to \link #columns.
         */
        QHash<QString,AliasedTable> tablesForColumns;

        /**
         * @brief columnEditionStatus
         * Map of query executor alias to column edition capabilities.
         */
        QHash<QString, bool> columnEditionStatus;

        QList<int> rowsDeletedSuccessfullyInTheCommit;

        bool allDataLoaded = false;

        bool structureOutOfDate = false;

        /**
         * @brief Set of existing model objects, updated for each construction and destruction.
         *
         * This is used by loadData() to determinathe whether processEvents() caused deletion of the model.
         * We need to keep processEvents() call so the UI is responsive to Interrupt button,
         * but this causes crash when model is deleted in the events processing (like when FK combobox is deleted faster than data is loaded).
         */
        static QSet<SqlQueryModel*> existingModels;

    private slots:
        void handleExecFinished(SqlQueryPtr results);
        void handleExecFailed(int code, QString errorMessage);
        void resultsCountingFinished(quint64 rowsAffected, quint64 rowsReturned, int totalPages);

    public slots:
        void itemValueEdited(SqlQueryItem* item);
        void repaintAllItems();
        void changeSorting(int logicalIndex, Qt::SortOrder order);
        void changeSorting(int logicalIndex);
        void firstPage();
        void prevPage();
        void nextPage();
        void lastPage();
        void executeQuery(bool enforcePage0 = false);
        void interrupt(bool sync = false);
        void commit();
        void rollback();
        void commit(const QList<SqlQueryItem*>& items);
        void rollback(const QList<SqlQueryItem*>& items);
        void reload();
        void updateSelectiveCommitRollbackActions(const QItemSelection& selected, const QItemSelection& deselected);
        void addNewRow();
        void addMultipleRows();
        void deleteSelectedRows();
        void handlePossibleTableModification(Db* modDb, const QString& database, const QString& objName);
        void handlePossibleTableRename(Db* modDb, const QString& database, const QString& oldName, const QString& newName);

    signals:
        /**
         * @brief executionStarted
         *
         * Emitted just after query started executing.
         */
        void executionStarted();

        /**
         * @brief executionSuccessful
         *
         * Emitted after initial query execution was successful. It's not emitted after data reloading of page changing.
         */
        void executionSuccessful();

        /**
         * @brief Execution is finished and data is about to be loaded to model.
         * Emitted every query execution, every data reloading and every page change.
         */
        void aboutToLoadResults();

        /**
         * @brief executionFailed
         * @param errorText
         *
         * Emitted after failed query execution, or data reloading failed or page changing failed.
         */
        void executionFailed(const QString& errorText);

        /**
         * @brief loadingEnded
         * @param executionSuccessful
         *
         * Emitted every query execution, every data reloading and every page change.
         */
        void loadingEnded(bool executionSuccessful);

        /**
         * @brief totalRowsAndPagesAvailable
         *
         * Emitted when model finished querying total number of rows (and pages).
         * This is asynchronously emitted after execution has finished, so counting doesn't block the model.
         * It might not get emitted in some cases, like when there was an error when counting (it will be logged with qWarning()),
         * or when counting was interrupted by executing query (the same, or modified).
         *
         * When the main query execution failed, this signal will be emitted to inform about total rows and pages being 0.
         */
        void totalRowsAndPagesAvailable();

        void storeExecutionInHistory();

        /**
         * @brief commitStatusChanged
         * @param commitAvailable Tells if there's anything to commit/rollback or not.
         *
         * Emitted after any results cell has been modified and can now be committed or rolled back.
         * Also emitted after commit and rollback.
         */
        void commitStatusChanged(bool commitAvailable);

        /**
         * @brief selectiveCommitStatusChanged
         * @param commitAvailable Tells if there's anything to commit/rollback or not.
         *
         * Emitted when user changes selection in the view, so if the selection includes any uncommitted cells,
         * then this signal will be emitted with parameter true, or if there is no uncommitted cells,
         * then it will be emitted with parameter false.
         */
        void selectiveCommitStatusChanged(bool commitAvailable);

        /**
         * @brief sortIndicatorUpdated
         *
         * Emitted after columns header sorting has been changed.
         */
        void sortingUpdated(const QueryExecutor::SortList& sortOrder);

        void aboutToCommit(int totalSteps);
        void committingStepFinished(int step);
        void commitFinished();
        void itemEditionEnded(SqlQueryItem* item);
};

Q_DECLARE_OPERATORS_FOR_FLAGS(SqlQueryModel::Features)

#endif // SQLQUERYMODEL_H