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
|
#ifndef CHAINEXECUTOR_H
#define CHAINEXECUTOR_H
#include "db/db.h"
#include <QObject>
// TODO add parameters support for ChainExecutor.
// it requires clever api, cause there can be multiple queries and each can use differend parameters,
// while we cannot apply same parameter set for each query, cause they are bind by an available list/hash.
/**
* @brief Simple query executor, which executes queries one by one.
*
* A query executor which lets you execute query (or many queries)
* using asynchronous execution and it gets back to the called only when
* all queries succeeded, or it failed at some query.
*
* This is very useful if there is a sequence of queries to be executed
* and you're interested only in the result of the last query.
*
* It also lets to configure if queries should be executed within transaction,
* or not.
*/
class API_EXPORT ChainExecutor : public QObject
{
Q_OBJECT
public:
typedef QPair<int,QString> ExecutionError;
/**
* @brief Creates executor.
* @param parent Parent object for QObject.
*/
explicit ChainExecutor(QObject *parent = 0);
/**
* @brief Tells if transactional execution is enabled.
* @return Transactional execution status.
*/
bool getTransaction() const;
/**
* @brief Enabled or disables transactional execution.
* @param value True to enable, false to disable.
*
* Transactional execution is enabled by default. It means that all defined SQL queries
* will be executed in single SQL transaction.
*/
void setTransaction(bool value);
/**
* @brief Provides list of SQL queries configured for this executor.
* @return List of queries.
*/
QStringList getQueries() const;
/**
* @brief Defines list of queries to be executed.
* @param value List of query strings.
*
* This is the main mathod you're interested in when using ChainExecutor.
* This is how you define what SQL queries will be executed.
*
* Calling this method will clear any parameters defined previously with setParam().
*/
void setQueries(const QStringList& value);
/**
* @brief Provides currently configured database.
* @return Database that the queries are executed on in this executor.
*/
Db* getDb() const;
/**
* @brief Defines database for executing queries.
* @param value The database object.
*
* It is necessary to define the database before executing queries,
* otherwise the start() will emit failure() signal and do nothing else.
*/
void setDb(Db* value);
/**
* @brief Provides list of configured query mandatory flags.
* @return List of flags.
*
* See setMandatoryQueries() for details on mandatory flags.
*/
QList<bool> getMandatoryQueries() const;
/**
* @brief Defines list of mandatory flags for queries.
* @param value List of flags - a boolean per each defined query.
*
* Setting mandatory flags lets you define which queries (defined with setSqls())
* are mandatory for the successful execution and which are not.
* Queries are mandatory by default (when flags are not defined),
* which means that every defined query execution must be successfull,
* otherwise executor breaks the execution chain and reports error.
*
* By defining mandatory flags to false for some queries, you're telling
* to ChainExecutor, that it's okay if those queries fail and it should
* move along.
*
* For example:
* @code
* ChainExecutor executor;
* executor.setSqls({
* "DELETE FROM table1 WHERE value = 5",
* "DELETE FROM possibly_not_existing_table WHERE column > 3",
* "INSERT INTO table1 VALUES (4, 6)"
* });
* executor.setMandatoryQueries({true, false, true});
* @endcode
* We defined second query to be optional, therefore if the table
* "possibly_not_existing_table" doesn't exist, that's fine.
* It will be ignored and the third query will be executed.
* If flags were not defined, then execution of second query would fail,
* executor would stop there, report error (with failure() signal)
* and the third query would not be executed.
*
* It also affects transactions. If executor was defined to execute
* in a transaction (with setTransaction()), then failed query
* that was not mandatory will also not rollback the transaction.
*
* In other words, queries marked as not mandatory are silently ignored
* when failed.
*/
void setMandatoryQueries(const QList<bool>& value);
/**
* @brief Provides list of execution error messages.
* @return List of messages.
*
* Execution error messages usually have zero or one message,
* but if you defined some queries to be not mandatory,
* then each failed optional query will be silently ignored,
* but its error message will be stored and returned by this method.
* In that case, the result of this method can provide more than
* one message.
*/
QStringList getErrorsMessages() const;
/**
* @brief Provides list of execution errors.
* @return List of errors.
*
* These are the same errors as returned by getErrorsMessages(), except this list contains
* both error code (as returned from SQLite) and error message.
*/
const QList<ExecutionError>& getErrors() const;
/**
* @brief Tells if the executor is configured for asynchronous execution.
* @return Asynchronous flag value.
*/
bool getAsync() const;
/**
* @brief Defines asynchronous execution mode.
* @param value true to enable asynchronous execution, false to disable it.
*
* Asynchronous execution causes start() to return immediately.
*
* When asynchronous mode is enabled, results of execution
* have to be handled by connecting to failed() and success() signals.
*
* If the asynchronous mode is disabled, result can be queried
* by getSuccessfulExecution() call.
*/
void setAsync(bool value);
/**
* @brief Tells if the most recent execution was successful.
* @return true if execution was successful, or false if it failed.
*
* Successful execution means that all mandatory queries
* (see setMandatoryQueries()) executed successfully.
* Optional (not mandatory) queries do not affect result of this method.
*
* If this method returns true, it also means that success() signal
* was emitted.
* If this method returns false, it also means that failure() signal
* was emitted.
*/
bool getSuccessfulExecution() const;
/**
* @brief Defines named parameter to bind in queries.
* @param paramName Parameter name (must include the preceding ':').
* @param value Value for the parameter.
*
* Any parameter defined with this method will be applied to each query
* executed by the executor. If some query doesn't include parameter
* placeholder with defined name, then the parameter will simply
* not be applied to that query.
*/
void setParam(const QString& paramName, const QVariant& value);
bool getDisableForeignKeys() const;
void setDisableForeignKeys(bool value);
bool getDisableObjectDropsDetection() const;
void setDisableObjectDropsDetection(bool value);
bool isExecuting() const;
private:
/**
* @brief Executes query defines as the current one.
*
* Checks is there is a current query defined (pointed by currentSqlIndex).
* If there is, then executes it. If not, goes to executionSuccessful().
*
* This is called for each next query in asynchronous mode.
*/
void executeCurrentSql();
/**
* @brief Handles failed execution.
* @param errorCode Error code.
* @param errorText Error message.
*
* Rolls back transaction (in case of transactional execution) and emits failure().
*/
void executionFailure(int errorCode, const QString& errorText);
/**
* @brief Handles successful execution.
*
* Commits transaction (in case of transactional execution) and emits success().
*/
void executionSuccessful(SqlQueryPtr results);
/**
* @brief Executes all queries synchronously.
*
* If the asynchronous mode is disabled, then this method executes all queries.
*/
void executeSync();
/**
* @brief Handles single query execution results.
* @param results Results from the query.
* @return true if the execution was successful, or false otherwise.
*
* If there was an error while execution, then executionFailure() is also called.
*/
bool handleResults(SqlQueryPtr results);
Db::Flags getExecFlags() const;
void restoreFk();
/**
* @brief Database for execution.
*/
Db* db = nullptr;
/**
* @brief Transactional execution mode.
*/
bool transaction = true;
/**
* @brief Asynchronous execution mode.
*/
bool async = true;
/**
* @brief Queries to be executed.
*/
QStringList sqls;
/**
* @brief List of flags for mandatory queries.
*
* See setMandatoryQueries() for details.
*/
QList<bool> mandatoryQueries;
/**
* @brief Index pointing to the current query in sqls list.
*
* When executing query in asynchronous mode, this index points to the next
* query that should be executed.
*/
int currentSqlIndex = -1;
/**
* @brief A flag indicating whether the executor is currently in process of executing queries.
*/
bool executionInProgress = false;
/**
* @brief Asynchronous ID of current query execution.
*
* The ID is provided by Db::asyncExec().
*/
quint32 asyncId = -1;
/**
* @brief Execution interrupted flag.
*
* Once the interrup() was called, this flag is set to true,
* so the executor knows that it should not execute any further queries.
*/
bool interrupted = false;
/**
* @brief Errors raised during queries execution.
*
* In case of major failure, the error message is appended to this list,
* but when mandatory flags allow some failures, than this list may
* contain more error messages.
*/
QList<ExecutionError> executionErrors;
/**
* @brief Successful execution indicator.
*
* This is set after execution is finished.
*/
bool successfulExecution = false;
/**
* @brief Parameters to bind to queries.
*
* This is filled with setParam() calls and used later to bind
* parameters to executed queries.
*/
QHash<QString,QVariant> queryParams;
bool disableForeignKeys = false;
bool disableObjectDropsDetection = false;
SqlQueryPtr lastExecutionResults;
public slots:
/**
* @brief Interrupts query execution.
*/
void interrupt();
/**
* @brief Starts execution of all defined queries, one by one.
*/
void exec();
private slots:
/**
* @brief Handles asynchronous execution results from Db::asyncExec().
* @param asyncId Asynchronous ID of the execution for the results.
* @param results Results returned from execution.
*
* Checks if given asynchronous ID matches the internally stored asyncId
* and if yes, then handles results and executes next query in the queue.
*/
void handleAsyncResults(quint32 asyncId, SqlQueryPtr results);
signals:
/**
* @brief Emitted when all mandatory queries were successfully executed.
* @param results Execution results from last query execution.
*
* See setMandatoryQueries() for details on mandatory queries.
*/
void success(SqlQueryPtr results);
/**
* @brief Emitted when chain execution is finished, regardless of result.
* @param results Execution results from last query execution (may be null).
*
* In slot for this signal always check getSuccessfulExecution(),
* because execution could failed, despite results providing no error.
* It may happen for example if execution was interrupted,
* or executor could not pass through execution preparation phase
* (in which case results will be null).
*/
void finished(SqlQueryPtr results);
/**
* @brief Emitted when major error occurred while executing a query.
* @param errorCode Error code.
* @param errorText Error message.
*
* It's emitted only when mandatory query has failed execution.
* See setMandatoryQueries() for details on mandatory queries.
*/
void failure(int errorCode, const QString& errorText);
};
#endif // CHAINEXECUTOR_H
|