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

List:       kde-commits
Subject:    [kdb] /: Make <SELECT "a" + "b"> SQL queries work and fix <SELECT "a" || "b">
From:       Jaroslaw Staniek <staniek () kde ! org>
Date:       2016-02-29 23:21:25
Message-ID: E1aaX81-0005Dj-2C () scm ! kde ! org
[Download RAW message or body]

Git commit f19ae68c17822b33097119974b2ee01c0b93c534 by Jaroslaw Staniek.
Committed on 29/02/2016 at 23:16.
Pushed by staniek into branch 'master'.

Make <SELECT "a" + "b"> SQL queries work and fix <SELECT "a" || "b">

Summary:
- port from calligra.git
- <SELECT "a" + "b"> parses in Kexi>=2.9.7 but results in invalid (boolean) \
                type/value
- for convenience, if either argument is of text type, operator "+" is now \
                assumed to be identical to operator ||
- also fix SELECT "a" || "b" for MySQL (using CONCAT() is needed)
- add autotests and parser tests

https://phabricator.kde.org/T677
BUG:358636
FIXED-IN:2.9.11

Test Plan:
    Try test queries from the project attached at \
https://bugs.kde.org/show_bug.cgi?id=358636.  Try other queries that use \
text columns instead of string constants.

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

(from calligra.git)

M  +9    -2    autotests/ExpressionsTest.cpp
M  +11   -0    autotests/parser/data/statements.txt
M  +9    -1    src/KDbDriver.cpp
M  +12   -1    src/KDbDriver.h
M  +9    -1    src/drivers/mysql/MysqlDriver.cpp
M  +7    -1    src/drivers/mysql/MysqlDriver.h
M  +17   -2    src/expression/KDbBinaryExpression.cpp
M  +1    -0    src/expression/KDbExpression.h

http://commits.kde.org/kdb/f19ae68c17822b33097119974b2ee01c0b93c534

