#ifndef QUERYEXECUTOR_H #define QUERYEXECUTOR_H #include "db/db.h" #include "coreSQLiteStudio_global.h" #include "common/bistrhash.h" #include "parser/ast/sqlitequery.h" #include "parser/ast/sqlitequerytype.h" #include "datatype.h" #include #include #include #include /** @file */ class Parser; class QueryExecutorStep; class DbPlugin; class ChainExecutor; /** * @brief Advanced SQL query execution handler. * * QueryExecutor is an advanced SQL query execution handler, which lets you execute any query (with subqueries, joins, etc) * and is capable of providing meta information about returned data, such as ROWID for all rows and columns, * data sources (database, table and column) for every column, rows affected, total rows number for query, etc. * All of this available for both SQLite versions: 2 and 3. * * Queries are executed asynchronously. To handle result a lambda function can be used (or any function pointer), * or manual tracking of asynchronous execution ID and signals from this class. Function pointers and lambdas * are recommended way to handle results. * * It also allows you to: *
    *
  • programatically define sorting on desired column.
  • *
  • define result rows paging (page size and queried page)
  • *
  • refer other databases by their symbolic name and they will be attached and detached on the fly
  • *
  • define maximum cell data size (in bytes), so you won't read too much data at once
  • *
* * Total number of result rows is counted by a separate call to the database (using SELECT count(*) ...) * and its result is provided later, which is signalized by signal resultsCountingFinished(). Row counting can * be disabled with setSkipRowCounting(). See "Counting query" section below for details. * * The simplest use case would be: * @code * Db* db = getDb(); * QueryExecutor *executor = new QueryExecutor(db, "SELECT * FROM table"); * executor->exec([=](SqlQueryPtr results) * { * if (results->isError()) * { * qCritical() << "Error " << results->getErrorCode() << ": " << results->getErrorText() << "\n"; * return; * } * qDebug() << results->valueList(); * } * @endcode * * Unless you want some of QueryExecutor's special features, it's recommended to use * Db::exec() and Db::asyncExec(), because while QueryExecutor is powerful, it also does lots of thing underneeth * you may not need at all. * * \note This class is used in SQL editor window (SqlQueryModel) to execute queries entered by the user. * * \section smart_simple_sec "smart mode" vs "simple mode" * * Documentation of this class references many times to "smart mode" and "simple mode" expressions. * The "smart mode" means that the QueryExecutor was able to parse the input query, modify it for its needs * (add some meta-information columns, etc) and executed modified query successfully. * When the "smart mode" fails (which should be rare), the "simple mode" steps in as a fallback strategy. * The "simple mode" doesn't modify input query, just directly executes in on the database * and then QueryExecutor tries to extract as much meta-information from "simple mode" as it can (which is not much). * * The "simple mode" also doesn't apply any paging (see QueryExecutor::setPage()), nor data size limits * (see QueryExecutor::setDataLengthLimit()). * * The meta-information is all the data from the query that is not the essential data requested in the input query. * That is full description on all requested columns (their source tables, databases, data types), * ROWID value for all returned data rows, and more... * * \section counting_query Counting query * * QueryExecutor can split results into pages. In such cases, results are not all read, instead they are limited * at SQL level with LIMIT and OFFSET keywords. Because of that it is impossible to tell how many rows * would actualy be returned if there were no limit keywords. * * To deal with it the QueryExecutor makes extra query execution, which happens asynchronously to the main query * execution. This extra execution starts just after the main query execution has finished (with success). * This extra query (aka "Counting query") is made of original query wrapped with: * @code * SELECT count(*) FROM (original_query) * @endcode * This way QueryExecutor know the true number of rows to be retuend by the query. * * Since this extra query execution takes some extra time, this is performed asynchronously and only after * successful execution of the main query. If you need to work with QueryExecutor::getTotalRowsReturned(), * wait for the QueryExecutor::resultsCountingFinished() signal first. * * Row counting query execution can be disabled with QueryExecutor::setSkipRowCounting(), */ class API_EXPORT QueryExecutor : public QObject, public QRunnable { Q_OBJECT public: /** * @brief General reasons for which results data cannot be edited. */ enum class EditionForbiddenReason { NOT_A_SELECT, /**< Executed query was not a SELECT. Only SELECT results can be edited. */ SMART_EXECUTION_FAILED /**< * QueryExecutor could not perform "smart" execution, * which means that it was unable to gather meta information * about returned data and therefore it cannot tell what are ROWIDs * or data sources for each column. Still it was able to perform * simple (direct, without query modifications) execution * and it returned results, so they can be presented to the user, * but not edited. * * This happens usually when there's a but in SQLiteStudio, * which caused - for example - error during query parsing by Parser, * or other query syntax issues, that wasn't handled correctly * by SQLiteStudio. */ }; /** * @brief Per-column reasons for which the data in the column cannot be edited. */ enum class ColumnEditionForbiddenReason { COMPOUND_SELECT, /**< * The data cell comes from compound SELECT (UNION, EXCEPT, INTERSECT), * which makes it problematic to SQLiteStudio to find out to which table * does the particular row belong to. * * It might be resolved in future SQLiteStudio versions and this enum value * would disappear then. */ GROUPED_RESULTS, /**< * The data cell comes from SELECT with aggregated results, therefore it's * hard to hard what were ROWIDs of each row in the results. * * It might be resolved in future SQLiteStudio versions and this enum value * would disappear then. */ DISTINCT_RESULTS, /**< * The data cell comes from SELECT DISTINCT clause, therefore extracting * ROWIDs from the results is impossible, becasuse querying ROWID would * make every row unique, therefore DISTINCT would not remove any rows, * even the rest of the data (which matters to the user) would not be * unique and should have been removed by the DISTINCT keyword. * * Because of that, SQLiteStudio doesn't extract ROWIDs for DISTINCT * queries, so the results are accurate, but in consequence, * the data cannot be edited. */ EXPRESSION, /**< * The data cell is a result of a formula, function or other expression, * which is not a direct data source, therefore it's impossible to change * it's value. */ SYSTEM_TABLE, /**< * The data cell comes from system table (sqlite_*) and those tables cannot * be edited. */ COMM_TAB_EXPR, /**< * The data cell comes from system "WITH common-table-expression" SELECT * statement and those tables cannot be edited for the same reasons as * in COMPOUND_SELECT case. To learn about common table expression statement, * see http://sqlite.org/lang_with.html */ VIEW_NOT_EXPANDED,/**< * The data cell comes from a VIEW that was not expanded (because there were * multi-level views), therefore it was impossible to get ROWID for the cell. */ RES_INLINE_SUBSEL,/**< * The data cell comes from an subselect used as inline expression of result column. * Getting ROWID of such cell is not possible. */ }; /** * @brief Sort order definition. * * QueryExecutor supports programmatic sort order definition. * It supports smooth transition from/to Qt sorting direction enum * and defines sorting column by its index (0-based). */ struct API_EXPORT Sort { /** * @brief Sorting order. */ enum Order { ASC, /**< Ascending order */ DESC, /**< Descending order */ NONE /**< No sorting at all */ }; /** * @brief Default constructor with no sorting defined. * * Constructed object uses NONE as sorting order. */ Sort(); /** * @brief Creates sort order with given order on given column. * @param order Order to sort with. * @param column 0-based column number. */ Sort(Order order, int column); /** * @brief Creates sort order with given order on given column. * @param order Qt typed sort order (Qt::AscendingOrder, Qt::DescendingOrder). * @param column 0-based column number. */ Sort(Qt::SortOrder order, int column); /** * @brief Gets Qt typed sort order. * @return Sort order. */ Qt::SortOrder getQtOrder() const; /** * @brief Sorting order. */ Order order = NONE; /** * @brief 0-based column number to sort by. */ int column = -1; }; typedef QList SortList; /** * @brief ResultColumn as represented by QueryExecutor. * * QueryExecutor has its own result column representation, because it provides more * meta information on the column. */ struct API_EXPORT ResultColumn { /** * @brief Database name that the result column comes from. * * It's an SQLite internal name of the database, which means it's either "main", or "temp", * or symbolic name of registered database (as represented in the databases tree), * or the name of any attached databases. * * Symbolic database name is provided when user used it in his query and SQLiteStudio attached * it transparently. In that case the temporary name used for "ATTACH" statement would make no sense, * because that database was detached automatically after the query execution finished. * * In case of databases attached manually by user, it's exactly the same string as used when executing * "ATTACH" statement. */ QString database; /** * @brief Table name that the result column comes from. */ QString table; /** * @brief Table column name that the result column comes from. */ QString column; /** * @brief Alias defined for the result column in the query. */ QString alias; /** * @brief Table alias defined in the query. * * This is an alias defined in the query for the table that the result column comes from. */ QString tableAlias; /** * @brief Name of the column as presented to user. * * This is the name of a column as SQLite would present it to the user. * If the query requested just a column from table, it will be that column name. * If the query resuested two columns with the same name, then the second column will get * suffix ":1", next one would get suffix ":2", and so on. * For expressions the display name is direct copy of the SQL code used to define the expression. * * If the alias was defined in query, than it's used for the display name instead of anything else. */ QString displayName; /** * @brief QueryExecutor's internal alias for the column. * * This value has no sense outside of QueryExecutor. It's used by QueryExecutor to * keep track of columns from subselects, etc. */ QString queryExecutorAlias; /** * @brief Set of reasons for which column editing is denied. * * If the set is empty, it means that the column can be edited. */ QSet editionForbiddenReasons; /** * @brief Flag indicating that the column is actually an expression. * * Column representing an expression is not just a column and it should not be ever wrapped with * quoting wrapper ([], "", ``). Such a column is for example call to the SQL function. * * For regular columns this will be false. */ bool expression = false; }; /** * @brief Shared pointer to ResultColumn. */ typedef QSharedPointer ResultColumnPtr; /** * @brief Combined row ID columns for tables in the query. * * Since version 3.8.2 SQLite introduced the "WITHOUT ROWID" clause. It allows tables to have no * ROWID built-in. Such tables must have PRIMARY KEY defined, which does the job of the unique key * for the table. * * This structure describes the unique key for the table, regardless if it's a regular ROWID, * or if it's a PRIMARY KEY on a column, or if it's a multi-column PRIMARY KEY. * * You should always understand it as a set of PRIMARY KEY columns for given table. * Referencing to that table using given columns will guarantee uniqueness of the row. * * In case of regular table (with no "WITHOUT ROWID" clause), there will be only one column * defined in ResultRowIdColumn::columns and it will be named "ROWID". */ struct API_EXPORT ResultRowIdColumn { /** * @brief Database name that the table with this row ID is in. * * It's the actual database name as SQLite sees it. That means it is a "main", or any attach name. */ QString database; /** * @brief Symbolic database name as listed in databases list. * * It can be empty if database was not explicitly passed in the query. */ QString dbName; /** * @brief Table name that the row ID is for. */ QString table; /** * @brief Table alias defined in the query. * @see ResultColumn::tableAlias */ QString tableAlias; /** * @brief Mapping from alias to real column. * * This is mapping from QueryExecutor's internal aliases for columns * into primary key column names of the table that the result column comes from. * * If you want to get list of column names used for RowId, use values() on this member. * If you want to get list of query executor aliases, use keys() on this member. */ QHash queryExecutorAliasToColumn; }; /** * @brief Shared pointer to ResultRowIdColumn. */ typedef QSharedPointer ResultRowIdColumnPtr; /** * @brief Table that was a data source for at least one column in the query. */ struct API_EXPORT SourceTable { /** * @brief Table's database. * * Same rules apply as for ResultColumn::database. */ QString database; /** * @brief Table name. */ QString table; /** * @brief Table alias defined in query. */ QString alias; }; /** * @brief Shared pointer to SourceTable. */ typedef QSharedPointer SourceTablePtr; /** * @brief Query execution context. * * This class is used to share data across all executor steps. * It also provides initial configuration for executor steps. */ struct Context { /** * @brief Query string after last query step processing. * * Before any step was executed, this is the same as originalQuery. * * The processed query is the one that will be executed in the end, * so any steps should apply their changes to this query. * * This string should be modified and updated from QueryExecutorStep implementations. * * You won't usually modify this string directly. Instead you will * want to use one of 2 methods: *
    *
  • Modify tokens
  • - modify tokens of top level objects in parsedQueries * and call QueryExecutorStep::updateQueries(). *
  • Modify parsed objects
  • - modify logical structure and values of * objects in parsedQueries, then call on those objects SqliteStatement::rebuildTokens() * and finally call QueryExecutorStep::updateQueries. *
* * The parsedQueries are refreshed every time when QueryExecutor executes * QueryExecutorParse step. */ QString processedQuery; /** * @brief Number of milliseconds that query execution took. * * This is measured and set by QueryExecutorStepExecute step. */ qint64 executionTime = 0; /** * @brief Number of rows affected by the query. */ qint64 rowsAffected = 0; /** * @brief Total number of rows returned from query. * * It provides correct number for all queries, no matter if it's SELECT, PRAGMA, or other. */ qint64 totalRowsReturned = 0; /** * @brief Total number of pages. * * If there's a lot of result rows, they are split to pages. * There's always at least one page of results. */ int totalPages = 1; /** * @brief Defines if row counting will be performed. * * In case of EXPLAIN or PRAGMA queries the number of result rows is not provided from * SQLite (at least not from Qt's drivers for them). Instead we need to manually count * number of rows. This is when this flag is set (it's done by QueryExecutor, * no need to care about it). */ bool rowsCountingRequired = false; /** * @brief Executing query in EXPLAIN mode. * * This is configuration parameter passed from QueryExecutor just before executing * the query. It can be defined by QueryExecutor::setExplainMode(). */ bool explainMode = false; /** * @brief Defines if row counting should be skipped. * * This is a configuration flag predefined by QueryExecutor just before executing starts. * You can set it with QueryExecutor::setSkipRowCounting(). * * Row counting is done asynchronously, just after normal query execution is finished. * It's done by executing yet another query, which is more or less an orginal query * wrapped with "SELECT count(*) FROM (...)". * * Separate counting has to be done, because QueryExecutor adds LIMIT and OFFSET * to SELECT queries for results paging. * * When counting is done, the resultsCountingFinished() signal is emitted. */ bool skipRowCounting = false; /** * @brief Parameters for query execution. * * It's defined by setParam(). */ QHash queryParameters; /** * @brief Results handler function pointer. * * This serves the same purpose as in Db class. It's used for execution * with results handled by provided function. See Db::QueryResultsHandler for details. * * It's defined by exec(). */ Db::QueryResultsHandler resultsHandler = nullptr; /** * @brief List of queries parsed from input query string. * * List of parsed queries is updated each time the QueryExecutorParseQuery step * is executed. When it's called is defined by QueryExecutor::executionChain. */ QList parsedQueries; /** * @brief Results of executed query. * * This is results object defined by the final query execution. It means that the * query executed passed all preprocessing steps and was executed in its final form. * * This member is defined by QueryExecutorExecute step. */ SqlQueryPtr executionResults; /** * @brief Currently attached databases. * * This is a cross-context information about currently attached databases. * As QueryExecutorAttaches step does attaching, other steps may need information * about attached databases. It's a map of orginal_db_name_used to attached_name. */ BiStrHash dbNameToAttach; /** * @brief Sequence used by executor steps to generate column names. */ int colNameSeq = 0; /** * @brief List of reasons that editing results is forbidden for. * * Executor steps may decide that the results of query cannot be edited. * In that case they add proper enum to this set. */ QSet editionForbiddenReasons; /** * @brief Result columns that provide ROWID. * * QueryExecutorAddRowIds step adds those columns. There is one or more columns * per data source table mentioned in the query. It depends on "WITHOUT ROWID" clause * in CREATE TABLE of the source table. */ QList rowIdColumns; /** * @brief Result columns from the query. * * List of result columns, just like they would be returned from regular execution * of the query. Column in this list are not just a names of those columns, * they provide full meta information about every single column. */ QList resultColumns; /** * @brief Data source tables mentioned in the query. * * List of tables used as data source in the query. */ QSet sourceTables; /** * @brief Map of column aliases containing column types. * * Keys are query executor column aliases of column representing types * and values are query executor column aliases of column to which these types apply to. */ QHash typeColumnToResultColumnAlias; /** * @brief Query used for counting results. * * Filled with SQL to be used for results counting (even if counting is disabled). * @see QueryExecutor::getCountingQuery() */ QString countingQuery; /** * @brief Flag indicating results preloading. * * Causes flag Db::Flag::PRELOAD to be added to the query execution. */ bool preloadResults = false; /** * @brief Tells if executed queries did modify database schema. * * This is defined by QueryExecutorDetectSchemaAlter step * and can be accessed by QueryExecutor::wasSchemaModified(). */ bool schemaModified = false; /** * @brief Tells if executed query was one of DELETE, UPDATE or INSERT. */ bool dataModifyingQuery = false; /** * @brief Forbids QueryExecutor to return meta columns. * * See QueryExecutor::noMetaColumns for details. */ bool noMetaColumns = false; /** * @brief Contains error code from smart execution. * * This is always set by the smart execution method. It's useful if the smart execution * failed and the simple execution method is being performed. In that case the query * result will contain error code from simple execution, hiding the original error code * from smart execution. */ int errorCodeFromSmartExecution = 0; /** * @brief Contains error message from smart execution. * * This is always set by the smart execution method. It's useful if the smart execution * failed and the simple execution method is being performed. In that case the query * result will contain error message from simple execution, hiding the original error * message from smart execution. */ QString errorMessageFromSmartExecution; /** * @brief Flag indicating whether views were replaced/expanded. * * In other words, this flag tells whether the ReplaceViews step of query executor * was executed, or skipped (due to many levels of views). False = skipped. */ bool viewsExpanded = false; }; /** * @brief Position for custom executor steps. * * These position define where in query executor chain of steps the new, registered step will be placed. * It's used in arguments of registerStep() method. * * If multiple steps are registered at same position, they will be executed in order they were registered. * * Values of positions determin what actions have been already taken by the executor, so for example * AFTER_ATTACHES means, that executor already attached referenced databases and replaced occurrences of objects * from that databases. * * Enumerations are in order as the steps are executed. * * If you need more detailed description about certain steps performed by query executor, see documentation * of their classes - all these classes name start with QueryExecutor prefix. */ enum StepPosition { FIRST, /**< As first step */ AFTER_ATTACHES, /**< After transparent attaching is applied (detected tables are defined to be attached first). */ AFTER_REPLACED_VIEWS, /**< After referenced views have been replaced with subqueries */ AFTER_ROW_IDS, /**< After ROWID columns have been added to result columns */ AFTER_REPLACED_COLUMNS, /**< After all columns have been explicitly listed in result list, together with unique alias names */ AFTER_ORDER, /**< After order clause was applied/modified */ AFTER_DISTINCT_WRAP, /**< After wrapping SELECT was added in case of DISTINCT or GROUP BY clauses were used */ AFTER_COLUMN_TYPES, /**< After typeof() result meta columns were added */ AFTER_COLUMN_FILTERS, /**< After WHERE filters applied */ AFTER_ROW_LIMIT_AND_OFFSET, /**< After LIMIT and ORDER clauses were added/modified. This is the last possible moment, directly ahead of final query execution */ JUST_BEFORE_EXECUTION, /**< Same as AFTER_ROW_LIMIT_AND_OFFSET */ LAST /**< Same as AFTER_ROW_LIMIT_AND_OFFSET */ }; /** * @brief Interface for producing query executor steps. * * It can be used for overloaded version of registerStep() method, * in case a step is stateful and needs to be created/deleted for every query executor processing. * * If step is stateless, it can be registered directly as an instance, using the other version of registerStep() method. * * This is an abstract class and not pointer to function, because it has to be comparable (and std::function is not), * so it's possible to deregister the factory later on. */ class StepFactory { public: virtual QueryExecutorStep* produceQueryExecutorStep() = 0; }; /** * @brief Creates query executor, initializes internal context object. * @param db Optional database. If not provided, it has to be defined later with setDb(). * @param query Optional query to execute. If not provided, it has to be defined later with setQuery(). * @param parent Parent QObject. */ QueryExecutor(Db* db = nullptr, const QString& query = QString(), QObject *parent = 0); /** * @brief Releases internal resources. */ ~QueryExecutor(); /** * @brief Defined query to be executed. * @param query SQL query string. * * The query string can actually be multiple queries separated with a semicolon, just like you would * write multiple queries in the SQL Editor window. Query executor will handle that. * * The query can contain parameter placeholders (such as :param, \@param). To bind values to params * use setParam(). */ void setQuery(const QString& query); /** * @brief Executes the query. * @param resultsHandler Optional handler function pointer, can be lambda function. See Db::QueryResultsHandler for details. * * While execution is asynchronous, the executor notifies about results by signals. * In case of success emits executionFinished(), in case of error emits executionFailed(). */ void exec(Db::QueryResultsHandler resultsHandler = nullptr); /** * @brief Interrupts current execution. * * Calls Db::asyncInterrupt() internally. */ void interrupt(); /** * @brief Executes counting query. * @return true if counting query is executed (in async mode) or was executed correctly (in sync mode), false on error. * * Executes (asynchronously) counting query for currently defined query. After execution is done, the resultsCountingFinished() * signal is emitted. * * Counting query is made of original query wrapped with "SELECT count(*) FROM (original_query)". * * It is executed after the main query execution has finished. * * If query is being executed in async mode, the true result (sucess/fail) will be known from later, not from this method. */ bool countResults(); /** * @brief Gets time of how long it took to execute query. * @return Execution time in milliseconds. * * The execution time is number of milliseconds from begining of the query execution, till receiving of the results. */ qint64 getLastExecutionTime() const; /** * @brief Gets number of rows affected by the query. * @return Affected rows number. * * Rows affected are defined by DbPlugin implementation and are usually a number of rows modified by UPDATE statement, * or deleted by DELETE statement, or inserted by INSERT statement. */ qint64 getRowsAffected() const; /** * @brief Gets number of rows returned by the query. * @return Number of rows. * * If QueryExecutor limits result rows number (if defined by setResultsPerPage()), the actual number of rows * to be returned from query can be larger. This methods returns this true number of rows, * that would be returned from the query. * * Calling this method makes sense only after resultsCountingFinished() was emitted, otherwise the value * returned will not be accurate. */ qint64 getTotalRowsReturned() const; /** * @brief Gets type of the SQL statement in the defined query. * @param index Index of the SQL statement in the query (statements are separated by semicolon character), or -1 to get the last one. * @return Type of the query. If there were no parsed queries in the context, or if passed index is out of range, * then SqliteQueryType::UNDEFINED is returned. */ SqliteQueryType getExecutedQueryType(int index = -1); /** * @brief Provides set of data source tables used in query. * @return Set of tables. */ QSet getSourceTables() const; /** * @brief Gets number of pages available. * @return Number of pages. * * Since QueryExecutor organizes results of the query into pages, this method gives number of pages that is necessary * to display all the data. In other words: "results of this method" - 1 = "last page index". * * Single page size is defined by setResultsPerPage(). */ int getTotalPages() const; /** * @brief Gets ordered list of result columns. * @return Result columns. * * See Context::resultColumns for details. */ QList getResultColumns() const; /** * @brief Gets map of meta columns providing SQLite data types. * @return Map of type column alias to target column alias (for which the type applies). */ QHash getTypeColumns() const; /** * @brief Gets list of ROWID columns. * @return ROWID columns. * * Note, that this returns list of ROWID columns as entities. This means that for ROWID a single ROWID column * can be actually couple of columns in the results. To count the ROWID columns offset for extracting * data columns use getMetaColumnCount(). * * See Context::rowIdColumns for details. */ QList getRowIdResultColumns() const; /** * @brief Gives number of meta columns in the executed query. * @return Number of the actual meta columns (such as ROWID columns) added to the executed query. * * This method should be used to find out the number of meta columns that were added to the begining * of the result columns in the executed query. This way you can learn which column index use as a start * for reading the actual data from the query. * * Meta columns are used by QueryExecutor to find more information about the query being executed * (like ROWID of each row for example). */ int getMetaColumnCount() const; /** * @brief Gets reasons for which editing results is forbidden. * @return Set of reasons. * * See Context::editionForbiddenReasons for details. */ QSet getEditionForbiddenGlobalReasons() const; /** * @brief Defines named bind parameter for the query. * @param name Name of the parameter (without the : or @ prefix). * @param value Value of the parameter. * * Positional (index oriented) parameters are not supported by the QueryExecutor. * Always use named parameters with QueryExecutor. */ void setParam(const QString& name, const QVariant& value); /** * @brief Assigns all query parameters at once. * @param params All bind parameters for the query. * * This is same as setParam(), except it overrides all current params with given ones. * It allows for setting all params at once. */ void setParams(const QHash& params); /** * @brief Gets currently defined database. * @return Database object, or null pointer if not yet defined. */ Db* getDb() const; /** * @brief Defines new database for query execution. * @param value Database object. It should be open before calling exec(). */ void setDb(Db* value); /** * @brief Gets original, not modified query. * @return SQL query string. */ QString getOriginalQuery() const; /** * @brief Gets data size limit. * @return Number of bytes, or UTF-8/UTF-16 characters. * * See setDataLengthLimit() for details. */ int getDataLengthLimit() const; /** * @brief Defines data size limit for results. * @param value Number of bytes, or UTF-8/UTF-16 characters. * * Limit is not defined by default and in that case it's not applied * to the query. To enable limit, set it to any positive number. * To disable limit, set it to any negative number. * * When QueryExecutor prepares query for execution, it applies SUBSTR() * to all result columns, so if the database has a huge value in some column, * SQLiteStudio won't load 1000 rows with huge values - that would kill performance * of the application. Instead it loads small chunk of every value. * * SqlQueryModel loads limited chunks of data and loads on-the-fly full cell values * when user requests it (edits the cell, or views it in form editor). * * Parameter defined by this method is passed to SQLite's SUBSTR() function. * As SQLite's documentation stand, numbers passed to that function are treated * as number of bytes for non-textual data and for textual data they are number * of characters (for UTF-8 and UTF-16 they can be made of more than 1 byte). */ void setDataLengthLimit(int value); // TODO manual row counting -> should be done by query executor already and returned in total rows /** * @brief Tests if manual row counting is required. * @return True if manual counting is required. * * In case of some queries the getTotalRowsReturned() won't provide proper value. * Then you will need to count result rows from the results object. * * It's okay, because this applies only for EXPLAIN and PRAGMA queries, * which will never return any huge row counts. */ bool isRowCountingRequired() const; /** * @brief Gets SQL query used for counting results. * @return SQL query. * * This is the query used by countResults(). */ QString getCountingQuery() const; /** * @brief Gets number of rows per page. * @return Number of rows. * * By default results are not split to pages and this method will return -1. */ int getResultsPerPage() const; /** * @brief Defines number of rows per page. * @param value Number of rows. * * By default results are not split to pages. * See setPage() for details on enabling and disabling paging. */ void setResultsPerPage(int value); /** * @brief Gets current results page. * @return Page index. * * Results page is 0-based index. It's always value between 0 and (getTotalPages() - 1). * If results paging is disabled (see setResultsPerPage()), then this method * will always return 0, as this is the first page (and in that case - the only one). */ int getPage() const; /** * @brief Defines results page for next execution. * @param value 0-based page index. * * If page value is negative, then paging is disabled. * Any positive value or 0 enables paging and sets requested page of results to given page. * * If requested page value is greater than "getTotalPages() - 1", then no results will be returned. * It's an invalid page value. * If requested page value is lower then 0, then paging is disabled. * * Once the page is defined, the exec() must be called to get results * from new defined page. */ void setPage(int value); /** * @brief Tests if there's any execution in progress at the moment. * @return true if the execution is in progress, or false otherwise. */ bool isExecutionInProgress(); /** * @brief Gets sorting defined for executor. * @return Sorting definition. * * See Sort for details. */ QueryExecutor::SortList getSortOrder() const; /** * @brief Defines sorting for next query execution. * @param value Sorting definition. * * Once the sorting definition is changed, the exec() must be called * to receive results in new order. */ void setSortOrder(const QueryExecutor::SortList& value); /** * @brief Tests if row counting is disabled. * @return true if row counting will be skipped, or false otherwise. * * See Context::skipRowCounting for details. */ bool getSkipRowCounting() const; /** * @brief Defines if executor should skip row counting. * @param value New value for this parameter. * * See Context::skipRowCounting for details. */ void setSkipRowCounting(bool value); /** * @brief Asynchronous executor processing in thread. * * This is an implementation of QRunnable::run(), so the QueryExecutor * does it's own asynchronous work on object members. */ void run(); /** * @brief Tests if query execution should be performed in EXPLAIN mode. * @return true if the mode is enabled, or false otherwise. */ bool getExplainMode() const; /** * @brief Defines EXPLAIN mode for next query execution. * @param value true to enable EXPLAIN mode, or false to disable it. * * EXPLAIN mode means simply that the EXPLAIN keyword will be prepended * to the query, except when the query already started with the EXPLAIN keyword. * * Once the mode is changed, the exec() must be called * to receive "explain" results. */ void setExplainMode(bool value); /** * @brief Defines results preloading. * @param value true to preload results. * * Results preloading is disabled by default. See Db::Flag::PRELOAD for details. */ void setPreloadResults(bool value); bool getAsyncMode() const; void setAsyncMode(bool value); SqlQueryPtr getResults() const; bool wasSchemaModified() const; bool wasDataModifyingQuery() const; static QList resolveColumnTypes(Db* db, QList& columns, bool noDbLocking = false); bool getNoMetaColumns() const; void setNoMetaColumns(bool value); void setFilters(const QString& newFilters); QString getFilters() const; void handleErrorsFromSmartAndSimpleMethods(SqlQueryPtr results); /** * @brief Clears results handle and detaches any attached databases. */ void releaseResultsAndCleanup(); const QStringList& getRequiredDbAttaches() const; bool getForceSimpleMode() const; void setForceSimpleMode(bool value); bool isInterrupted() const; int getQueryCountLimitForSmartMode() const; void setQueryCountLimitForSmartMode(int value); /** * @brief Adds new step to the chain of execution * @param position Where in chain should the step be placed. * @param step Step implementation instance. * * If multiple steps are registered for the same position, they will be executed in the order they were registered. * * If step is registered with a plugin, remember to deregister the step upon plugin unload. * Best place for that is in Plugin::deinit(). */ static void registerStep(StepPosition position, QueryExecutorStep* step); /** * @brief Adds new step to chain of execution * @param position Where in chain should the step be placed. * @param stepFactory Factory for creating instance of the step. * * This is overloaded method for cases when the step is stateful and needs to be recreated for every invokation of query executor. */ static void registerStep(StepPosition position, StepFactory* stepFactory); /** * @brief Removes extra step from the execution chain. * @param position Position from which the step should be removed. * @param step Step implementation instance. * * Removes step from list of additional steps to be performed. If such step was not on the list, this method does nothing. * * There is a position parameter to this method, becuase a step could be added to multiple different positions * and you decide from which you want to deregister it. */ static void deregisterStep(StepPosition position, QueryExecutorStep* step); /** * @brief Removes extra step from the execution chain. * @param position Position from which the step should be removed. * @param stepFactory Factory to deregister. * * This is overloaded method to remove factory instead of instance of a step. To be used together with registerStep() * that also takes factory in parameters. */ static void deregisterStep(StepPosition position, StepFactory* stepFactory); private: /** * @brief Executes query. * * It's called from run(). This is the execution of query but called from different * thread than exec() was called from. */ void execInternal(); /** * @brief Raises execution error. * @param code Error code. Can be either from SQLite error codes, or from SqlErrorCode. * @param text Error message. * * This method is called when some of executor's preconditions has failed, or when SQLite * execution raised an error. */ void error(int code, const QString& text); /** * @brief Build chain of executor steps. * * Defines executionChain by adding new QueryExecutorStep descendants. * Each step has its own purpose described in its class documentation. * See inheritance hierarchy of QueryExecutorStep. */ void setupExecutionChain(); /** * @brief Deletes executor step objects. * * Deletes all QueryExecutorStep objects from executionChain clears the list. */ void clearChain(); /** * @brief Executes all steps from executor chain. * * The steps chain is defined by setupExecutionChain(). * On execution error, the stepFailed() is called and the method returns. */ void executeChain(); /** * @brief Executes the original, unmodified query. * * When smart execution (using steps chain) failed, then this method * is a fallback. It executes original query passed to the executor. * Given, that query was not modified, it cannot provide meta information, * therefore results of that execution won't editable. */ void executeSimpleMethod(); /** * @brief Tests whether the original query is a SELECT statement. * @return true if the query is SELECT, or false otherwise. * * This method assumes that there was a problem with parsing the query with the Parser * (and that's why we're using simple execution method) and so it tries to figure out * a query type using other algorithms. */ bool simpleExecIsSelect(); /** * @brief Releases resources acquired during query execution. * * Currently it just detaches databases attached for query execution needs (transparent * database attaching feature). */ void cleanup(); /** * @brief Extracts counting query results. * @param results Results from the counting query execution. * @return true if counting was successful, i.e. there is no ongoing query execution, or false otherwise. * * It may be called from database asynchronous execution thread. * Basicly this method is called a result of countResults() call. Extracts counted number of rows * and stores it in query executor's context. */ bool handleRowCountingResults(SqlQueryPtr results); QStringList applyFiltersAndLimitAndOrderForSimpleMethod(const QStringList &queries); /** * @brief Creates instances of steps for all registered factories for given position. * @param position Position for which factories will be used. * @return List of instances of steps from given factories. */ QList createSteps(StepPosition position); /** * @brief Query executor context object. * * Context object is shared across all execution steps. It's (re)initialized for every * call to exec(). Initialization involves copying configuration parameters (such as sortOrder, * explainMode, etc) from local members to the context. * * During steps execution the context is used to share information between steps. * For example if one step modifies query in anyway, it should store updated query * in Context::processedQuery. See QueryExecutorStep for details on possible methods * for updating Context::processedQuery (you don't always have to build the whole processed * query string by yourself). * * Finally, the context serves as a results container from all steps. QueryExecutor reads * result columns metadata, total rows number, affected rows and other information from the context. */ Context* context = nullptr; /** * @brief Database that all queries will be executed on. * * It can be passed in constructor or defined later with setDb(), but it cannot be null * when calling exec(). The exec() will simply return with no execution performed * and will log a warning. */ Db* db = nullptr; /** * @brief Synchronization mutex for "execution in progress" state of executor. * * The state of "execution in progress" is the only value synchronized between threads. * It makes sure that single QueryExecutor will execute only one query at the time. */ mutable QMutex executionMutex; mutable QMutex interruptionMutex; /** * @brief Query to execute as defined by the user. * * This is a copy of original query provided by user to the executor. */ QString originalQuery; QStringList queriesForSimpleExecution; /** * @brief Predefined number of results per page. * * See setResultsPerPage() for details. */ int resultsPerPage = -1; /** * @brief Predefined results page index. * * See setPage() for details. */ int page = -1; /** * @brief Predefined sorting order. * * There's no sorting predefined by default. If you want it, you have to apply it with setSortOrder(). */ SortList sortOrder; /** * @brief Flag indicating that the execution is currently in progress. * * This variable is synchronized across threads and therefore you can always ask QueryExecutor * if it's currently busy (with isExecutionInProgress()). */ bool executionInProgress = false; /** * @brief Flag indicating that the most recent execution was made in "simple mode". * * This flag is set by executeSimpleMethod() method. See its documentation for details. * The exec() resets this flag to false each time, but each time the smart execution fails, * the executeSimpleMethod() is called and the flag is set to true. */ bool simpleExecution = false; /** * @brief Flag indicating that the most recent execution was interrupted. * * This flag is set only if execution was interrupted by call to interrupt() on this class. * If the execution was interrupted by another thread (which called sqlite_interrupt() * or Db::interrupt()), then this flag is not set. * * This variable is tested at several stages of query execution in order to abort * execution if the interruption was already requested. */ bool interrupted = false; /** * @brief Flag indicating that the execution is performed in EXPLAIN mode. * * See setExplainMode() for details. */ bool explainMode = false; /** * @brief Flag indicating that the row counting was disabled. * * See Context::skipRowCounting for details. */ bool skipRowCounting = false; /** * @brief Defines results data size limit. * * See setDataLengthLimit() for details. */ int dataLengthLimit = -1; /** * @brief Optional filters to apply to the query. * If not empty, it will be appended to the WHERE clause at the very end of execution chain, * skipping complex result colum analysis, etc. */ QString filters; /** * @brief Limit of queries, after which simple mode is used. * * Up to the defined limit the smart execution will be used (unless #forceSimpleMode was set). * After exceeding this limit, the simple mode will be used. * Set to negative number to disable this limit. */ int queryCountLimitForSmartMode = -1; /** * @brief Exact moment when query execution started. * * Expressed in number of milliseconds since 1970-01-01 00:00:00. */ qint64 simpleExecutionStartTime; /** * @brief Flag indicating results preloading. * * See Context::preloadResults. */ bool preloadResults = false; /** * @brief Determinates if asynchronous mode is used. * * By default QueryExecutor runs in asynchronous mode (in another thread). * You can set this to false to make exec() work synchronously, on calling thread. */ bool asyncMode = true; /** * @brief Defines if the QueryExecutor will provide meta columns in the results. * * Set to true to forbid providing meta columns, or leave as false to let QueryExecutor * provide meta columns. * * Meta columns are additional columns that are not part of the query that was passed to the executor. * Those are for example ROWID columns (currently those are the only meta columns). * * You can always find out number of ROWID columns from getRowIdResultColumns(). * * Meta columns are placed always at the begining. */ bool noMetaColumns = false; /** * @brief List of required databases to attach. * * List of database names (symbolic names, as they appear on the list) that needs to be * attached to access all columns returned by the most recent successful execution. * * This is set after every successful execution. */ QStringList requiredDbAttaches; /** * @brief Chain of executor steps. * * Executor step list is set up by setupExecutionChain() and cleaned up after * execution is finished. Steps are executed in order they appear in this list. * * Steps are executed one by one and if any of them raises the error, * execution stops and error from QueryExecutor is raised (with executionFailed() signal). */ QList executionChain; /** * @brief List of registered additional steps to be performed * * It's a map, where keys describe where in chain should the steps be placed * and values are list of steps to be inserted at that position. * * They are added/removed by methods: registerStep() and deregisterStep(). */ static QHash> additionalStatelessSteps; /** * @brief List of all registered additional steps * * This is a list of all registered additional steps (registered instances of steps), regardless of their position. * This is used to identify registered stateless steps, so they are not deleted on query executor cleanup. * * Only steps created with a factory are deleted upon executor cleanup (and of course all internal steps created by the executor itself). */ static QList allAdditionalStatelsssSteps; /** * @brief List of registered factories that create additional steps to be performed * * This is similar to additionalSteps, except it holds factories, instead of step instances. * * Steps created with a factory are appended after stateless steps registered as direct * instances (held by additionalSteps) - that is for case when both instance and factory * are registered for the same step position. */ static QHash> additionalStatefulStepFactories; /** * @brief Execution results handler. * * This member keeps address of function that was defined for handling results. * It is defined only if exec() method was called with the handler function argument. * * Results handler function is evaluated once the query execution has finished * with success. It's not called on failure. */ Db::QueryResultsHandler resultsHandler = nullptr; /** * @brief Parameters for query execution. * * It's defined by setParam(). */ QHash queryParameters; bool forceSimpleMode = false; ChainExecutor* simpleExecutor = nullptr; Db* countingDb = nullptr; QStringList countingAttaches; signals: /** * @brief Emitted on successful query execution. * @param results Results from query execution. * * It's emitted at the very end of the whole query execution process * and only on successful execution. It doesn't matter if the execution was * performed in "smart mode" or "simple mode". */ void executionFinished(SqlQueryPtr results); /** * @brief Emitted on failed query execution. * @param code Error code. * @param errorMessage Error message. * * It doesn't matter if the execution was performed in "smart mode" or "simple mode". */ void executionFailed(int code, QString errorMessage); /** * @brief Emitted on successful counting query execution. * @param rowsAffected Rows affected by the original query. * @param rowsReturned Rows returned by the original query. * @param totalPages Number of pages needed to represent all rows given the value defined with setResultsPerPage(). * * This signal is emitted only when setSkipRowCounting() was set to false (it is by default) * and the counting query execution was successful. * * The counting query actually counts only \p rowsReturned, while \p rowsAffected and \p totalPages * are extracted from original query execution. */ void resultsCountingFinished(quint64 rowsAffected, quint64 rowsReturned, int totalPages); public slots: /** * @brief Executes given query. * @param originalQuery to be executed. * * This is a shorthand for: * @code * queryExecutor->setQuery(query); * queryExecutor->exec(); * @endcode * * This exec() version is a SLOT, while the other exec() method is not. */ void exec(const QString& originalQuery); private slots: /** * @brief Calledn when an executor step has failed with its job. * * An executor step reported an error. "Smart execution" failed and now the executor will try * to execute query with a "simple method". */ void stepFailed(QueryExecutorStep *currentStep); /** * @brief Cleanup routines after failed query execution. * @param code Error code. * @param errorMessage Error message. * * Releases resources that are no longer used. Currently simply calls cleanup(). */ void cleanupAfterExecFailed(int code, QString errorMessage); /** * @brief Called when the currently set db is about to be destroyed. * * Deletes results from the Context if there were any, so they are not referencing anything * from deleted Db. Keeping results is dangerous, becuase the Db driver (plugin) is most likely to * be unloaded soon and we won't be able to call results destructor. */ void cleanupBeforeDbDestroy(Db* dbToBeUnloaded); /** * @brief Handles results of simple execution. * @param results Results object returned from Db. * * Checks results for errors and extracts basic meta information, * such as rows affected, total result rows and time of execution. * * In case of success emits executionFinished(), in case of error emits executionFailed(). */ void simpleExecutionFinished(SqlQueryPtr results); }; int qHash(QueryExecutor::EditionForbiddenReason reason); int qHash(QueryExecutor::ColumnEditionForbiddenReason reason); int qHash(QueryExecutor::SourceTable sourceTable); int operator==(const QueryExecutor::SourceTable& t1, const QueryExecutor::SourceTable& t2); #endif // QUERYEXECUTOR_H