[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [kexi/3.1] src/plugins/reports: Fix crash in data fetching and aggregation scripts in reports
From:       Jaroslaw Staniek <null () kde ! org>
Date:       2018-05-16 8:23:31
Message-ID: E1fIrid-000760-V5 () code ! kde ! org
[Download RAW message or body]

Git commit 90c3ed3251ddcebee4ac4d2dce1c24c857967bd3 by Jaroslaw Staniek.
Committed on 16/05/2018 at 08:23.
Pushed by staniek into branch '3.1'.

Fix crash in data fetching and aggregation scripts in reports

Summary:
BUG:392753
FIXED-IN:3.1.1

NOTE: Reports can still crash in Design view until 393705 (ORDER BY) is fixed (3.2).

Test Plan:
Test 1. Open report from https://bugs.kde.org/show_bug.cgi?id=392753#c0

Test 2. Create report field with data sources like these and try it:

```
=field.max("....")
=field.min("....")
=field.sum("....")
=field.avg("....")
=field.count("....")
```

Reviewers: piggz

Reviewed By: piggz

Subscribers: Kexi-Devel-list

Tags: #kexi

Differential Revision: https://phabricator.kde.org/D12868

M  +53   -3    src/plugins/reports/KexiDBReportDataSource.cpp
M  +16   -1    src/plugins/reports/KexiDBReportDataSource.h
M  +5    -5    src/plugins/reports/kexireportview.cpp
M  +3    -4    src/plugins/reports/kexireportview.h
M  +7    -56   src/plugins/reports/krscriptfunctions.cpp
M  +8    -6    src/plugins/reports/krscriptfunctions.h

https://commits.kde.org/kexi/90c3ed3251ddcebee4ac4d2dce1c24c857967bd3

diff --git a/src/plugins/reports/KexiDBReportDataSource.cpp \
b/src/plugins/reports/KexiDBReportDataSource.cpp index dfe92268a..fdeddbc47 100644
--- a/src/plugins/reports/KexiDBReportDataSource.cpp
+++ b/src/plugins/reports/KexiDBReportDataSource.cpp
@@ -1,7 +1,7 @@
 /*
 * Kexi Report Plugin
 * Copyright (C) 2007-2017 by Adam Pigg <adam@piggz.co.uk>
-* Copyright (C) 2017 Jarosław Staniek <staniek@kde.org>
+* Copyright (C) 2017-2018 Jarosław Staniek <staniek@kde.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
@@ -48,6 +48,7 @@ public:
     KexiReportPartTempData *tempData;
     KDbQuerySchema *originalSchema;
     KDbQuerySchema *copySchema;
+    KDbEscapedString schemaSql;
 };
 
 KexiDBReportDataSource::KexiDBReportDataSource(const QString &objectName, const \
QString &pluginId, @@ -185,8 +186,8 @@ bool KexiDBReportDataSource::getSchema(const \
QString& pluginId)  
             d->copySchema = new KDbQuerySchema(*d->originalSchema, \
                d->tempData->connection());
             qDebug() << KDbConnectionAndQuerySchema(d->tempData->connection(), \
                *d->copySchema);
-            if (builder.generateSelectStatement(&sql, d->copySchema)) {
-                qDebug() << "Copy:" << sql;
+            if (builder.generateSelectStatement(&d->schemaSql, d->copySchema)) {
+                qDebug() << "Copy:" << d->schemaSql;
             } else {
                 qDebug() << "Copy: ERROR";
                 return false;
@@ -302,6 +303,55 @@ qint64 KexiDBReportDataSource::recordCount() const
     return 1;
 }
 
+double KexiDBReportDataSource::runAggregateFunction(const QString &function, const \
QString &field, +                                                    const \
QMap<QString, QVariant> &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<QString, QVariant>::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
diff --git a/src/plugins/reports/KexiDBReportDataSource.h \
b/src/plugins/reports/KexiDBReportDataSource.h index 18342e25b..c8e8885dc 100644
--- a/src/plugins/reports/KexiDBReportDataSource.h
+++ b/src/plugins/reports/KexiDBReportDataSource.h
@@ -1,7 +1,7 @@
 /*
 * Kexi Report Plugin
 * Copyright (C) 2007-2017 by Adam Pigg <adam@piggz.co.uk>
-* Copyright (C) 2017 Jarosław Staniek <staniek@kde.org>
+* Copyright (C) 2017-2018 Jarosław Staniek <staniek@kde.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
@@ -66,6 +66,21 @@ public:
     virtual qint64 at() const;
     virtual qint64 recordCount() const;
 
+    /**
+     * Runs aggregate function @a function on the data source
+     *
+     * @param function name such as max, min, avg
+     * @param field name of field for which the aggregation should be executed
+     * @param conditions optional conditions that limit the record set
+     * @return value of the function, 0.0 on failure
+     *
+     * @warning SQL injection warning: validity of @a function name is not checked, \
this should not +     *          be part of a public API.
+     * @todo Move SQL aggregate functions to KDb. Current code depends on support \
for subqueries. +     */
+    double runAggregateFunction(const QString &function, const QString &field,
+                                const QMap<QString, QVariant> &conditions);
+
     //Utility Functions
     virtual QStringList dataSourceNames() const;
     virtual KReportDataSource* create(const QString& source) const \
                Q_REQUIRED_RESULT;
diff --git a/src/plugins/reports/kexireportview.cpp \
b/src/plugins/reports/kexireportview.cpp index 61dcaff1e..aaee5c4f6 100644
--- a/src/plugins/reports/kexireportview.cpp
+++ b/src/plugins/reports/kexireportview.cpp
@@ -1,7 +1,7 @@
 /*
  * Kexi Report Plugin
  * Copyright (C) 2007-2008 by Adam Pigg (adam@piggz.co.uk)
- * Copyright (C) 2014-2017 Jarosław Staniek <staniek@kde.org>
+ * Copyright (C) 2014-2018 Jarosław Staniek <staniek@kde.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -358,7 +358,7 @@ tristate KexiReportView::afterSwitchFrom(Kexi::ViewMode mode)
         //qDebug() << tempData()->reportDefinition.tagName();
         m_preRenderer = new KReportPreRenderer(tempData()->reportDefinition);
         if (m_preRenderer->isValid()) {
-            KReportDataSource *reportData = 0;
+            KexiDBReportDataSource *reportData = nullptr;
             if (!tempData()->connectionDefinition.isNull())  {
                 reportData = createDataSource(tempData()->connectionDefinition);
             }
@@ -373,8 +373,8 @@ tristate KexiReportView::afterSwitchFrom(Kexi::ViewMode mode)
 //            }
 //            m_preRenderer->registerScriptObject(m_kexi, "Kexi");
             //If using a kexidb source, add a functions scripting object
-            if (tempData()->connectionDefinition.attribute("type") == "internal") {
-                m_functions = new KRScriptFunctions(reportData, \
KexiMainWindowIface::global()->project()->dbConnection()); +            if \
(reportData && tempData()->connectionDefinition.attribute("type") == "internal") { +  \
m_functions = new KRScriptFunctions(reportData);  
                 m_preRenderer->registerScriptObject(m_functions, "field");
                 connect(m_preRenderer, SIGNAL(groupChanged(QMap<QString, \
QVariant>)), @@ -399,7 +399,7 @@ tristate \
KexiReportView::afterSwitchFrom(Kexi::ViewMode mode)  return true;
 }
 
-KReportDataSource* KexiReportView::createDataSource(const QDomElement &e)
+KexiDBReportDataSource* KexiReportView::createDataSource(const QDomElement &e)
 {
     if (e.attribute("type") == "internal" && !e.attribute("source").isEmpty()) {
         return new KexiDBReportDataSource(e.attribute("source"), \
                e.attribute("class"), tempData());
diff --git a/src/plugins/reports/kexireportview.h \
b/src/plugins/reports/kexireportview.h index bf213fa9f..ee4e1b168 100644
--- a/src/plugins/reports/kexireportview.h
+++ b/src/plugins/reports/kexireportview.h
@@ -1,7 +1,7 @@
 /*
  * Kexi Report Plugin
  * Copyright (C) 2007-2008 by Adam Pigg (adam@piggz.co.uk)
- * Copyright (C) 2014-2017 Jarosław Staniek <staniek@kde.org>
+ * Copyright (C) 2014-2018 Jarosław Staniek <staniek@kde.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -20,8 +20,6 @@
 #ifndef KEXIREPORTVIEW_H
 #define KEXIREPORTVIEW_H
 
-#include <QGraphicsView>
-
 #include <config-kreport.h>
 #include <KReportRendererBase>
 
@@ -29,6 +27,7 @@
 #include <core/KexiRecordNavigatorHandler.h>
 #include "kexireportpart.h"
 
+class KexiDBReportDataSource;
 class KReportPreRenderer;
 class ORODocument;
 class KReportView;
@@ -69,7 +68,7 @@ private:
 #endif
 
     KexiReportPartTempData* tempData() const;
-    KReportDataSource* createDataSource(const QDomElement &e);
+    KexiDBReportDataSource* createDataSource(const QDomElement &e);
     //! @todo KEXI3 KexiScriptAdaptor *m_kexi;
     KRScriptFunctions *m_functions;
     KReportRendererFactory m_factory;
diff --git a/src/plugins/reports/krscriptfunctions.cpp \
b/src/plugins/reports/krscriptfunctions.cpp index 43254ac30..b311b7652 100644
--- a/src/plugins/reports/krscriptfunctions.cpp
+++ b/src/plugins/reports/krscriptfunctions.cpp
@@ -1,7 +1,7 @@
 /*
  * Kexi Report Plugin
  * Copyright (C) 2007-2008 by Adam Pigg <adam@piggz.co.uk>
- * Copyright (C) 2012 Jarosław Staniek <staniek@kde.org>
+ * Copyright (C) 2012-2018 Jarosław Staniek <staniek@kde.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -18,6 +18,7 @@
  */
 
 #include "krscriptfunctions.h"
+#include "KexiDBReportDataSource.h"
 
 #include <KDbConnection>
 #include <KDbCursor>
@@ -25,22 +26,10 @@
 
 #include <QDebug>
 
-KRScriptFunctions::KRScriptFunctions(const KReportDataSource* datasource, \
KDbConnection* conn) +KRScriptFunctions::KRScriptFunctions(KexiDBReportDataSource \
*datasource) +    : m_dataSource(datasource)
 {
-    m_cursor = datasource;
-    m_connection = conn;
-
-    if (datasource) {
-        if (m_connection->containsTable(datasource->sourceName()) == true) {
-            m_source = datasource->sourceName();
-        } else if (m_connection->querySchema(datasource->sourceName())) {
-            KDbNativeStatementBuilder builder(conn, KDb::DriverEscaping);
-            KDbEscapedString source;
-            if (builder.generateSelectStatement(&source, \
                m_connection->querySchema(datasource->sourceName()))) {
-                m_source = source.toByteArray();
-            }
-        }
-    }
+    Q_ASSERT(m_dataSource);
 }
 
 KRScriptFunctions::~KRScriptFunctions()
@@ -54,23 +43,7 @@ void KRScriptFunctions::setGroupData(const QMap<QString, \
QVariant>& groupData)  
 qreal KRScriptFunctions::math(const QString &function, const QString &field)
 {
-    QString ret = QLatin1String("0.0");
-
-    if (!m_connection) {
-        return 0.0;
-    }
-
-    KDbEscapedString sql = KDbEscapedString("SELECT " + function + "(" + field + ") \
                FROM (" + m_source + ")");
-
-    if (!m_groupData.isEmpty()) {
-        sql += " WHERE(" + where() + ')';
-    }
-
-    qDebug() << sql;
-
-    m_connection->querySingleString(sql,&ret);
-
-    return ret.toDouble();
+    return m_dataSource->runAggregateFunction(function, field, m_groupData);
 }
 
 qreal KRScriptFunctions::sum(const QString &field)
@@ -100,32 +73,10 @@ qreal KRScriptFunctions::count(const QString &field)
 
 QVariant KRScriptFunctions::value(const QString &field)
 {
-    QVariant val;
-    if (!m_cursor) {
-        qDebug() << "No cursor to get value of field " << field;
-        return val;
-    }
-
-    QStringList fields = m_cursor->fieldNames();
-
-    val = m_cursor->value(fields.indexOf(field));
+    const QVariant val = m_dataSource->value(field);
     if (val.type() == QVariant::String) {
         // UTF-8 values are expected so convert this
         return val.toString().toUtf8();
     }
-
     return val;
 }
-
-KDbEscapedString KRScriptFunctions::where()
-{
-    QByteArray w;
-    QMap<QString, QVariant>::const_iterator i = m_groupData.constBegin();
-    while (i != m_groupData.constEnd()) {
-        w += '(' + i.key().toUtf8() + QByteArrayLiteral(" = '") + \
                i.value().toString().toUtf8() + QByteArrayLiteral("') AND ");
-        ++i;
-    }
-    w.chop(4);
-    //kreportDebug() << w;
-    return KDbEscapedString(w);
-}
diff --git a/src/plugins/reports/krscriptfunctions.h \
b/src/plugins/reports/krscriptfunctions.h index 89d44da46..f3af19b85 100644
--- a/src/plugins/reports/krscriptfunctions.h
+++ b/src/plugins/reports/krscriptfunctions.h
@@ -1,6 +1,7 @@
 /*
  * Kexi Report Plugin
  * Copyright (C) 2007-2008 by Adam Pigg (adam@piggz.co.uk)
+ * Copyright (C) 2012-2018 Jarosław Staniek <staniek@kde.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -19,11 +20,13 @@
 #ifndef KRSCRIPTFUNCTIONS_H
 #define KRSCRIPTFUNCTIONS_H
 
-#include <KReportDataSource>
 #include <KReportGroupTracker>
 
 #include <KDbEscapedString>
 
+#include <QMap>
+
+class KexiDBReportDataSource;
 class KDbConnection;
 class KDbCursor;
 
@@ -33,20 +36,19 @@ class KRScriptFunctions : public KReportGroupTracker
 {
     Q_OBJECT
 public:
-    KRScriptFunctions(const KReportDataSource *, KDbConnection*);
+    KRScriptFunctions(KexiDBReportDataSource *dataSource);
 
     ~KRScriptFunctions();
 
 private:
-    KDbConnection *m_connection;
-    const KReportDataSource *m_cursor;
+    KexiDBReportDataSource * const m_dataSource;
     QString m_source;
+
+    //! @todo Move SQL aggregate functions to KDb
     qreal math(const QString &, const QString &);
 
     QMap<QString, QVariant> m_groupData;
 
-    KDbEscapedString where();
-
 public Q_SLOTS:
     virtual void setGroupData(const QMap<QString, QVariant> &groupData);
 


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic