diff --git a/autotests/parser/SqlParserTest.cpp b/autotests/parser/SqlParserTest.cpp --- a/autotests/parser/SqlParserTest.cpp +++ b/autotests/parser/SqlParserTest.cpp @@ -256,8 +256,9 @@ } else { m_errorStream << fname << ':' << lineNum << ' ' << message << endl; if (parser->query()) { - qDebug() << *parser->query(); - m_errorStream << KDbUtils::debugString(*parser->query()) << endl; + const KDbConnectionAndQuerySchema connQuery(parser->connection(), *parser->query()); + qDebug() << connQuery; + m_errorStream << KDbUtils::debugString(connQuery) << endl; } } QVERIFY2(ok, qPrintable(message)); diff --git a/src/KDb.h b/src/KDb.h --- a/src/KDb.h +++ b/src/KDb.h @@ -35,7 +35,6 @@ class KDbEscapedString; class KDbLookupFieldSchema; class KDbMessageHandler; -class KDbQuerySchema; class KDbResultable; class KDbResultInfo; class KDbTableOrQuerySchema; @@ -226,63 +225,6 @@ int objType); /** - * @brief Returns number of records returned by given SQL statement - * - * @return number of records that can be retrieved after executing @a sql statement within - * a connection @a conn. The statement should be of type SELECT. For SQL data sources it does not - * fetch any records, only "COUNT(*)" SQL aggregation is used at the backed. - * -1 is returned if any error occurred or if @a conn is @c nullptr. - */ -//! @todo perhaps use quint64 here? -KDB_EXPORT int recordCount(KDbConnection* conn, const KDbEscapedString& sql); - -/** - * @brief Returns number of records that contains given table - * - * @return number of records that can be retrieved from @a tableSchema. - * To obtain the result the table must be created or retrieved using a KDbConnection object, - * i.e. tableSchema.connection() must not return @c nullptr. For SQL data sources only "COUNT(*)" - * SQL aggregation is used at the backed. - * -1 is returned if error occurred or if tableSchema.connection() is @c nullptr. - */ -//! @todo perhaps use quint64 here? -KDB_EXPORT int recordCount(const KDbTableSchema& tableSchema); - -/** - * @overload - * - * Operates on a query schema. @a params are optional values of parameters that will be inserted - * into [] placeholders before execution of query that counts the records. - * To obtain the result the query must be created or retrieved using a KDbConnection object, - * i.e. querySchema->connection() must not return @c nullptr. For SQL data sources only "COUNT(*)" - * SQL aggregation is used at the backed. - * -1 is returned if error occurred or if querySchema->connection() is @c nullptr. - */ -//! @todo perhaps use quint64 here? -KDB_EXPORT int recordCount(KDbQuerySchema* querySchema, - const QList& params = QList()); - -/** - * @overload - * - * Operates on a table or query schema. @a params is a list of optional parameters that - * will be inserted into [] placeholders before execution of query that counts the records. - * - * If @a tableOrQuery is @c nullptr or provides neither table nor query, -1 is returned. - */ -//! @todo perhaps use quint64 here? -KDB_EXPORT int recordCount(KDbTableOrQuerySchema* tableOrQuery, - const QList& params = QList()); - -/** - * @brief Returns number of columns within record set returned from specified table or query - * - * In case of query expanded fields list is counted. - * Returns -1 if @a tableOrQuery is @c nullptr or has neither table or query assigned. - */ -KDB_EXPORT int fieldCount(KDbTableOrQuerySchema* tableOrQuery); - -/** * @brief Shows connection test dialog * * Shows connection test dialog with a progress bar indicating connection testing diff --git a/src/KDb.cpp b/src/KDb.cpp --- a/src/KDb.cpp +++ b/src/KDb.cpp @@ -599,73 +599,6 @@ return result == QDialog::Accepted; } -int KDb::recordCount(KDbConnection* conn, const KDbEscapedString& sql) -{ - int count = -1; //will be changed only on success of querySingleNumber() - if (conn) { - (void)conn->querySingleNumber( - KDbEscapedString("SELECT COUNT() FROM (") + sql + ") AS kdb__subquery", &count); - } - return count; -} - -int KDb::recordCount(const KDbTableSchema& tableSchema) -{ -//! @todo does not work with non-SQL data sources - if (!tableSchema.connection()) { - kdbWarning() << "no tableSchema.connection()"; - return -1; - } - int count = -1; //will be changed only on success of querySingleNumber() - tableSchema.connection()->querySingleNumber( - KDbEscapedString("SELECT COUNT(*) FROM ") - + tableSchema.connection()->escapeIdentifier(tableSchema.name()), - &count - ); - return count; -} - -int KDb::recordCount(KDbQuerySchema* querySchema, const QList& params) -{ -//! @todo does not work with non-SQL data sources - if (!querySchema->connection()) { - kdbWarning() << "no querySchema->connection()"; - return -1; - } - int count = -1; //will be changed only on success of querySingleNumber() - KDbNativeStatementBuilder builder(querySchema->connection()); - KDbEscapedString subSql; - if (!builder.generateSelectStatement(&subSql, querySchema, params)) { - return -1; - } - tristate result = querySchema->connection()->querySingleNumber( - KDbEscapedString("SELECT COUNT(*) FROM (") + subSql + ") AS kdb__subquery", &count - ); - return true == result ? count : -1; -} - -int KDb::recordCount(KDbTableOrQuerySchema* tableOrQuery, const QList& params) -{ - if (tableOrQuery) { - if (tableOrQuery->table()) - return recordCount(*tableOrQuery->table()); - if (tableOrQuery->query()) - return recordCount(tableOrQuery->query(), params); - } - return -1; -} - -int KDb::fieldCount(KDbTableOrQuerySchema* tableOrQuery) -{ - if (tableOrQuery) { - if (tableOrQuery->table()) - return tableOrQuery->table()->fieldCount(); - if (tableOrQuery->query()) - return tableOrQuery->query()->fieldsExpanded().count(); - } - return -1; -} - bool KDb::splitToTableAndFieldParts(const QString& string, QString *tableName, QString *fieldName, SplitToTableAndFieldPartsOptions option) diff --git a/src/KDbConnection.h b/src/KDbConnection.h --- a/src/KDbConnection.h +++ b/src/KDbConnection.h @@ -898,6 +898,64 @@ bool isInternalTableSchema(const QString& tableName); + /** + * @brief Returns number of records returned by given SQL statement + * + * @return number of records that can be retrieved after executing @a sql statement within + * a connection @a conn. The statement should be of type SELECT. For SQL data sources it does not + * fetch any records, only "COUNT(*)" SQL aggregation is used at the backed. + * -1 is returned if any error occurred or if @a conn is @c nullptr. + * + * @since 3.1 + */ + //! @todo perhaps use quint64 here? + KDB_EXPORT int recordCount(const KDbEscapedString& sql); + + /** + * @brief Returns number of records that contains given table + * + * @return number of records that can be retrieved from @a tableSchema. + * To obtain the result the table must be created or retrieved using a KDbConnection object, + * i.e. tableSchema.connection() must not return @c nullptr. For SQL data sources only "COUNT(*)" + * SQL aggregation is used at the backed. + * -1 is returned if error occurred or if tableSchema.connection() is @c nullptr. + * + * @since 3.1 + */ + //! @todo perhaps use quint64 here? + //! @todo does not work with non-SQL data sources + KDB_EXPORT int recordCount(const KDbTableSchema& tableSchema); + + /** + * @overload + * + * Operates on a query schema. @a params are optional values of parameters that will be inserted + * into [] placeholders before execution of query that counts the records. + * To obtain the result the query must be created or retrieved using a KDbConnection object, + * i.e. querySchema->connection() must not return @c nullptr. For SQL data sources only "COUNT(*)" + * SQL aggregation is used at the backed. + * -1 is returned if error occurred or if querySchema->connection() is @c nullptr. + * + * @since 3.1 + */ + //! @todo perhaps use quint64 here? + KDB_EXPORT int recordCount(KDbQuerySchema* querySchema, + const QList& params = QList()); + + /** + * @overload + * + * Operates on a table or query schema. @a params is a list of optional parameters that + * will be inserted into [] placeholders before execution of query that counts the records. + * + * If @a tableOrQuery is @c nullptr or provides neither table nor query, -1 is returned. + * + * @since 3.1 + */ + //! @todo perhaps use quint64 here? + KDB_EXPORT int recordCount(KDbTableOrQuerySchema* tableOrQuery, + const QList& params = QList()); + //! Identifier escaping function in the associated KDbDriver. /*! Calls the identifier escaping function in this connection to escape table and column names. This should be used when explicitly @@ -1252,6 +1310,8 @@ To avoid having deleted table object on its list. */ void removeMe(KDbTableSchema *ts); + // -- internal methods follow + /*! @internal @return true if the cursor @a cursor contains column @a column, else, sets appropriate error with a message and returns false. */ diff --git a/src/KDbConnection.cpp b/src/KDbConnection.cpp --- a/src/KDbConnection.cpp +++ b/src/KDbConnection.cpp @@ -32,6 +32,7 @@ #include "KDbRelationship.h" #include "KDbSqlRecord.h" #include "KDbSqlResult.h" +#include "KDbTableOrQuerySchema.h" #include "KDbTableSchemaChangeListener.h" #include "KDbTransactionData.h" #include "KDbTransactionGuard.h" @@ -2939,7 +2940,7 @@ if (q) return q; //not found: retrieve schema - QScopedPointer newQuery(KDbQuerySchema::Private::createQuery(this)); + QScopedPointer newQuery(new KDbQuerySchema); clearResult(); if (true != loadObjectData(KDb::QueryObjectType, aQueryName, newQuery.data())) { return nullptr; @@ -2953,7 +2954,7 @@ if (q) return q; //not found: retrieve schema - QScopedPointer newQuery(KDbQuerySchema::Private::createQuery(this)); + QScopedPointer newQuery(new KDbQuerySchema); clearResult(); if (true != loadObjectData(KDb::QueryObjectType, queryId, newQuery.data())) { return nullptr; @@ -3005,11 +3006,13 @@ } //! @internal used in updateRecord(), insertRecord(), -inline static void updateRecordDataWithNewValues(KDbQuerySchema* query, KDbRecordData* data, - const KDbRecordEditBuffer::DbHash& b, - QHash* columnsOrderExpanded) +inline static void updateRecordDataWithNewValues( + KDbConnection *conn, KDbQuerySchema* query, KDbRecordData* data, + const KDbRecordEditBuffer::DbHash& b, + QHash* columnsOrderExpanded) { - *columnsOrderExpanded = query->columnsOrder(KDbQuerySchema::ExpandedList); + *columnsOrderExpanded + = query->columnsOrder(conn, KDbQuerySchema::ColumnsOrderMode::ExpandedList); QHash::ConstIterator columnsOrderExpandedIt; for (KDbRecordEditBuffer::DbHash::ConstIterator it = b.constBegin();it != b.constEnd();++it) { columnsOrderExpandedIt = columnsOrderExpanded->constFind(it.key()); @@ -3071,9 +3074,9 @@ d->driver->valueToSql(currentField, it.value()); } if (pkey) { - const QVector pkeyFieldsOrder(query->pkeyFieldsOrder()); + const QVector pkeyFieldsOrder(query->pkeyFieldsOrder(this)); //kdbDebug() << pkey->fieldCount() << " ? " << query->pkeyFieldCount(); - if (pkey->fieldCount() != query->pkeyFieldCount()) { //sanity check + if (pkey->fieldCount() != query->pkeyFieldCount(this)) { //sanity check kdbWarning() << " -- NO ENTIRE MASTER TABLE's PKEY SPECIFIED!"; m_result = KDbResult(ERR_UPDATE_NO_ENTIRE_MASTER_TABLES_PKEY, tr("Could not update record because it does not contain entire primary key of master table.")); @@ -3120,7 +3123,7 @@ } //success: now also assign new values in memory: QHash columnsOrderExpanded; - updateRecordDataWithNewValues(query, data, b, &columnsOrderExpanded); + updateRecordDataWithNewValues(this, query, data, b, &columnsOrderExpanded); return true; } @@ -3157,7 +3160,8 @@ KDbRecordEditBuffer::DbHash b = buf->dbBuffer(); // add default values, if available (for any column without value explicitly set) - const KDbQueryColumnInfo::Vector fieldsExpanded(query->fieldsExpanded(KDbQuerySchema::Unique)); + const KDbQueryColumnInfo::Vector fieldsExpanded( + query->fieldsExpanded(this, KDbQuerySchema::FieldsExpandedMode::Unique)); int fieldsExpandedCount = fieldsExpanded.count(); for (int i = 0; i < fieldsExpandedCount; i++) { KDbQueryColumnInfo *ci = fieldsExpanded.at(i); @@ -3182,9 +3186,9 @@ return false; } if (pkey) { - const QVector pkeyFieldsOrder(query->pkeyFieldsOrder()); + const QVector pkeyFieldsOrder(query->pkeyFieldsOrder(this)); // kdbDebug() << pkey->fieldCount() << " ? " << query->pkeyFieldCount(); - if (pkey->fieldCount() != query->pkeyFieldCount()) { // sanity check + if (pkey->fieldCount() != query->pkeyFieldCount(this)) { // sanity check kdbWarning() << "NO ENTIRE MASTER TABLE's PKEY SPECIFIED!"; m_result = KDbResult(ERR_INSERT_NO_ENTIRE_MASTER_TABLES_PKEY, tr("Could not insert record because it does not contain " @@ -3235,10 +3239,10 @@ } //success: now also assign a new value in memory: QHash columnsOrderExpanded; - updateRecordDataWithNewValues(query, data, b, &columnsOrderExpanded); + updateRecordDataWithNewValues(this, query, data, b, &columnsOrderExpanded); //fetch autoincremented values - KDbQueryColumnInfo::List *aif_list = query->autoIncrementFields(); + KDbQueryColumnInfo::List *aif_list = query->autoIncrementFields(this); quint64 recordId = 0; if (pkey && !aif_list->isEmpty()) { //! @todo now only if PKEY is present, this should also work when there's no PKEY @@ -3315,9 +3319,9 @@ sqlwhere.reserve(1024); if (pkey) { - const QVector pkeyFieldsOrder(query->pkeyFieldsOrder()); + const QVector pkeyFieldsOrder(query->pkeyFieldsOrder(this)); //kdbDebug() << pkey->fieldCount() << " ? " << query->pkeyFieldCount(); - if (pkey->fieldCount() != query->pkeyFieldCount()) { //sanity check + if (pkey->fieldCount() != query->pkeyFieldCount(this)) { //sanity check kdbWarning() << " -- NO ENTIRE MASTER TABLE's PKEY SPECIFIED!"; m_result = KDbResult(ERR_DELETE_NO_ENTIRE_MASTER_TABLES_PKEY, tr("Could not delete record because it does not contain entire master table's primary key.")); @@ -3376,6 +3380,59 @@ return true; } +int KDbConnection::recordCount(const KDbEscapedString& sql) +{ + int count = -1; //will be changed only on success of querySingleNumber() + const tristate result = querySingleNumber( + KDbEscapedString("SELECT COUNT() FROM (") + sql + ") AS kdb__subquery", &count); + if (~result) { + count = 0; + } + return count; +} + +int KDbConnection::recordCount(const KDbTableSchema& tableSchema) +{ + //! @todo does not work with non-SQL data sources + int count = -1; // will be changed only on success of querySingleNumber() + const tristate result + = querySingleNumber(KDbEscapedString("SELECT COUNT(*) FROM ") + + tableSchema.connection()->escapeIdentifier(tableSchema.name()), + &count); + if (~result) { + count = 0; + } + return count; +} + +int KDbConnection::recordCount(KDbQuerySchema* querySchema, const QList& params) +{ +//! @todo does not work with non-SQL data sources + int count = -1; //will be changed only on success of querySingleNumber() + KDbNativeStatementBuilder builder(this); + KDbEscapedString subSql; + if (!builder.generateSelectStatement(&subSql, querySchema, params)) { + return -1; + } + const tristate result = querySingleNumber( + KDbEscapedString("SELECT COUNT(*) FROM (") + subSql + ") AS kdb__subquery", &count); + if (~result) { + count = 0; + } + return count; +} + +int KDbConnection::recordCount(KDbTableOrQuerySchema* tableOrQuery, const QList& params) +{ + if (tableOrQuery) { + if (tableOrQuery->table()) + return recordCount(*tableOrQuery->table()); + if (tableOrQuery->query()) + return recordCount(tableOrQuery->query(), params); + } + return -1; +} + KDbConnectionOptions* KDbConnection::options() { return &d->options; diff --git a/src/KDbCursor.h b/src/KDbCursor.h --- a/src/KDbCursor.h +++ b/src/KDbCursor.h @@ -77,7 +77,11 @@ Q_DECLARE_FLAGS(Options, Option) /*! @return connection used for the cursor */ - KDbConnection* connection() const; + KDbConnection* connection(); + + //! @overload + //! @since 3.1 + const KDbConnection* connection() const; /*! Opens the cursor using data provided on creation. The data might be either KDbQuerySchema or a raw SQL statement. */ @@ -329,6 +333,10 @@ }; //! Sends information about object @a cursor to debug output @a dbg. +//! @since 3.1 +KDB_EXPORT QDebug operator<<(QDebug dbg, KDbCursor& cursor); + +//! Sends information about object @a cursor to debug output @a dbg. KDB_EXPORT QDebug operator<<(QDebug dbg, const KDbCursor& cursor); Q_DECLARE_OPERATORS_FOR_FLAGS(KDbCursor::Options) diff --git a/src/KDbCursor.cpp b/src/KDbCursor.cpp --- a/src/KDbCursor.cpp +++ b/src/KDbCursor.cpp @@ -105,11 +105,11 @@ if (m_query) { //get list of all fields m_visibleFieldsExpanded = new KDbQueryColumnInfo::Vector(); - *m_visibleFieldsExpanded = m_query->visibleFieldsExpanded( - d->containsRecordIdInfo ? KDbQuerySchema::WithInternalFieldsAndRecordId - : KDbQuerySchema::WithInternalFields); + *m_visibleFieldsExpanded = m_query->visibleFieldsExpanded(conn, + d->containsRecordIdInfo ? KDbQuerySchema::FieldsExpandedMode::WithInternalFieldsAndRecordId + : KDbQuerySchema::FieldsExpandedMode::WithInternalFields); m_logicalFieldCount = m_visibleFieldsExpanded->count() - - m_query->internalFields().count() - (d->containsRecordIdInfo ? 1 : 0); + - m_query->internalFields(conn).count() - (d->containsRecordIdInfo ? 1 : 0); m_fieldCount = m_visibleFieldsExpanded->count(); m_fieldsToStoreInRecord = m_fieldCount; } else { @@ -145,7 +145,12 @@ return d->readAhead; } -KDbConnection* KDbCursor::connection() const +KDbConnection* KDbCursor::connection() +{ + return d->conn; +} + +const KDbConnection* KDbCursor::connection() const { return d->conn; } @@ -527,14 +532,14 @@ return d->conn->deleteAllRecords(m_query); } -QDebug operator<<(QDebug dbg, const KDbCursor& cursor) +QDebug debug(QDebug dbg, KDbCursor& cursor, bool buildSql) { dbg.nospace() << "CURSOR("; if (!cursor.query()) { dbg.nospace() << "RAW SQL STATEMENT:" << cursor.rawSql().toString() << "\n"; } - else { + else if (buildSql) { KDbNativeStatementBuilder builder(cursor.connection()); KDbEscapedString sql; QString sqlString; @@ -562,6 +567,16 @@ return dbg.space(); } +QDebug operator<<(QDebug dbg, KDbCursor &cursor) +{ + return debug(dbg, cursor, true /*buildSql*/); +} + +QDebug operator<<(QDebug dbg, const KDbCursor &cursor) +{ + return debug(dbg, const_cast(cursor), false /* !buildSql*/); +} + void KDbCursor::setOrderByColumnList(const QStringList& columnNames) { Q_UNUSED(columnNames); diff --git a/src/KDbField.cpp b/src/KDbField.cpp --- a/src/KDbField.cpp +++ b/src/KDbField.cpp @@ -111,6 +111,16 @@ //------------------------------------------------------- +class KDbFieldPrivate +{ +public: + static KDbConnection *connection(const KDbTableSchema &table) + { + return table.connection(); + } +}; + + class Q_DECL_HIDDEN KDbField::Private { public: @@ -917,7 +927,7 @@ void debug(QDebug dbg, const KDbField& field, KDbFieldDebugOptions options) { - KDbConnection *conn = field.table() ? field.table()->connection() : nullptr; + const KDbConnection *conn = field.table() ? KDbFieldPrivate::connection(*field.table()) : nullptr; if (options & KDbFieldDebugAddName) { if (field.name().isEmpty()) { dbg.nospace() << " "; diff --git a/src/KDbNativeStatementBuilder.cpp b/src/KDbNativeStatementBuilder.cpp --- a/src/KDbNativeStatementBuilder.cpp +++ b/src/KDbNativeStatementBuilder.cpp @@ -164,7 +164,7 @@ // "LEFT OUTER JOIN lookupTable ON thisTable.thisField=lookupTable.boundField" KDbLookupFieldSchemaRecordSource recordSource = lookupFieldSchema->recordSource(); if (recordSource.type() == KDbLookupFieldSchemaRecordSource::Table) { - KDbTableSchema *lookupTable = querySchema->connection()->tableSchema(recordSource.name()); + KDbTableSchema *lookupTable = connection->tableSchema(recordSource.name()); KDbFieldList* visibleColumns = nullptr; KDbField *boundField = nullptr; if (lookupTable @@ -196,12 +196,13 @@ } delete visibleColumns; } else if (recordSource.type() == KDbLookupFieldSchemaRecordSource::Query) { - KDbQuerySchema *lookupQuery = querySchema->connection()->querySchema(recordSource.name()); + KDbQuerySchema *lookupQuery = connection->querySchema(recordSource.name()); if (!lookupQuery) { kdbWarning() << "!lookupQuery"; return false; } - const KDbQueryColumnInfo::Vector fieldsExpanded(lookupQuery->fieldsExpanded()); + const KDbQueryColumnInfo::Vector fieldsExpanded( + lookupQuery->fieldsExpanded(connection)); if (lookupFieldSchema->boundColumn() >= fieldsExpanded.count()) { kdbWarning() << "lookupFieldSchema->boundColumn() >= fieldsExpanded.count()"; return false; @@ -365,11 +366,11 @@ querySchema->orderByColumnList()->toSqlString( !singleTable/*includeTableName*/, connection, driver ? KDb::DriverEscaping : KDb::KDbEscaping) ); - const QVector pkeyFieldsOrder(querySchema->pkeyFieldsOrder()); + const QVector pkeyFieldsOrder(querySchema->pkeyFieldsOrder(connection)); if (orderByString.isEmpty() && !pkeyFieldsOrder.isEmpty()) { //add automatic ORDER BY if there is no explicitly defined (especially helps when there are complex JOINs) KDbOrderByColumnList automaticPKOrderBy; - const KDbQueryColumnInfo::Vector fieldsExpanded(querySchema->fieldsExpanded()); + const KDbQueryColumnInfo::Vector fieldsExpanded(querySchema->fieldsExpanded(connection)); foreach(int pkeyFieldsIndex, pkeyFieldsOrder) { if (pkeyFieldsIndex < 0) // no field mentioned in this query continue; diff --git a/src/KDbOrderByColumn.h b/src/KDbOrderByColumn.h --- a/src/KDbOrderByColumn.h +++ b/src/KDbOrderByColumn.h @@ -61,7 +61,8 @@ In @a fromQuery and @a toQuery is needed if column() is assigned to this info. Then, column info within @a toQuery will be assigned to the new KDbOrderByColumn object, corresponding to column() from "this" KDbOrderByColumn object. */ - KDbOrderByColumn* copy(KDbQuerySchema* fromQuery, KDbQuerySchema* toQuery) const; + KDbOrderByColumn *copy(KDbConnection *conn, KDbQuerySchema *fromQuery, + KDbQuerySchema *toQuery) const; //! A column to sort. KDbQueryColumnInfo* column() const; @@ -117,7 +118,7 @@ KDbOrderByColumnList(); /*! A copy constructor. */ - KDbOrderByColumnList(const KDbOrderByColumnList& other, + KDbOrderByColumnList(const KDbOrderByColumnList& other, KDbConnection *conn, KDbQuerySchema* fromQuery, KDbQuerySchema* toQuery); ~KDbOrderByColumnList(); @@ -142,7 +143,7 @@ @return false if there is at least one name for which a field or alias name does not exist (all the newly appended fields are removed in this case) Returns @c false if @a querySchema is @c nullptr. */ - bool appendFields(KDbQuerySchema* querySchema, + bool appendFields(KDbConnection *conn, KDbQuerySchema* querySchema, const QString& field1, KDbOrderByColumn::SortOrder order1 = KDbOrderByColumn::SortOrder::Ascending, const QString& field2 = QString(), KDbOrderByColumn::SortOrder order2 = KDbOrderByColumn::SortOrder::Ascending, const QString& field3 = QString(), KDbOrderByColumn::SortOrder order3 = KDbOrderByColumn::SortOrder::Ascending, @@ -165,13 +166,13 @@ @return @c true on successful appending, and @c false if there is no such field or alias name in the @a querySchema. Returns @c false if @a querySchema is @c nullptr. */ - bool appendField(KDbQuerySchema* querySchema, const QString& fieldName, + bool appendField(KDbConnection *conn, KDbQuerySchema* querySchema, const QString& fieldName, KDbOrderByColumn::SortOrder order = KDbOrderByColumn::SortOrder::Ascending); /*! Appends a column that is at position @a pos (counted from 0). @return true on successful adding and false if there is no such position @a pos. Returns @c false if @a querySchema is @c nullptr. */ - bool appendColumn(KDbQuerySchema* querySchema, + bool appendColumn(KDbConnection *conn, KDbQuerySchema* querySchema, KDbOrderByColumn::SortOrder order = KDbOrderByColumn::SortOrder::Ascending, int pos = -1); diff --git a/src/KDbOrderByColumn.cpp b/src/KDbOrderByColumn.cpp --- a/src/KDbOrderByColumn.cpp +++ b/src/KDbOrderByColumn.cpp @@ -86,20 +86,21 @@ delete d; } -KDbOrderByColumn* KDbOrderByColumn::copy(KDbQuerySchema* fromQuery, KDbQuerySchema* toQuery) const +KDbOrderByColumn *KDbOrderByColumn::copy(KDbConnection *conn, KDbQuerySchema *fromQuery, + KDbQuerySchema *toQuery) const { if (d->field) { return new KDbOrderByColumn(d->field, d->order); } if (d->column) { KDbQueryColumnInfo* columnInfo; if (fromQuery && toQuery) { - int columnIndex = fromQuery->columnsOrder().value(d->column); + int columnIndex = fromQuery->columnsOrder(conn).value(d->column); if (columnIndex < 0) { kdbWarning() << "Index not found for column" << *d->column; return nullptr; } - columnInfo = toQuery->expandedOrInternalField(columnIndex); + columnInfo = toQuery->expandedOrInternalField(conn, columnIndex); if (!columnInfo) { kdbWarning() << "Column info not found at index" << columnIndex << "in toQuery"; return nullptr; @@ -224,14 +225,14 @@ { } -KDbOrderByColumnList::KDbOrderByColumnList(const KDbOrderByColumnList& other, +KDbOrderByColumnList::KDbOrderByColumnList(const KDbOrderByColumnList& other, KDbConnection *conn, KDbQuerySchema* fromQuery, KDbQuerySchema* toQuery) : KDbOrderByColumnList() { for (QList::ConstIterator it(other.constBegin()); it != other.constEnd(); ++it) { - KDbOrderByColumn* order = (*it)->copy(fromQuery, toQuery); + KDbOrderByColumn* order = (*it)->copy(conn, fromQuery, toQuery); if (order) { d->data.append(order); } @@ -258,7 +259,7 @@ return d->data.value(index); } -bool KDbOrderByColumnList::appendFields(KDbQuerySchema* querySchema, +bool KDbOrderByColumnList::appendFields(KDbConnection *conn, KDbQuerySchema* querySchema, const QString& field1, KDbOrderByColumn::SortOrder order1, const QString& field2, KDbOrderByColumn::SortOrder order2, const QString& field3, KDbOrderByColumn::SortOrder order3, @@ -271,7 +272,7 @@ int numAdded = 0; #define ADD_COL(fieldName, order) \ if (ok && !fieldName.isEmpty()) { \ - if (!appendField( querySchema, fieldName, order )) \ + if (!appendField(conn, querySchema, fieldName, order)) \ ok = false; \ else \ numAdded++; \ @@ -300,13 +301,13 @@ } } -bool KDbOrderByColumnList::appendColumn(KDbQuerySchema* querySchema, +bool KDbOrderByColumnList::appendColumn(KDbConnection *conn, KDbQuerySchema* querySchema, KDbOrderByColumn::SortOrder order, int pos) { if (!querySchema) { return false; } - KDbQueryColumnInfo::Vector fieldsExpanded(querySchema->fieldsExpanded()); + KDbQueryColumnInfo::Vector fieldsExpanded(querySchema->fieldsExpanded(conn)); if (pos < 0 || pos >= fieldsExpanded.size()) { return false; } @@ -322,13 +323,13 @@ } } -bool KDbOrderByColumnList::appendField(KDbQuerySchema* querySchema, +bool KDbOrderByColumnList::appendField(KDbConnection *conn, KDbQuerySchema* querySchema, const QString& fieldName, KDbOrderByColumn::SortOrder order) { if (!querySchema) { return false; } - KDbQueryColumnInfo *columnInfo = querySchema->columnInfo(fieldName); + KDbQueryColumnInfo *columnInfo = querySchema->columnInfo(conn, fieldName); if (columnInfo) { d->data.append(new KDbOrderByColumn(columnInfo, order)); return true; diff --git a/src/KDbQuerySchema.h b/src/KDbQuerySchema.h --- a/src/KDbQuerySchema.h +++ b/src/KDbQuerySchema.h @@ -64,7 +64,7 @@ /*! Copy constructor. Creates deep copy of @a querySchema. KDbQueryAsterisk objects are deeply copied while only pointers to KDbField objects are copied. */ - KDbQuerySchema(const KDbQuerySchema& querySchema); + KDbQuerySchema(const KDbQuerySchema& querySchema, KDbConnection *conn); ~KDbQuerySchema() override; @@ -191,10 +191,6 @@ @see KDbFieldList::clear() */ void clear() override; - /*! If query was created using a connection, - returns this connection object, otherwise @c nullptr. */ - KDbConnection* connection() const; - /*! @return table that is master to this query. All potentially-editable columns within this query belong just to this table. This method also can return @c nullptr if there are no tables at all, @@ -375,6 +371,14 @@ /*! @return list of KDbQueryAsterisk objects defined for this query */ KDbField::List* asterisks() const; + //! Mode for field() and columnInfo() + //! @since 3.1 + enum class ExpandMode { + Unexpanded, //!< All fields are returned even if duplicated + Expanded //!< Expanded list of the query fields is computed so queries with asterisks + //!< are processed well + }; + /*! @return field for @a identifier or @c nullptr if no field for this name was found within the query. fieldsExpanded() method is used to lookup expanded list of the query fields, so queries with asterisks @@ -401,27 +405,20 @@ This method is also a product of inheritance from KDbFieldList. */ - const KDbField* field(const QString& identifier) const override; - - /** - * @overload const KDbField* field(const QString& identifier) const - */ - KDbField* field(const QString& identifier) override; + const KDbField *field(KDbConnection *conn, const QString &identifier, + ExpandMode mode = ExpandMode::Expanded) const; /** - * An overloaded method KDbField* field(const QString& identifier) - * where unexpanded list of fields is used to find a field. + * @overload */ - const KDbField* unexpandedField(const QString& identifier) const; - - /** - * @overload const KDbField* unexpandedField(const QString& identifier) const - */ - KDbField* unexpandedField(const QString& identifier); + KDbField *field(KDbConnection *conn, const QString &identifier, + ExpandMode mode = ExpandMode::Expanded); /*! @return field id or @c nullptr if there is no such a field. */ KDbField* field(int id) override; + using KDbFieldList::field; + /*! @overload KDbField* field(int id) */ const KDbField* field(int id) const override; @@ -437,10 +434,12 @@ calling columnInfo("name") for "SELECT t1.name, t2.name FROM t1, t2" statement will only return the column related to t1.name and not t2.name, so you'll need to explicitly specify "t2.name" as the identifier to get the second column. */ - KDbQueryColumnInfo* columnInfo(const QString& identifier, bool expanded = true) const; + KDbQueryColumnInfo *columnInfo(KDbConnection *conn, const QString &identifier, + ExpandMode mode = ExpandMode::Expanded) const; - /*! Options used in fieldsExpanded() and visibleFieldsExpanded(). */ - enum FieldsExpandedOptions { + //! Mode for fieldsExpanded() and visibleFieldsExpanded() + //! @since 3.1 + enum class FieldsExpandedMode { Default, //!< All fields are returned even if duplicated Unique, //!< Unique list of fields is returned WithInternalFields, //!< Like Default but internal fields (for lookup) are appended @@ -490,29 +489,30 @@ @todo js: UPDATE CACHE! */ inline KDbQueryColumnInfo::Vector fieldsExpanded( - FieldsExpandedOptions options = Default) const + KDbConnection *conn, FieldsExpandedMode mode = FieldsExpandedMode::Default) const { - return fieldsExpandedInternal(options, false); + return fieldsExpandedInternal(conn, mode, false); } /*! Like fieldsExpanded() but returns only visible fields. */ inline KDbQueryColumnInfo::Vector visibleFieldsExpanded( - FieldsExpandedOptions options = Default) const + KDbConnection *conn, FieldsExpandedMode options = FieldsExpandedMode::Default) const { - return fieldsExpandedInternal(options, true); + return fieldsExpandedInternal(conn, options, true); } /*! @return list of internal fields used for lookup columns. */ - KDbQueryColumnInfo::Vector internalFields() const; + KDbQueryColumnInfo::Vector internalFields(KDbConnection *conn) const; /*! @return info for expanded of internal field at index @a index. The returned field can be either logical or internal (for lookup), the latter case is @c true if @a index >= fieldsExpanded().count(). Equivalent of KDbQuerySchema::fieldsExpanded(WithInternalFields).at(index). */ - KDbQueryColumnInfo* expandedOrInternalField(int index) const; + KDbQueryColumnInfo* expandedOrInternalField(KDbConnection *conn, int index) const; - /*! Options used in columnsOrder(). */ - enum ColumnsOrderOptions { + //! Mode for columnsOrder() + //! @since 3.1 + enum class ColumnsOrderMode { UnexpandedList, //!< A map for unexpanded list is created UnexpandedListWithoutAsterisks, //!< A map for unexpanded list is created, with asterisks skipped ExpandedList //!< A map for expanded list is created @@ -547,7 +547,8 @@ - columnsOrder(UnexpandedListWithoutAsterisks) will return the following map: KDbQueryColumnInfo(id)->0, */ - QHash columnsOrder(ColumnsOrderOptions options = ExpandedList) const; + QHash columnsOrder(KDbConnection *conn, + ColumnsOrderMode mode = ColumnsOrderMode::ExpandedList) const; /*! @return table describing order of primary key (PKEY) fields within the query. Indexing is performed against vector returned by fieldsExpanded(). @@ -571,7 +572,7 @@ @see example for pkeyFieldCount(). @todo js: UPDATE CACHE! */ - QVector pkeyFieldsOrder() const; + QVector pkeyFieldsOrder(KDbConnection *conn) const; /*! @return number of master table's primary key fields included in this query. This method is useful to quickly check whether the vector returned by pkeyFieldsOrder() @@ -591,13 +592,13 @@ and pkeyFieldsOrder() will return vector {-1, 1}, as second primary key's field is at position #1 and first field is not specified at all within the query. */ - int pkeyFieldCount(); + int pkeyFieldCount(KDbConnection *conn); /*! @return a list of field infos for all auto-incremented fields from master table of this query. This result is cached for efficiency. fieldsExpanded() is used for that. */ - KDbQueryColumnInfo::List* autoIncrementFields() const; + KDbQueryColumnInfo::List* autoIncrementFields(KDbConnection *conn) const; /*! @return a preset statement (if any). */ KDbEscapedString statement() const; @@ -681,7 +682,7 @@ /*! @return query schema parameters. These are taked from the WHERE section (a tree of expression items). */ - QList parameters() const; + QList parameters(KDbConnection *conn) const; //! @return @c true if this query is valid /*! Detailed validation is performed in the same way as parsing of query statements @@ -702,14 +703,12 @@ class Private; // Protected not private because of the parser protected: - //! @internal associates @a conn with this query so it's possible to find tables - explicit KDbQuerySchema(KDbConnection *conn); - - void computeFieldsExpanded() const; + void computeFieldsExpanded(KDbConnection *conn) const; - //! Used by fieldsExpanded(FieldsExpandedOptions) - //! and visibleFieldsExpanded(FieldsExpandedOptions options). - KDbQueryColumnInfo::Vector fieldsExpandedInternal(FieldsExpandedOptions options, + //! Used by fieldsExpanded(KDbConnection*, FieldsExpandedMode) + //! and visibleFieldsExpanded(KDbConnection*, FieldsExpandedMode). + KDbQueryColumnInfo::Vector fieldsExpandedInternal(KDbConnection *conn, + FieldsExpandedMode mode, bool onlyVisible) const; /** Internal method used by all insert*Field methods. @@ -735,10 +734,17 @@ */ void setWhereExpressionInternal(const KDbExpression &expr); + Q_DISABLE_COPY(KDbQuerySchema) Private * const d; }; -//! Sends query schema information @a query to debug output @a dbg. -KDB_EXPORT QDebug operator<<(QDebug dbg, const KDbQuerySchema& query); +//! A pair (connection, table-or-schema) for QDebug operator<< +//! @since 3.1 +typedef std::tuple KDbConnectionAndQuerySchema; + +//! Sends connection and query schema information @a connectionAndSchema to debug output @a dbg. +//! @since 3.1 +KDB_EXPORT QDebug operator<<(QDebug dbg, + const KDbConnectionAndQuerySchema &connectionAndSchema); #endif diff --git a/src/KDbQuerySchema.cpp b/src/KDbQuerySchema.cpp --- a/src/KDbQuerySchema.cpp +++ b/src/KDbQuerySchema.cpp @@ -56,7 +56,6 @@ { if (tableSchema) { d->masterTable = tableSchema; - d->conn = tableSchema->connection(); /*if (!d->masterTable) { kdbWarning() << "!d->masterTable"; m_name.clear(); @@ -76,7 +75,7 @@ } } -KDbQuerySchema::KDbQuerySchema(const KDbQuerySchema& querySchema) +KDbQuerySchema::KDbQuerySchema(const KDbQuerySchema& querySchema, KDbConnection *conn) : KDbFieldList(querySchema, false /* !deepCopyFields */) , KDbObject(querySchema) , d(new Private(this, querySchema.d)) @@ -96,16 +95,8 @@ addField(copiedField); } // this deep copy must be after the 'd' initialization because fieldsExpanded() is used there - d->orderByColumnList = new KDbOrderByColumnList(*querySchema.d->orderByColumnList, - const_cast(&querySchema), this); -} - -KDbQuerySchema::KDbQuerySchema(KDbConnection *conn) - : KDbFieldList(false)//fields are not owned by KDbQuerySchema object - , KDbObject(KDb::QueryObjectType) - , d(new Private(this)) -{ - d->conn = conn; + d->orderByColumnList = new KDbOrderByColumnList(*querySchema.d->orderByColumnList, conn, + const_cast(&querySchema), this); } KDbQuerySchema::~KDbQuerySchema() @@ -316,19 +307,10 @@ return addAsteriskInternal(asterisk, false); } -KDbConnection* KDbQuerySchema::connection() const -{ - if (d->conn) { - return d->conn; - } - if (!d->tables.isEmpty()) { - return d->tables.first()->connection(); - } - return nullptr; -} - -QDebug operator<<(QDebug dbg, const KDbQuerySchema& query) +QDebug operator<<(QDebug dbg, const KDbConnectionAndQuerySchema &connectionAndSchema) { + KDbConnection* conn = std::get<0>(connectionAndSchema); + const KDbQuerySchema& query = std::get<1>(connectionAndSchema); //fields KDbTableSchema *mt = query.masterTable(); dbg.nospace() << "QUERY"; @@ -348,7 +330,7 @@ int fieldsExpandedCount = 0; bool first; if (query.fieldCount() > 0) { - const KDbQueryColumnInfo::Vector fe(query.fieldsExpanded()); + const KDbQueryColumnInfo::Vector fe(query.fieldsExpanded(conn)); fieldsExpandedCount = fe.size(); dbg.nospace() << fieldsExpandedCount << "):\n"; first = true; @@ -683,26 +665,17 @@ d->sql = sql; } -const KDbField* KDbQuerySchema::field(const QString& identifier) const +const KDbField* KDbQuerySchema::field(KDbConnection *conn, const QString& identifier, + ExpandMode mode) const { - KDbQueryColumnInfo *ci = columnInfo(identifier, true /*expanded*/); + KDbQueryColumnInfo *ci = columnInfo(conn, identifier, mode); return ci ? ci->field() : nullptr; } -KDbField* KDbQuerySchema::field(const QString& identifier) +KDbField* KDbQuerySchema::field(KDbConnection *conn, const QString& identifier, ExpandMode mode) { - return const_cast(static_cast(this)->field(identifier)); -} - -const KDbField* KDbQuerySchema::unexpandedField(const QString& identifier) const -{ - KDbQueryColumnInfo *ci = columnInfo(identifier, false /*unexpanded*/); - return ci ? ci->field() : nullptr; -} - -KDbField* KDbQuerySchema::unexpandedField(const QString& identifier) -{ - return const_cast(static_cast(this)->unexpandedField(identifier)); + return const_cast( + static_cast(this)->field(conn, identifier, mode)); } KDbField* KDbQuerySchema::field(int id) @@ -715,32 +688,39 @@ return KDbFieldList::field(id); } -KDbQueryColumnInfo* KDbQuerySchema::columnInfo(const QString& identifier, bool expanded) const +KDbQueryColumnInfo *KDbQuerySchema::columnInfo(KDbConnection *conn, const QString &identifier, + ExpandMode mode) const { - computeFieldsExpanded(); - return expanded ? d->columnInfosByNameExpanded.value(identifier) - : d->columnInfosByName.value(identifier); + computeFieldsExpanded(conn); + return mode == ExpandMode::Expanded ? d->columnInfosByNameExpanded.value(identifier) + : d->columnInfosByName.value(identifier); } KDbQueryColumnInfo::Vector KDbQuerySchema::fieldsExpandedInternal( - FieldsExpandedOptions options, bool onlyVisible) const + KDbConnection *conn, FieldsExpandedMode mode, bool onlyVisible) const { - computeFieldsExpanded(); - KDbQueryColumnInfo::Vector *realFieldsExpanded = onlyVisible ? d->visibleFieldsExpanded - : d->fieldsExpanded; - if (options == WithInternalFields || options == WithInternalFieldsAndRecordId) { + if (!conn) { + kdbWarning() << "Connection required"; + return KDbQueryColumnInfo::Vector(); + } + computeFieldsExpanded(conn); + KDbQueryColumnInfo::Vector *realFieldsExpanded + = onlyVisible ? d->visibleFieldsExpanded : d->fieldsExpanded; + if (mode == FieldsExpandedMode::WithInternalFields + || mode == FieldsExpandedMode::WithInternalFieldsAndRecordId) + { //a ref to a proper pointer (as we cache the vector for two cases) KDbQueryColumnInfo::Vector*& tmpFieldsExpandedWithInternal = - (options == WithInternalFields) ? + (mode == FieldsExpandedMode::WithInternalFields) ? (onlyVisible ? d->visibleFieldsExpandedWithInternal : d->fieldsExpandedWithInternal) : (onlyVisible ? d->visibleFieldsExpandedWithInternalAndRecordId : d->fieldsExpandedWithInternalAndRecordId); //special case if (!tmpFieldsExpandedWithInternal) { //glue expanded and internal fields and cache it const int internalFieldCount = d->internalFields ? d->internalFields->size() : 0; const int fieldsExpandedVectorSize = realFieldsExpanded->size(); const int size = fieldsExpandedVectorSize + internalFieldCount - + ((options == WithInternalFieldsAndRecordId) ? 1 : 0) /*ROWID*/; + + ((mode == FieldsExpandedMode::WithInternalFieldsAndRecordId) ? 1 : 0) /*ROWID*/; tmpFieldsExpandedWithInternal = new KDbQueryColumnInfo::Vector(size); for (int i = 0; i < fieldsExpandedVectorSize; ++i) { (*tmpFieldsExpandedWithInternal)[i] = realFieldsExpanded->at(i); @@ -751,7 +731,7 @@ (*tmpFieldsExpandedWithInternal)[fieldsExpandedVectorSize + i] = info; } } - if (options == WithInternalFieldsAndRecordId) { + if (mode == FieldsExpandedMode::WithInternalFieldsAndRecordId) { if (!d->fakeRecordIdField) { d->fakeRecordIdField = new KDbField(QLatin1String("rowID"), KDbField::BigInteger); d->fakeRecordIdCol = new KDbQueryColumnInfo(d->fakeRecordIdField, QString(), true); @@ -762,11 +742,11 @@ return *tmpFieldsExpandedWithInternal; } - if (options == Default) { + if (mode == FieldsExpandedMode::Default) { return *realFieldsExpanded; } - //options == Unique: + //mode == Unique: QSet columnsAlreadyFound; const int fieldsExpandedCount(realFieldsExpanded->count()); KDbQueryColumnInfo::Vector result(fieldsExpandedCount); //initial size is set @@ -783,15 +763,15 @@ return result; } -KDbQueryColumnInfo::Vector KDbQuerySchema::internalFields() const +KDbQueryColumnInfo::Vector KDbQuerySchema::internalFields(KDbConnection *conn) const { - computeFieldsExpanded(); + computeFieldsExpanded(conn); return d->internalFields ? *d->internalFields : KDbQueryColumnInfo::Vector(); } -KDbQueryColumnInfo* KDbQuerySchema::expandedOrInternalField(int index) const +KDbQueryColumnInfo* KDbQuerySchema::expandedOrInternalField(KDbConnection *conn, int index) const { - return fieldsExpanded(WithInternalFields).value(index); + return fieldsExpanded(conn, FieldsExpandedMode::WithInternalFields).value(index); } inline static QString lookupColumnKey(KDbField *foreignField, KDbField* field) @@ -803,7 +783,7 @@ + QLatin1Char('.') + foreignField->name(); } -void KDbQuerySchema::computeFieldsExpanded() const +void KDbQuerySchema::computeFieldsExpanded(KDbConnection *conn) const { if (d->fieldsExpanded) return; @@ -872,7 +852,7 @@ // "LEFT OUTER JOIN lookupTable ON thisTable.thisField=lookupTable.boundField" KDbLookupFieldSchemaRecordSource recordSource = lookupFieldSchema->recordSource(); if (recordSource.type() == KDbLookupFieldSchemaRecordSource::Table) { - KDbTableSchema *lookupTable = connection()->tableSchema(recordSource.name()); + KDbTableSchema *lookupTable = conn->tableSchema(recordSource.name()); KDbFieldList* visibleColumns = nullptr; KDbField *boundField = nullptr; if (lookupTable @@ -913,10 +893,11 @@ } delete visibleColumns; } else if (recordSource.type() == KDbLookupFieldSchemaRecordSource::Query) { - KDbQuerySchema *lookupQuery = connection()->querySchema(recordSource.name()); + KDbQuerySchema *lookupQuery = conn->querySchema(recordSource.name()); if (!lookupQuery) continue; - const KDbQueryColumnInfo::Vector lookupQueryFieldsExpanded(lookupQuery->fieldsExpanded()); + const KDbQueryColumnInfo::Vector lookupQueryFieldsExpanded( + lookupQuery->fieldsExpanded(conn)); if (lookupFieldSchema->boundColumn() >= lookupQueryFieldsExpanded.count()) continue; KDbQueryColumnInfo *boundColumnInfo = nullptr; @@ -1095,7 +1076,7 @@ continue; const KDbLookupFieldSchemaRecordSource recordSource = lookupFieldSchema->recordSource(); if (recordSource.type() == KDbLookupFieldSchemaRecordSource::Table) { - KDbTableSchema *lookupTable = connection()->tableSchema(recordSource.name()); + KDbTableSchema *lookupTable = conn->tableSchema(recordSource.name()); KDbFieldList* visibleColumns = nullptr; if (lookupTable && lookupFieldSchema->boundColumn() < lookupTable->fieldCount() @@ -1118,10 +1099,11 @@ } delete visibleColumns; } else if (recordSource.type() == KDbLookupFieldSchemaRecordSource::Query) { - KDbQuerySchema *lookupQuery = connection()->querySchema(recordSource.name()); + KDbQuerySchema *lookupQuery = conn->querySchema(recordSource.name()); if (!lookupQuery) continue; - const KDbQueryColumnInfo::Vector lookupQueryFieldsExpanded(lookupQuery->fieldsExpanded()); + const KDbQueryColumnInfo::Vector lookupQueryFieldsExpanded( + lookupQuery->fieldsExpanded(conn)); if (lookupFieldSchema->boundColumn() >= lookupQueryFieldsExpanded.count()) continue; KDbQueryColumnInfo *boundColumnInfo = nullptr; @@ -1154,18 +1136,21 @@ } } -QHash KDbQuerySchema::columnsOrder(ColumnsOrderOptions options) const +QHash KDbQuerySchema::columnsOrder(KDbConnection *conn, + ColumnsOrderMode mode) const { - if (!d->columnsOrder) - computeFieldsExpanded(); - if (options == UnexpandedList) + if (!d->columnsOrder) { + computeFieldsExpanded(conn); + } + if (mode == ColumnsOrderMode::UnexpandedList) { return *d->columnsOrder; - else if (options == UnexpandedListWithoutAsterisks) + } else if (mode == ColumnsOrderMode::UnexpandedListWithoutAsterisks) { return *d->columnsOrderWithoutAsterisks; + } return *d->columnsOrderExpanded; } -QVector KDbQuerySchema::pkeyFieldsOrder() const +QVector KDbQuerySchema::pkeyFieldsOrder(KDbConnection *conn) const { if (d->pkeyFieldsOrder) return *d->pkeyFieldsOrder; @@ -1179,7 +1164,7 @@ kdbDebug() << *pkey; d->pkeyFieldsOrder = new QVector(pkey->fieldCount(), -1); - const int fCount = fieldsExpanded().count(); + const int fCount = fieldsExpanded(conn).count(); d->pkeyFieldCount = 0; for (int i = 0; i < fCount; i++) { KDbQueryColumnInfo *fi = d->fieldsExpanded->at(i); @@ -1196,9 +1181,9 @@ return *d->pkeyFieldsOrder; } -int KDbQuerySchema::pkeyFieldCount() +int KDbQuerySchema::pkeyFieldCount(KDbConnection *conn) { - (void)pkeyFieldsOrder(); /* rebuild information */ + (void)pkeyFieldsOrder(conn); /* rebuild information */ return d->pkeyFieldCount; } @@ -1215,7 +1200,7 @@ return r; } -KDbQueryColumnInfo::List* KDbQuerySchema::autoIncrementFields() const +KDbQueryColumnInfo::List* KDbQuerySchema::autoIncrementFields(KDbConnection *conn) const { if (!d->autoincFields) { d->autoincFields = new KDbQueryColumnInfo::List(); @@ -1226,7 +1211,7 @@ return d->autoincFields; } if (d->autoincFields->isEmpty()) {//no cache - KDbQueryColumnInfo::Vector fexp = fieldsExpanded(); + KDbQueryColumnInfo::Vector fexp = fieldsExpanded(conn); for (int i = 0; i < fexp.count(); i++) { KDbQueryColumnInfo *ci = fexp[i]; if (ci->field()->table() == mt && ci->field()->isAutoIncrement()) { @@ -1238,8 +1223,9 @@ } // static -KDbEscapedString KDbQuerySchema::sqlColumnsList(const KDbQueryColumnInfo::List& infolist, KDbConnection *conn, - KDb::IdentifierEscapingType escapingType) +KDbEscapedString KDbQuerySchema::sqlColumnsList(const KDbQueryColumnInfo::List &infolist, + KDbConnection *conn, + KDb::IdentifierEscapingType escapingType) { KDbEscapedString result; result.reserve(256); @@ -1261,7 +1247,7 @@ if ( /*d->lastUsedDriverForAutoIncrementSQLFieldsList != driverWeakPointer ||*/ d->autoIncrementSqlFieldsList.isEmpty()) { - d->autoIncrementSqlFieldsList = KDbQuerySchema::sqlColumnsList(*autoIncrementFields(), conn); + d->autoIncrementSqlFieldsList = KDbQuerySchema::sqlColumnsList(*autoIncrementFields(conn), conn); //d->lastUsedDriverForAutoIncrementSQLFieldsList = driverWeakPointer; } return d->autoIncrementSqlFieldsList; @@ -1365,7 +1351,7 @@ void KDbQuerySchema::setOrderByColumnList(const KDbOrderByColumnList& list) { delete d->orderByColumnList; - d->orderByColumnList = new KDbOrderByColumnList(list, nullptr, nullptr); + d->orderByColumnList = new KDbOrderByColumnList(list, nullptr, nullptr, nullptr); // all field names should be found, exit otherwise ..........? } @@ -1379,10 +1365,10 @@ return d->orderByColumnList; } -QList KDbQuerySchema::parameters() const +QList KDbQuerySchema::parameters(KDbConnection *conn) const { QList params; - const KDbQueryColumnInfo::Vector fieldsExpanded(this->fieldsExpanded()); + const KDbQueryColumnInfo::Vector fieldsExpanded(this->fieldsExpanded(conn)); for (int i = 0; i < fieldsExpanded.count(); ++i) { KDbQueryColumnInfo *ci = fieldsExpanded[i]; if (!ci->field()->expression().isNull()) { diff --git a/src/KDbQuerySchema_p.h b/src/KDbQuerySchema_p.h --- a/src/KDbQuerySchema_p.h +++ b/src/KDbQuerySchema_p.h @@ -99,10 +99,6 @@ KDbField *fakeRecordIdField; //! used to mark a place for record Id KDbQueryColumnInfo *fakeRecordIdCol; //! used to mark a place for record Id - //! Connection on which this query operates - //! @todo use equivalent of QPointer - KDbConnection* conn; - protected: void tryRegenerateExprAliases(); diff --git a/src/KDbQuerySchema_p.cpp b/src/KDbQuerySchema_p.cpp --- a/src/KDbQuerySchema_p.cpp +++ b/src/KDbQuerySchema_p.cpp @@ -25,7 +25,6 @@ , masterTable(nullptr) , fakeRecordIdField(nullptr) , fakeRecordIdCol(nullptr) - , conn(nullptr) , maxIndexWithAlias(-1) , visibility(64) , fieldsExpanded(nullptr) @@ -70,7 +69,6 @@ pkeyFieldsOrder = nullptr; fakeRecordIdCol = nullptr; fakeRecordIdField = nullptr; - conn = nullptr; ownedVisibleColumns = nullptr; // if (!copy->whereExpr.isNull()) { @@ -112,12 +110,6 @@ delete visibleFieldsExpandedWithInternal; } -//static -KDbQuerySchema* KDbQuerySchema::Private::createQuery(KDbConnection *conn) -{ - return new KDbQuerySchema(conn); -} - void KDbQuerySchema::Private::clear() { columnAliases.clear(); diff --git a/src/KDbTableOrQuerySchema.h b/src/KDbTableOrQuerySchema.h --- a/src/KDbTableOrQuerySchema.h +++ b/src/KDbTableOrQuerySchema.h @@ -85,33 +85,46 @@ //! @return caption (if present) or name of the table/query QString captionOrName() const; - //! @return number of fields - int fieldCount() const; + /** + * @brief Returns number of columns within record set returned from specified table or query + * + * In case of query expanded fields list is counted. + * For tables @a conn is not required. + * Returns -1 if the object has neither table or query assigned. + * Returns -1 if the object has query assigned but @a conn is @c nullptr. + */ + int fieldCount(KDbConnection *conn) const; + + /*! Mode for columns(). */ + enum class ColumnsMode { + NonUnique, //!< Non-unique columns are returned + Unique //!< Unique columns are returned + }; //! @return all columns for the table or the query - const KDbQueryColumnInfo::Vector columns(bool unique = false); + const KDbQueryColumnInfo::Vector columns(KDbConnection *conn, ColumnsMode mode = ColumnsMode::NonUnique); /*! @return a field of the table or the query schema for name @a name or 0 if there is no such field. */ KDbField* field(const QString& name); /*! Like KDbField* field(const QString& name); but returns all information associated with field/column @a name. */ - KDbQueryColumnInfo* columnInfo(const QString& name); - - /*! @return connection object, for table or query or 0 if there's no table or query defined. */ - KDbConnection* connection() const; + KDbQueryColumnInfo* columnInfo(KDbConnection *conn, const QString& name); private: class Private; Private * const d; Q_DISABLE_COPY(KDbTableOrQuerySchema) }; -namespace KDb { -//! Sends information about table or query schema @a schema to debug output @a dbg. -KDB_EXPORT QDebug operator<<(QDebug dbg, const KDbTableOrQuerySchema& schema); -} +//! A pair (connection, table-or-schema) for QDebug operator<< +//! @since 3.1 +typedef std::tuple KDbConnectionAndSchema; + +//! Sends information about table or query schema and connection @a connectionAndSchema to debug output @a dbg. +//! @since 3.1 +KDB_EXPORT QDebug operator<<(QDebug dbg, const KDbConnectionAndSchema &connectionAndSchema); #endif diff --git a/src/KDbTableOrQuerySchema.cpp b/src/KDbTableOrQuerySchema.cpp --- a/src/KDbTableOrQuerySchema.cpp +++ b/src/KDbTableOrQuerySchema.cpp @@ -110,23 +110,27 @@ delete d; } -int KDbTableOrQuerySchema::fieldCount() const +int KDbTableOrQuerySchema::fieldCount(KDbConnection *conn) const { if (d->table) return d->table->fieldCount(); - if (d->query) - return d->query->fieldsExpanded().size(); - return 0; + if (d->query && conn) + return d->query->fieldsExpanded(conn).size(); + return -1; } -const KDbQueryColumnInfo::Vector KDbTableOrQuerySchema::columns(bool unique) +const KDbQueryColumnInfo::Vector KDbTableOrQuerySchema::columns(KDbConnection * conn, ColumnsMode mode) { - if (d->table) - return d->table->query()->fieldsExpanded(unique ? KDbQuerySchema::Unique : KDbQuerySchema::Default); - - if (d->query) - return d->query->fieldsExpanded(unique ? KDbQuerySchema::Unique : KDbQuerySchema::Default); - + if (d->table) { + return d->table->query()->fieldsExpanded(conn, mode == ColumnsMode::Unique + ? KDbQuerySchema::FieldsExpandedMode::Unique + : KDbQuerySchema::FieldsExpandedMode::Default); + } + if (d->query) { + return d->query->fieldsExpanded(conn, mode == ColumnsMode::Unique + ? KDbQuerySchema::FieldsExpandedMode::Unique + : KDbQuerySchema::FieldsExpandedMode::Default); + } kdbWarning() << "no query or table specified!"; return KDbQueryColumnInfo::Vector(); } @@ -158,36 +162,29 @@ return nullptr; } -KDbQueryColumnInfo* KDbTableOrQuerySchema::columnInfo(const QString& name) +KDbQueryColumnInfo* KDbTableOrQuerySchema::columnInfo(KDbConnection *conn, const QString& name) { if (d->table) - return d->table->query()->columnInfo(name); + return d->table->query()->columnInfo(conn, name); if (d->query) - return d->query->columnInfo(name); + return d->query->columnInfo(conn, name); return nullptr; } //! Sends information about table or query schema @a schema to debug output @a dbg. -QDebug operator<<(QDebug dbg, const KDbTableOrQuerySchema& schema) +QDebug operator<<(QDebug dbg, const KDbConnectionAndSchema &connectionAndSchema) { - if (schema.table()) - dbg.nospace() << *schema.table(); - else if (schema.query()) - dbg.nospace() << *schema.query(); + if (std::get<1>(connectionAndSchema).table()) { + dbg.nospace() << *std::get<1>(connectionAndSchema).table(); + } else if (std::get<1>(connectionAndSchema).query()) { + dbg.nospace() << KDbConnectionAndQuerySchema(std::get<0>(connectionAndSchema), + *std::get<1>(connectionAndSchema).query()); + } return dbg.space(); } -KDbConnection* KDbTableOrQuerySchema::connection() const -{ - if (d->table) - return d->table->connection(); - else if (d->query) - return d->query->connection(); - return nullptr; -} - KDbQuerySchema* KDbTableOrQuerySchema::query() const { return d->query; diff --git a/src/KDbTableSchema.h b/src/KDbTableSchema.h --- a/src/KDbTableSchema.h +++ b/src/KDbTableSchema.h @@ -29,6 +29,7 @@ class KDbConnection; class KDbLookupFieldSchema; +class KDbFieldPrivate; /*! Provides information about native database table that can be stored using KDb database engine. @@ -128,10 +129,6 @@ /*! Sends information about fields of this table schema to debug output @a dbg. */ QDebug debugFields(QDebug dbg) const; - /*! @return connection object if table was created/retrieved using a connection, - otherwise 0. */ - KDbConnection* connection() const; - /*! @return true if this is internal KDb's table. Internal tables are hidden in applications (if desired) but are available for schema export/import functionality. @@ -182,6 +179,10 @@ /*! Automatically retrieves table schema via connection. */ explicit KDbTableSchema(KDbConnection *conn, const QString & name = QString()); + /*! @return connection object if table was created/retrieved using a connection, + otherwise @c nullptr. */ + KDbConnection* connection() const; + /*! For KDbConnection. */ void setConnection(KDbConnection* conn); @@ -197,6 +198,7 @@ friend class KDbConnection; friend class KDbNativeStatementBuilder; + friend class KDbFieldPrivate; Q_DISABLE_COPY(KDbTableSchema) }; diff --git a/src/KDbTransaction.h b/src/KDbTransaction.h --- a/src/KDbTransaction.h +++ b/src/KDbTransaction.h @@ -87,7 +87,11 @@ * * @c nullptr is returned for null transactions. */ - KDbConnection* connection() const; + KDbConnection* connection(); + + //! @overload + //! @since 3.1 + const KDbConnection* connection() const; /** * @brief Returns @c true if transaction is active (i.e. started) diff --git a/src/KDbTransaction.cpp b/src/KDbTransaction.cpp --- a/src/KDbTransaction.cpp +++ b/src/KDbTransaction.cpp @@ -101,6 +101,11 @@ return d->connection; } +const KDbConnection *KDbTransactionData::connection() const +{ + return d->connection; +} + //--------------------------------------------------- KDbTransaction::KDbTransaction() @@ -165,11 +170,16 @@ return m_data == other.m_data; } -KDbConnection* KDbTransaction::connection() const +KDbConnection* KDbTransaction::connection() { return m_data ? m_data->connection() : nullptr; } +const KDbConnection* KDbTransaction::connection() const +{ + return const_cast(this)->connection(); +} + bool KDbTransaction::isActive() const { return m_data && m_data->isActive(); diff --git a/src/KDbTransactionData.h b/src/KDbTransactionData.h --- a/src/KDbTransactionData.h +++ b/src/KDbTransactionData.h @@ -57,6 +57,10 @@ //! @return connection for this data KDbConnection *connection(); + //! @overload + //! @since 3.1 + const KDbConnection *connection() const; + #ifdef KDB_TRANSACTIONS_DEBUG //! Helper for debugging, returns value of global transaction data reference counter static int globalCount(); diff --git a/src/parser/KDbParser.h b/src/parser/KDbParser.h --- a/src/parser/KDbParser.h +++ b/src/parser/KDbParser.h @@ -162,18 +162,31 @@ */ KDbTableSchema *table(); + //! @overload + //! @since 3.1 + const KDbTableSchema *table() const; + /** - * @return a pointer to a query schema if 'SELECT ...' statement was parsed + * @return a pointer to a new query schema created by parsing 'SELECT ...' statement * or @c nullptr for any other statements or on error. + * If existing query was supplied to parse() @c nullptr is returned. * @note A proper query schema is returned only once for each successful parse() call, * and the object is owned by the caller. In all other cases nullptr is returned. */ KDbQuerySchema *query(); + //! @overload + //! @since 3.1 + const KDbQuerySchema *query() const; + /** * @return a pointer to the used database connection or @c nullptr if it was not set. */ - KDbConnection *connection() const; + KDbConnection *connection(); + + //! @overload + //! @since 3.1 + const KDbConnection *connection() const; /** * @return detailed information about last error. diff --git a/src/parser/KDbParser.cpp b/src/parser/KDbParser.cpp --- a/src/parser/KDbParser.cpp +++ b/src/parser/KDbParser.cpp @@ -27,7 +27,7 @@ #include -bool parseData(KDbParser *p, const KDbEscapedString &sql); +bool parseData(); //! Cache class ParserStatic @@ -80,14 +80,29 @@ return t; } +const KDbTableSchema *KDbParser::table() const +{ + return const_cast(this)->table(); +} + KDbQuerySchema *KDbParser::query() { KDbQuerySchema *s = d->query; d->query = nullptr; return s; } -KDbConnection *KDbParser::connection() const +const KDbQuerySchema *KDbParser::query() const +{ + return const_cast(this)->query(); +} + +KDbConnection *KDbParser::connection() +{ + return d->connection; +} + +const KDbConnection *KDbParser::connection() const { return d->connection; } @@ -119,9 +134,14 @@ KDbParser *oldParser = globalParser; KDbField *oldField = globalField; - bool res = parseData(this, sql); + globalParser = this; + globalField = nullptr; + bool res = parseData(); globalParser = oldParser; globalField = oldField; + if (query) { // if existing query was supplied to parse() nullptr should be returned by query() + d->query = nullptr; + } return res; } diff --git a/src/parser/KDbParser_p.cpp b/src/parser/KDbParser_p.cpp --- a/src/parser/KDbParser_p.cpp +++ b/src/parser/KDbParser_p.cpp @@ -80,13 +80,15 @@ void KDbParserPrivate::setQuerySchema(KDbQuerySchema *query) { - delete this->query; + if (this->query != query) { + delete this->query; + } this->query = query; } KDbQuerySchema* KDbParserPrivate::createQuery() { - return query ? query : KDbQuerySchema::Private::createQuery(connection); + return query ? query : new KDbQuerySchema; } //------------------------------------- @@ -243,27 +245,23 @@ //! @internal Parses @a data for parser @a p //! @todo Make it REENTRANT -bool parseData(KDbParser *p, const KDbEscapedString &sql) +bool parseData() { - globalParser = p; - globalParser->reset(); - globalField = nullptr; fieldList.clear(); + const KDbEscapedString sql(globalParser->statement()); if (sql.isEmpty()) { KDbParserError err(KDbParser::tr("Error"), KDbParser::tr("No query statement specified."), globalToken, globalCurrentPos); KDbParserPrivate::get(globalParser)->setError(err); yyerror(""); - globalParser = nullptr; return false; } const char *data = sql.constData(); tokenize(data); if (!globalParser->error().type().isEmpty()) { - globalParser = nullptr; return false; } @@ -291,7 +289,6 @@ ok = false; } yylex_destroy(); - globalParser = nullptr; return ok; } @@ -499,15 +496,19 @@ for (;count > 0; --it, --count) /*opposite direction due to parser specifics*/ { - //first, try to find a column name or alias (outside of asterisks) - KDbQueryColumnInfo *columnInfo = querySchema->columnInfo((*it).aliasOrName, false/*outside of asterisks*/); + // first, try to find a column name or alias (outside of asterisks) + KDbQueryColumnInfo *columnInfo = querySchema->columnInfo( + globalParser->connection(), (*it).aliasOrName, + KDbQuerySchema::ExpandMode::Unexpanded /*outside of asterisks*/); if (columnInfo) { orderByColumnList->appendColumn(columnInfo, (*it).order); } else { //failed, try to find a field name within all the tables if ((*it).columnNumber != -1) { - if (!orderByColumnList->appendColumn(querySchema, - (*it).order, (*it).columnNumber - 1)) { + if (!orderByColumnList->appendColumn(globalParser->connection(), + querySchema, (*it).order, + (*it).columnNumber - 1)) + { setError(KDbParser::tr("Could not define sorting. Column at " "position %1 does not exist.") .arg((*it).columnNumber)); diff --git a/src/views/KDbTableViewColumn.h b/src/views/KDbTableViewColumn.h --- a/src/views/KDbTableViewColumn.h +++ b/src/views/KDbTableViewColumn.h @@ -163,9 +163,9 @@ /*! A rich field information for db-aware data. Specifies information for a column that should be visible instead of columnInfo. For example case see - @ref KDbQueryColumnInfo::Vector KDbQuerySchema::fieldsExpanded(KDbQuerySchema::FieldsExpandedOptions options = Default) + @ref KDbQueryColumnInfo::Vector KDbQuerySchema::fieldsExpanded(KDbQuerySchema::FieldsExpandedMode mode = Default) - For not-db-aware data it is always 0. */ + For not-db-aware data it is always @c nullptr. */ KDbQueryColumnInfo* visibleLookupColumnInfo(); //! @overload diff --git a/src/views/KDbTableViewColumn.cpp b/src/views/KDbTableViewColumn.cpp --- a/src/views/KDbTableViewColumn.cpp +++ b/src/views/KDbTableViewColumn.cpp @@ -155,8 +155,7 @@ // - if the query itself is coming from read-only connection, or // - if the query itself is stored (i.e. has connection) and lookup column is defined const bool columnFromMasterTable = query.masterTable() == d->columnInfo->field()->table(); - d->readOnly = !columnFromMasterTable - || (query.connection() && query.connection()->options()->isReadOnly()); + d->readOnly = !columnFromMasterTable; //! @todo remove this when queries become editable ^^^^^^^^^^^^^^ // kdbDebug() << "KDbTableViewColumn: query.masterTable()==" // << (query.masterTable() ? query.masterTable()->name() : "notable") << ", columnInfo->field->table()==" diff --git a/src/views/KDbTableViewData.cpp b/src/views/KDbTableViewData.cpp --- a/src/views/KDbTableViewData.cpp +++ b/src/views/KDbTableViewData.cpp @@ -322,24 +322,27 @@ d->cursor = c; d->containsRecordIdInfo = d->cursor->containsRecordIdInfo(); if (d->cursor && d->cursor->query()) { - const KDbQuerySchema::FieldsExpandedOptions fieldsExpandedOptions - = d->containsRecordIdInfo ? KDbQuerySchema::WithInternalFieldsAndRecordId - : KDbQuerySchema::WithInternalFields; - d->realColumnCount = d->cursor->query()->fieldsExpanded(fieldsExpandedOptions).count(); + const KDbQuerySchema::FieldsExpandedMode fieldsExpandedMode + = d->containsRecordIdInfo ? KDbQuerySchema::FieldsExpandedMode::WithInternalFieldsAndRecordId + : KDbQuerySchema::FieldsExpandedMode::WithInternalFields; + d->realColumnCount = d->cursor->query()->fieldsExpanded( + d->cursor->connection(), fieldsExpandedMode).count(); } else { d->realColumnCount = d->columns.count() + (d->containsRecordIdInfo ? 1 : 0); } // Allocate KDbTableViewColumn objects for each visible query column - const KDbQueryColumnInfo::Vector fields = d->cursor->query()->fieldsExpanded(); + const KDbQueryColumnInfo::Vector fields + = d->cursor->query()->fieldsExpanded(d->cursor->connection()); const int fieldCount = fields.count(); for (int i = 0;i < fieldCount;i++) { KDbQueryColumnInfo *ci = fields[i]; if (ci->isVisible()) { KDbQueryColumnInfo *visibleLookupColumnInfo = nullptr; if (ci->indexForVisibleLookupValue() != -1) { - //Lookup field is defined - visibleLookupColumnInfo = d->cursor->query()->expandedOrInternalField(ci->indexForVisibleLookupValue()); + // Lookup field is defined + visibleLookupColumnInfo = d->cursor->query()->expandedOrInternalField( + d->cursor->connection(), ci->indexForVisibleLookupValue()); } KDbTableViewColumn* col = new KDbTableViewColumn(*d->cursor->query(), ci, visibleLookupColumnInfo); addColumn(col); diff --git a/tests/features/parser_test.h b/tests/features/parser_test.h --- a/tests/features/parser_test.h +++ b/tests/features/parser_test.h @@ -41,7 +41,7 @@ variantParams.append(param.toLocal8Bit()); } if (ok && q) { - cout << qPrintable(KDbUtils::debugString(*q)) << '\n'; + cout << qPrintable(KDbUtils::debugString(KDbConnectionAndQuerySchema(conn, *q))) << '\n'; KDbNativeStatementBuilder builder(conn); KDbEscapedString sql; if (builder.generateSelectStatement(&sql, q, variantParams)) {