diff --git a/autotests/ExpressionsTest.cpp b/autotests/ExpressionsTest.cpp
index 145dab1..aa61e91 100644
--- a/autotests/ExpressionsTest.cpp
+++ b/autotests/ExpressionsTest.cpp
@@ -1,5 +1,5 @@
 /* This file is part of the KDE project
-   Copyright (C) 2011-2015 Jarosław Staniek <staniek@kde.org>
+   Copyright (C) 2011-2016 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 Library General Public
@@ -700,6 +700,7 @@ void \
                ExpressionsTest::testBinaryExpressionCloning_data()
     T(KDbToken::INTEGER_CONST, 3, KDbToken::AND, KDbToken::INTEGER_CONST, \
                4, "3 AND 4");
     T(KDbToken::INTEGER_CONST, 3, KDbToken::XOR, KDbToken::INTEGER_CONST, \
                4, "3 XOR 4");
     T(KDbToken::CHARACTER_STRING_LITERAL, "AB", KDbToken::CONCATENATION, \
KDbToken::CHARACTER_STRING_LITERAL, "CD", "'AB' || 'CD'"); +    \
T(KDbToken::CHARACTER_STRING_LITERAL, "AB", '+', \
KDbToken::CHARACTER_STRING_LITERAL, "CD", "'AB' + 'CD'");  #undef T
 }
 
@@ -1299,12 +1300,18 @@ void \
                ExpressionsTest::testBinaryExpressionValidate_data()
     T(KDbToken::REAL_CONST, 30.2, KDbToken::XOR, KDbToken::REAL_CONST, 20, \
KDbField::InvalidType);  // string
     T(KDbToken::CHARACTER_STRING_LITERAL, "ab", KDbToken::CONCATENATION, \
KDbToken::CHARACTER_STRING_LITERAL, "cd", KDbField::Text); +    \
T(KDbToken::CHARACTER_STRING_LITERAL, "ab", '+', \
KDbToken::CHARACTER_STRING_LITERAL, "cd", KDbField::Text); +
     T(KDbToken::SQL_NULL, QVariant(), KDbToken::CONCATENATION, \
KDbToken::CHARACTER_STRING_LITERAL, "cd", KDbField::Null); +    \
T(KDbToken::SQL_NULL, QVariant(), '+', KDbToken::CHARACTER_STRING_LITERAL, \
"cd", KDbField::Null); +
     T(KDbToken::INTEGER_CONST, 50, KDbToken::CONCATENATION, \
                KDbToken::INTEGER_CONST, 20, KDbField::InvalidType);
     T(KDbToken::CHARACTER_STRING_LITERAL, "ab", KDbToken::CONCATENATION, \
KDbToken::INTEGER_CONST, 20, KDbField::InvalidType); +    \
T(KDbToken::CHARACTER_STRING_LITERAL, "ab", '+', KDbToken::INTEGER_CONST, \
20, KDbField::InvalidType); +
     T(KDbToken::CHARACTER_STRING_LITERAL, "ab", \
KDbToken::GREATER_OR_EQUAL, KDbToken::CHARACTER_STRING_LITERAL, "cd", \
                KDbField::Boolean);
     T(KDbToken::CHARACTER_STRING_LITERAL, "ab", '<', \
                KDbToken::INTEGER_CONST, 3, KDbField::InvalidType);
-    T(KDbToken::CHARACTER_STRING_LITERAL, "ab", '+', \
KDbToken::CHARACTER_STRING_LITERAL, "cd", KDbField::InvalidType); +    \
T(KDbToken::CHARACTER_STRING_LITERAL, "ab", '+', \
                KDbToken::CHARACTER_STRING_LITERAL, "cd", KDbField::Text);
     T(KDbToken::CHARACTER_STRING_LITERAL, "A", KDbToken::OR, \
                KDbToken::REAL_CONST, 20, KDbField::InvalidType);
     T(KDbToken::CHARACTER_STRING_LITERAL, "A", KDbToken::AND, \
                KDbToken::REAL_CONST, 20, KDbField::InvalidType);
     T(KDbToken::CHARACTER_STRING_LITERAL, "A", KDbToken::XOR, \
                KDbToken::REAL_CONST, 20, KDbField::InvalidType);
diff --git a/autotests/parser/data/statements.txt \
b/autotests/parser/data/statements.txt index fc0c3e1..95648c2 100644
--- a/autotests/parser/data/statements.txt
+++ b/autotests/parser/data/statements.txt
@@ -92,6 +92,17 @@ select 'ABC' LIKE 'A%';
 select 3 SIMILAR TO 4;
 select 3 NOT SIMILAR TO 4;
 select 'AB' || 'CD';
+select 'AB' || 'CD' || 'EF';
+select 'AB' + 'CD' || 'EF' + 'GH' || '';
+select NULL || NULL;
+select NULL || 'AB';
+select 'AB' + NULL;
+-- ERROR: ||
+select 'AB' || 1;
+select 'AB' + 1;
+select 7 || 'AB';
+select 7 + 'AB';
+select 7 || 1;
 -- ERROR: **
 select 1**2;
 -- ERROR: * or / are not unary operators
diff --git a/src/KDbDriver.cpp b/src/KDbDriver.cpp
index 8752585..efd460d 100644
--- a/src/KDbDriver.cpp
+++ b/src/KDbDriver.cpp
@@ -1,5 +1,5 @@
 /* This file is part of the KDE project
-   Copyright (C) 2003-2015 Jarosław Staniek <staniek@kde.org>
+   Copyright (C) 2003-2016 Jarosław Staniek <staniek@kde.org>
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
@@ -356,6 +356,14 @@ KDbEscapedString KDbDriver::unicodeFunctionToString(
     return KDbFunctionExpression::toString(QLatin1String("UNICODE"), this, \
args, params, callStack);  }
 
+KDbEscapedString KDbDriver::concatenateFunctionToString(const \
KDbBinaryExpression &args, +                                               \
KDbQuerySchemaParameterValueListIterator* params, +                         \
KDb::ExpressionCallStack* callStack) const +{
+    return args.left().toString(this, params, callStack) + \
KDbEscapedString("||") +            + args.right().toString(this, params, \
callStack); +}
+
 //---------------
 
 Q_GLOBAL_STATIC_WITH_ARGS(
diff --git a/src/KDbDriver.h b/src/KDbDriver.h
index 540e9cc..5b0b286 100644
--- a/src/KDbDriver.h
+++ b/src/KDbDriver.h
@@ -1,5 +1,5 @@
 /* This file is part of the KDE project
-   Copyright (C) 2003-2015 Jarosław Staniek <staniek@kde.org>
+   Copyright (C) 2003-2016 Jarosław Staniek <staniek@kde.org>
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
@@ -40,6 +40,7 @@ class KDbConnectionOptions;
 class KDbDriverManager;
 class KDbDriverBehaviour;
 class KDbDriverMetaData;
+class KDbBinaryExpression;
 class KDbNArgExpression;
 class KDbQuerySchemaParameterValueListIterator;
 class DriverPrivate;
@@ -280,6 +281,16 @@ public:
                                             \
                KDbQuerySchemaParameterValueListIterator* params,
                                             KDb::ExpressionCallStack* \
callStack) const;  
+    //! Generates native (driver-specific) function call for concatenation \
of two strings. +    //! Default implementation USES infix "||" operator.
+    //! Special case is for MYSQL (CONCAT()).
+    //! @todo API supporting KDbNArgExpression would be useful so instead \
of a||b||c can be +    //!       expressed as CONCAT(a,b,c) instead of \
CONCAT(CONCAT(a,b),c). +    //!       This requires changes to the KDbSQL \
parser. +    KDbEscapedString concatenateFunctionToString(const \
KDbBinaryExpression &args, +                                                \
KDbQuerySchemaParameterValueListIterator* params, +                         \
KDb::ExpressionCallStack* callStack) const; +
 protected:
     /*! Used by KDbDriverManager.
      Note for driver developers: Reimplement this.
diff --git a/src/drivers/mysql/MysqlDriver.cpp \
b/src/drivers/mysql/MysqlDriver.cpp index 70403a5..c6344ef 100644
--- a/src/drivers/mysql/MysqlDriver.cpp
+++ b/src/drivers/mysql/MysqlDriver.cpp
@@ -2,7 +2,7 @@
    Copyright (C) 2002 Lucijan Busch <lucijan@gmx.at>
    Copyright (C) 2003 Daniel Molkentin <molkentin@kde.org>
    Copyright (C) 2003 Joseph Wenninger<jowenn@kde.org>
-   Copyright (C) 2003-2015 Jarosław Staniek <staniek@kde.org>
+   Copyright (C) 2003-2016 Jarosław Staniek <staniek@kde.org>
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
@@ -221,4 +221,12 @@ KDbEscapedString MysqlDriver::unicodeFunctionToString(
                             .arg(args.arg(0).toString(this, params, \
callStack));  }
 
+KDbEscapedString MysqlDriver::concatenateFunctionToString(const \
KDbBinaryExpression &args, +                                                \
KDbQuerySchemaParameterValueListIterator* params, +                         \
KDb::ExpressionCallStack* callStack) const +{
+    return KDbEscapedString("CONCAT(%1, \
%2)").arg(args.left().toString(this, params, callStack)) +                  \
.arg(args.right().toString(this, params, callStack)); +}
+
 #include "MysqlDriver.moc"
diff --git a/src/drivers/mysql/MysqlDriver.h \
b/src/drivers/mysql/MysqlDriver.h index bf86c72..a5d1530 100644
--- a/src/drivers/mysql/MysqlDriver.h
+++ b/src/drivers/mysql/MysqlDriver.h
@@ -2,7 +2,7 @@
    Copyright (C) 2002 Lucijan Busch <lucijan@gmx.at>
    Copyright (C) 2003 Daniel Molkentin <molkentin@kde.org>
    Copyright (C) 2003 Joseph Wenninger<jowenn@kde.org>
-   Copyright (C) 2003-2015 Jarosław Staniek <staniek@kde.org>
+   Copyright (C) 2003-2016 Jarosław Staniek <staniek@kde.org>
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
@@ -77,6 +77,12 @@ public:
                                             \
                KDbQuerySchemaParameterValueListIterator* params,
                                             KDb::ExpressionCallStack* \
callStack) const;  
+    //! Generates native (driver-specific) function call for concatenation \
of two strings. +    //! Uses CONCAT().
+    KDbEscapedString concatenateFunctionToString(const KDbBinaryExpression \
&args, +                                                 \
KDbQuerySchemaParameterValueListIterator* params, +                         \
KDb::ExpressionCallStack* callStack) const; +
 protected:
     virtual QString drv_escapeIdentifier(const QString& str) const;
     virtual QByteArray drv_escapeIdentifier(const QByteArray& str) const;
diff --git a/src/expression/KDbBinaryExpression.cpp \
b/src/expression/KDbBinaryExpression.cpp index 18ff7e0..5515861 100644
--- a/src/expression/KDbBinaryExpression.cpp
+++ b/src/expression/KDbBinaryExpression.cpp
@@ -1,5 +1,5 @@
 /* This file is part of the KDE project
-   Copyright (C) 2003-2015 Jarosław Staniek <staniek@kde.org>
+   Copyright (C) 2003-2016 Jarosław Staniek <staniek@kde.org>
 
    Based on nexp.cpp : Parser module of Python-like language
    (C) 2001 Jarosław Staniek, MIMUW (www.mimuw.edu.pl)
@@ -139,6 +139,7 @@ KDbField::Type \
KDbBinaryExpressionData::typeInternal(KDb::ExpressionCallStack* c  return \
KDbField::Null;  }
         return (ltBool && rtBool) ? KDbField::Boolean : \
KDbField::InvalidType; +    case '+':
     case CONCATENATION:
         if (lt == KDbField::Text && rt == KDbField::Text) {
             return KDbField::Text;
@@ -150,8 +151,10 @@ KDbField::Type \
KDbBinaryExpressionData::typeInternal(KDb::ExpressionCallStack* c  || \
(rtText && ltNull))  {
             return KDbField::Null;
+        } else if (token.value() == CONCATENATION) {
+            return KDbField::InvalidType;
         }
-        return KDbField::InvalidType;
+        break; // '+' can still be handled below for non-text types
     default:;
     }
 
@@ -244,6 +247,18 @@ KDbEscapedString \
                KDbBinaryExpressionData::toStringInternal(
                                         \
                KDbQuerySchemaParameterValueListIterator* params,
                                         KDb::ExpressionCallStack* \
callStack) const  {
+    switch (token.value()) {
+    case '+':
+    case CONCATENATION: {
+        if (driver && KDbField::isTextType(type())) {
+            const KDbBinaryExpression \
binaryExpr(const_cast<KDbBinaryExpressionData*>(this)); +            return \
driver->concatenateFunctionToString(binaryExpr, params, callStack); +       \
} +        break;
+    }
+    default:;
+    }
+
 #define INFIX(a) \
     (left().constData() ? left()->toString(driver, params, callStack) : \
                KDbEscapedString("<NULL>")) \
     + " " + a + " " + (right().constData() ? right()->toString(driver, \
                params, callStack) : KDbEscapedString("<NULL>"))
diff --git a/src/expression/KDbExpression.h \
b/src/expression/KDbExpression.h index 11a5e88..9621123 100644
--- a/src/expression/KDbExpression.h
+++ b/src/expression/KDbExpression.h
@@ -369,6 +369,7 @@ protected:
     explicit KDbBinaryExpression(const \
ExplicitlySharedExpressionDataPointer &ptr);  
     friend class KDbExpression;
+    friend class KDbBinaryExpressionData;
 };
 
 


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

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