diff --git a/src/plugins/reports/KexiDBReportDataSource.cpp b/src/plugins/reports/KexiDBReportDataSource.cpp index 4bea737ad..0ca76bd30 100644 --- a/src/plugins/reports/KexiDBReportDataSource.cpp +++ b/src/plugins/reports/KexiDBReportDataSource.cpp @@ -1,393 +1,393 @@ /* * Kexi Report Plugin * Copyright (C) 2007-2017 by Adam Pigg * Copyright (C) 2017-2018 Jarosław Staniek * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "KexiDBReportDataSource.h" #include "kexireportpart.h" #include #include #include #include #include #include #include #include class Q_DECL_HIDDEN KexiDBReportDataSource::Private { public: explicit Private(KexiReportPartTempData *data) : cursor(0), tempData(data), originalSchema(0), copySchema(0) { } ~Private() { delete copySchema; delete originalSchema; } QString objectName; KDbCursor *cursor; KexiReportPartTempData *tempData; KDbQuerySchema *originalSchema; KDbQuerySchema *copySchema; KDbEscapedString schemaSql; QList currentParams; }; KexiDBReportDataSource::KexiDBReportDataSource(const QString &objectName, const QString &pluginId, KexiReportPartTempData *data) : d(new Private(data)) { d->objectName = objectName; getSchema(pluginId); } void KexiDBReportDataSource::setSorting(const QList& sorting) { if (d->copySchema) { if (sorting.isEmpty()) return; KDbOrderByColumnList order; for (int i = 0; i < sorting.count(); i++) { if (!order.appendField(d->tempData->connection(), d->copySchema, sorting[i].field(), KDbOrderByColumn::fromQt(sorting[i].order()))) { qWarning() << "Cannot set sort field" << i << sorting[i].field(); return; } } d->copySchema->setOrderByColumnList(order); } else { qWarning() << "Unable to sort null schema"; } } void KexiDBReportDataSource::addCondition(const QString &field, const QVariant &value, const QString& relation) { if (d->copySchema) { KDbField *fld = d->copySchema->findTableField(field); if (fld) { if (relation.length() == 1) { QString errorMessage; QString errorDescription; if (!d->copySchema->addToWhereExpression(fld, value, KDbToken(relation.toLatin1()[0]), &errorMessage, &errorDescription)) { qWarning() << "Invalid expression cannot be added to WHERE:" << fld << relation << value; qWarning() << "addToWhereExpression() failed, message=" << errorMessage << "description=" << errorDescription; } } else { qWarning() << "Invalid relation passed in:" << relation; } } } else { qDebug() << "Unable to add expresstion to null schema"; } } KexiDBReportDataSource::~KexiDBReportDataSource() { close(); delete d; } bool KexiDBReportDataSource::open() { if ( d->tempData->connection() && d->cursor == 0 ) { if ( d->objectName.isEmpty() ) { return false; } else if ( d->copySchema) { //qDebug() << "Opening cursor.." // << KDbConnectionAndQuerySchema(d->tempData->connection(), *d->copySchema); d->cursor = d->tempData->connection()->executeQuery(d->copySchema, d->currentParams, KDbCursor::Option::Buffered); } if ( d->cursor ) { qDebug() << "Moving to first record.."; return d->cursor->moveFirst(); } else return false; } return false; } bool KexiDBReportDataSource::close() { if (d->cursor) { const bool ok = d->cursor->close(); d->tempData->connection()->deleteCursor(d->cursor); d->cursor = nullptr; return ok; } return true; } bool KexiDBReportDataSource::getSchema(const QString& pluginId) { if (d->tempData->connection()) { KDbTableSchemaChangeListener::unregisterForChanges(d->tempData->connection(), d->tempData); delete d->originalSchema; d->originalSchema = 0; delete d->copySchema; d->copySchema = 0; KDbTableSchema *table = nullptr; KDbQuerySchema *query = nullptr; if ((pluginId.isEmpty() || pluginId == "org.kexi-project.table") && (table = d->tempData->connection()->tableSchema(d->objectName))) { qDebug() << d->objectName << "is a table.."; d->originalSchema = new KDbQuerySchema(table); } else if ((pluginId.isEmpty() || pluginId == "org.kexi-project.query") && (query = d->tempData->connection()->querySchema(d->objectName))) { qDebug() << d->objectName << "is a query.."; qDebug() << KDbConnectionAndQuerySchema(d->tempData->connection(), *query); d->originalSchema = new KDbQuerySchema(*query, d->tempData->connection()); bool ok; KexiUtils::WaitCursorRemover remover; d->currentParams = KexiQueryParameters::getParameters(0, d->tempData->connection(), d->originalSchema, &ok); if (!ok) { return false; } } if (d->originalSchema) { const KDbNativeStatementBuilder builder(d->tempData->connection(), KDb::DriverEscaping); KDbEscapedString sql; - if (builder.generateSelectStatement(&sql, d->originalSchema)) { + if (builder.generateSelectStatement(&sql, d->originalSchema, d->currentParams)) { qDebug() << "Original:" << sql; } else { qDebug() << "Original: ERROR"; return false; } qDebug() << KDbConnectionAndQuerySchema(d->tempData->connection(), *d->originalSchema); d->copySchema = new KDbQuerySchema(*d->originalSchema, d->tempData->connection()); qDebug() << KDbConnectionAndQuerySchema(d->tempData->connection(), *d->copySchema); - if (builder.generateSelectStatement(&d->schemaSql, d->copySchema)) { + if (builder.generateSelectStatement(&d->schemaSql, d->copySchema, d->currentParams)) { qDebug() << "Copy:" << d->schemaSql; } else { qDebug() << "Copy: ERROR"; return false; } if (table) { KDbTableSchemaChangeListener::registerForChanges(d->tempData->connection(), d->tempData, table); } else if (query) { KDbTableSchemaChangeListener::registerForChanges(d->tempData->connection(), d->tempData, query); } } return true; } return false; } QString KexiDBReportDataSource::sourceName() const { return d->objectName; } int KexiDBReportDataSource::fieldNumber ( const QString &fld ) const { if (!d->cursor || !d->cursor->query()) { return -1; } const KDbQueryColumnInfo::Vector fieldsExpanded(d->cursor->query()->fieldsExpanded( d->tempData->connection(), KDbQuerySchema::FieldsExpandedMode::Unique)); for (int i = 0; i < fieldsExpanded.size(); ++i) { if (0 == QString::compare(fld, fieldsExpanded[i]->aliasOrName(), Qt::CaseInsensitive)) { return i; } } return -1; } QStringList KexiDBReportDataSource::fieldNames() const { if (!d->originalSchema) { return QStringList(); } QStringList names; const KDbQueryColumnInfo::Vector fieldsExpanded(d->originalSchema->fieldsExpanded( d->tempData->connection(), KDbQuerySchema::FieldsExpandedMode::Unique)); for (int i = 0; i < fieldsExpanded.size(); i++) { //! @todo in some Kexi mode captionOrAliasOrName() would be used here (more user-friendly) names.append(fieldsExpanded[i]->aliasOrName()); } return names; } QVariant KexiDBReportDataSource::value (int i) const { if ( d->cursor ) return d->cursor->value ( i ); return QVariant(); } QVariant KexiDBReportDataSource::value ( const QString &fld ) const { int i = fieldNumber ( fld ); if (d->cursor && i >= 0) return d->cursor->value ( i ); return QVariant(); } bool KexiDBReportDataSource::moveNext() { if ( d->cursor ) return d->cursor->moveNext(); return false; } bool KexiDBReportDataSource::movePrevious() { if ( d->cursor ) return d->cursor->movePrev(); return false; } bool KexiDBReportDataSource::moveFirst() { if ( d->cursor ) return d->cursor->moveFirst(); return false; } bool KexiDBReportDataSource::moveLast() { if ( d->cursor ) return d->cursor->moveLast(); return false; } qint64 KexiDBReportDataSource::at() const { if ( d->cursor ) return d->cursor->at(); return 0; } qint64 KexiDBReportDataSource::recordCount() const { if (d->copySchema) { return d->tempData->connection()->recordCount(d->copySchema); } return 1; } double KexiDBReportDataSource::runAggregateFunction(const QString &function, const QString &field, const QMap &conditions) { double numberResult = 0.0; if (d->schemaSql.isEmpty()) { qWarning() << "No query for running aggregate function" << function << field; return numberResult; } KDbEscapedString whereSql; KDbConnection *conn = d->tempData->connection(); if (!conditions.isEmpty()) { for (QMap::ConstIterator it = conditions.constBegin(); it != conditions.constEnd(); ++it) { if (!whereSql.isEmpty()) { whereSql.append(" AND "); } KDbQueryColumnInfo *cinfo = d->copySchema->columnInfo(conn, it.key()); if (!cinfo) { qWarning() << "Could not find column" << it.key() << "for condition" << it.key() << "=" << it.value(); return numberResult; } whereSql.append( KDbEscapedString(d->tempData->connection()->escapeIdentifier(cinfo->aliasOrName())) + " = " + d->tempData->connection()->driver()->valueToSql(cinfo->field(), it.value())); } whereSql.prepend(" WHERE "); } const KDbEscapedString sql = KDbEscapedString("SELECT " + function + "(" + field + ") FROM (" + d->schemaSql + ")" + whereSql); QString stringResult; const tristate res = d->tempData->connection()->querySingleString(sql, &stringResult); if (res != true) { qWarning() << "Failed to execute query for running aggregate function" << function << field; return numberResult; } bool ok; numberResult = stringResult.toDouble(&ok); if (!ok) { qWarning() << "Result of query for running aggregate function" << function << field << "is not a number (" << stringResult << ")"; return numberResult; } return numberResult; } QStringList KexiDBReportDataSource::dataSourceNames() const { //Get the list of queries in the database QStringList qs; if (d->tempData->connection() && d->tempData->connection()->isConnected()) { QList tids = d->tempData->connection()->tableIds(); qs << ""; for (int i = 0; i < tids.size(); ++i) { KDbTableSchema* tsc = d->tempData->connection()->tableSchema(tids[i]); if (tsc) qs << tsc->name(); } QList qids = d->tempData->connection()->queryIds(); qs << ""; for (int i = 0; i < qids.size(); ++i) { KDbQuerySchema* qsc = d->tempData->connection()->querySchema(qids[i]); if (qsc) qs << qsc->name(); } } return qs; } KReportDataSource* KexiDBReportDataSource::create(const QString& source) const { return new KexiDBReportDataSource(source, QString(), d->tempData); }