File indexing completed on 2024-04-14 14:53:02

0001 /* This file is part of the KDE project
0002    Copyright (C) 2011-2016 Jarosław Staniek <staniek@kde.org>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LIB.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "ExpressionsTest.h"
0021 
0022 #include <QtTest>
0023 
0024 #include <KDbDateTime>
0025 #include <KDbExpression>
0026 #include "parser/generated/sqlparser.h"
0027 #include "parser/KDbParser_p.h"
0028 
0029 Q_DECLARE_METATYPE(KDb::ExpressionClass)
0030 Q_DECLARE_METATYPE(KDbEscapedString)
0031 Q_DECLARE_METATYPE(KDbField::Type)
0032 Q_DECLARE_METATYPE(KDbToken)
0033 
0034 namespace QTest {
0035     template<>
0036     char *toString(const KDbEscapedString &string)
0037     {
0038         return qstrdup(qPrintable(string.toString()));
0039     }
0040 
0041     template<>
0042     char *toString(const KDbField::Type &type)
0043     {
0044         return qstrdup(qPrintable(KDbField::typeString(type)));
0045     }
0046 
0047     //! Adds a quote if this is the single-character token to match the format of Bison
0048     template<>
0049     char* toString(const KDbToken &token)
0050     {
0051         return qstrdup(qPrintable(
0052                            token.toChar() ? QString::fromLatin1("'%1'").arg(token.toString())
0053                                           : token.toString()
0054                       ));
0055     }
0056 }
0057 
0058 QTEST_GUILESS_MAIN(ExpressionsTest)
0059 
0060 //! Used in macros so characters and KDbTokens can be used interchangeably
0061 static inline KDbToken TO_TOKEN(char charValue)
0062 {
0063     return KDbToken(charValue);
0064 }
0065 
0066 //! Used in macros so characters and KDbTokens can be used interchangeably
0067 static inline KDbToken TO_TOKEN(KDbToken token)
0068 {
0069     return token;
0070 }
0071 
0072 void ExpressionsTest::initTestCase()
0073 {
0074 }
0075 
0076 //! compares two expression @a e1 and @a e2 based on strings/debug strings
0077 //! and token strings
0078 template <typename T1, typename T2>
0079 static void compareStrings(const T1 &e1, const T2 &e2)
0080 {
0081     //qDebug() << "compareStrings():"
0082     //         << "\ne1:" << e1.toString() << e1.token() << e1.token().ToString()
0083     //         << "\ne2:" << e2.toString() << e2.token() << e2.token().toString();
0084     QCOMPARE(e1.toString(nullptr), e2.toString(nullptr));
0085     QCOMPARE(e1.token(), e2.token());
0086     QCOMPARE(e1.token().value(), e2.token().value());
0087     QCOMPARE(e1.token().toString(), e2.token().toString());
0088 }
0089 
0090 //! tests clone and copy ctor for @a e1
0091 template <typename T>
0092 static void testCloneExpression(const T &e1)
0093 {
0094     KDbExpression e1clone = e1.clone();
0095     //qDebug() << e1;
0096     //qDebug() << e1clone;
0097     QVERIFY(e1 != e1.clone());
0098     QVERIFY(e1 != e1clone);
0099     QVERIFY(e1.clone() != e1clone);
0100     QVERIFY(e1.expressionClass() == e1clone.expressionClass());
0101     QVERIFY(e1.token() == e1clone.token());
0102     compareStrings(e1, e1clone);
0103 
0104     const T copied(e1);
0105     QVERIFY(e1 == copied);
0106     QVERIFY(e1.clone() != copied);
0107     QVERIFY(e1.expressionClass() == copied.expressionClass());
0108     QVERIFY(e1.token() == copied.token());
0109     compareStrings(e1, copied);
0110 }
0111 
0112 //! Validates expression @a expr and shows error message on failure
0113 static bool validate(KDbExpression *expr)
0114 {
0115     KDbParseInfoInternal parseInfo(nullptr);
0116     bool ok = expr->validate(&parseInfo);
0117     if (!ok) {
0118         qInfo() << "Validation of" << *expr << "FAILED.";
0119         if (!parseInfo.errorMessage().isEmpty()) {
0120             qInfo() << "Error message:" << parseInfo.errorMessage();
0121         }
0122         if (!parseInfo.errorDescription().isEmpty()) {
0123             qInfo() << "Error description:" << parseInfo.errorDescription();
0124         }
0125     }
0126     return ok;
0127 }
0128 
0129 void ExpressionsTest::testNullExpression()
0130 {
0131     QVERIFY(KDbExpression() != KDbExpression());
0132 
0133     KDbExpression e1;
0134     KDbExpression e2;
0135     QVERIFY(e1.isNull());
0136     QVERIFY(!e1.isValid());
0137     QVERIFY(!e1.isBinary());
0138     QVERIFY(e1.toBinary().isNull());
0139     QVERIFY(!e1.isConst());
0140     QVERIFY(e1.toConst().isNull());
0141     QVERIFY(!e1.isFunction());
0142     QVERIFY(e1.toFunction().isNull());
0143     QVERIFY(!e1.isNArg());
0144     QVERIFY(e1.toNArg().isNull());
0145     QVERIFY(!e1.isQueryParameter());
0146     QVERIFY(e1.toQueryParameter().isNull());
0147     QVERIFY(!e1.isUnary());
0148     QVERIFY(e1.toUnary().isNull());
0149     QVERIFY(!e1.isVariable());
0150     QVERIFY(e1.toVariable().isNull());
0151     QCOMPARE(e1.expressionClass(), KDb::UnknownExpression);
0152     QCOMPARE(e1.token(), KDbToken());
0153     QVERIFY(e1 != KDbExpression());
0154     QVERIFY(e1 == e1);
0155     QVERIFY(e1 != e2);
0156 
0157     e1 = e2;
0158     QVERIFY(e1.isNull());
0159     QCOMPARE(e1, e2);
0160     QCOMPARE(e1.toString(nullptr), KDbEscapedString("<UNKNOWN!>"));
0161     QCOMPARE(e1.token().name(), QLatin1String("<INVALID_TOKEN>"));
0162     QCOMPARE(e1.token().toString(nullptr), QString("<INVALID_TOKEN>"));
0163     compareStrings(e1, e2);
0164 
0165     KDbExpression e3(e2);
0166     QVERIFY(e3.isNull());
0167     QCOMPARE(e2, e3);
0168     compareStrings(e2, e3);
0169     //ExpressionDebug << "$$$" << e1.toString() << e1.token() << e1.token().toString();
0170 
0171     e1 = KDbExpression();
0172     testCloneExpression(e1);
0173 }
0174 
0175 void ExpressionsTest::testExpressionClassName_data()
0176 {
0177     QTest::addColumn<KDb::ExpressionClass>("expClass");
0178     QTest::addColumn<QString>("name");
0179 
0180     int c = 0;
0181 #define T(n, t) ++c; QTest::newRow(n) << t << n
0182     T("Unknown", KDb::UnknownExpression);
0183     T("Unary", KDb::UnaryExpression);
0184     T("Arithm", KDb::ArithmeticExpression);
0185     T("Logical", KDb::LogicalExpression);
0186     T("Relational", KDb::RelationalExpression);
0187     T("SpecialBinary", KDb::SpecialBinaryExpression);
0188     T("Const", KDb::ConstExpression);
0189     T("Variable", KDb::VariableExpression);
0190     T("Function", KDb::FunctionExpression);
0191     T("Aggregation", KDb::AggregationExpression);
0192     T("FieldList", KDb::FieldListExpression);
0193     T("TableList", KDb::TableListExpression);
0194     T("ArgumentList", KDb::ArgumentListExpression);
0195     T("QueryParameter", KDb::QueryParameterExpression);
0196 #undef T
0197     QCOMPARE(c, int(KDb::LastExpressionClass) + 1);
0198 }
0199 
0200 void ExpressionsTest::testExpressionClassName()
0201 {
0202     QFETCH(KDb::ExpressionClass, expClass);
0203     QTEST(expressionClassName(expClass), "name");
0204 }
0205 
0206 #include "KDbUtils_p.h"
0207 
0208 void ExpressionsTest::testExpressionToken()
0209 {
0210     KDbExpression e1;
0211     QVERIFY(!e1.isValid());
0212     QVERIFY(!KDbToken().isValid());
0213     QCOMPARE(e1.token(), KDbToken());
0214     QVERIFY(KDbToken('+').toChar() > 0);
0215     QCOMPARE(KDbToken('*'), KDbToken('*'));
0216     QCOMPARE(KDbToken('*').toChar(), '*');
0217     QCOMPARE(KDbToken('*').value(), int('*'));
0218     QCOMPARE(KDbToken('*').name(), QString::fromLatin1("*"));
0219     QCOMPARE(KDbToken('*').toString(), QString::fromLatin1("*"));
0220     QCOMPARE(KDbToken::LEFT.toChar(), char(0));
0221     QCOMPARE(KDbToken().toChar(), char(0));
0222     QVERIFY(KDbToken::LEFT.isValid());
0223     QVERIFY(KDbToken::maxCharTokenValue > 0);
0224     QVERIFY(KDbToken::LEFT.value() > KDbToken::maxCharTokenValue);
0225     const QList<KDbToken> allTokens(KDbToken::allTokens());
0226     QVERIFY(!allTokens.isEmpty());
0227 
0228     for(const KDbToken &t : allTokens) {
0229         //qDebug() << t << t.value();
0230         if (t.toChar() > 0) {
0231             QVERIFY(t.value() <= KDbToken::maxCharTokenValue);
0232             QCOMPARE(t, KDbToken(char(t.value())));
0233             QCOMPARE(t.name(), isprint(t.value()) ? QString(QLatin1Char(uchar(t.value())))
0234                                                   : QString::number(t.value()));
0235             QCOMPARE(QTest::toString(t), QString::fromLatin1(g_tokenName(t.value())).toLatin1().data());
0236         }
0237         else {
0238             QCOMPARE(t.name(), QString::fromLatin1(g_tokenName(t.value())));
0239         }
0240     }
0241 }
0242 
0243 void ExpressionsTest::testNArgExpression()
0244 {
0245     KDbNArgExpression n;
0246     KDbNArgExpression n2;
0247     KDbConstExpression c;
0248     KDbConstExpression c1;
0249     KDbConstExpression c2;
0250     KDbConstExpression c3;
0251 
0252     // -- empty
0253     KDbNArgExpression emptyNarg;
0254     QVERIFY(emptyNarg.isNArg());
0255     QVERIFY(emptyNarg.clone().isNArg());
0256     QVERIFY(emptyNarg.isEmpty());
0257     QCOMPARE(emptyNarg.argCount(), 0);
0258     QVERIFY(emptyNarg.arg(-1).isNull());
0259     QVERIFY(emptyNarg.arg(0).isNull());
0260     QVERIFY(!emptyNarg.containsInvalidArgument());
0261     QVERIFY(!emptyNarg.containsNullArgument());
0262 
0263     // -- copy ctor & cloning
0264     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
0265     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 7);
0266     c2 = KDbConstExpression(KDbToken::INTEGER_CONST, 8);
0267     n.append(c1);
0268     n.append(c2);
0269     testCloneExpression(n);
0270 
0271     // copy on stack
0272     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
0273     {
0274         KDbConstExpression s1(KDbToken::INTEGER_CONST, 7);
0275         KDbConstExpression s2(KDbToken::INTEGER_CONST, 8);
0276         n.append(s1);
0277         n.append(s2);
0278         c1 = s1;
0279         c2 = s2;
0280     }
0281     QCOMPARE(n.argCount(), 2);
0282     QCOMPARE(n.arg(0).toConst(), c1);
0283     QCOMPARE(n.arg(1).toConst(), c2);
0284 
0285     QCOMPARE(n.token().name(), QString("+"));
0286     QCOMPARE(n.toString(nullptr), KDbEscapedString("7, 8"));
0287     n.setToken('*');
0288     QCOMPARE(n.token().name(), QString("*"));
0289 
0290     // -- append(KDbExpression), prepend(KDbExpression)
0291     KDbExpression e;
0292     KDbNArgExpression nNull;
0293     QCOMPARE(nNull.argCount(), 0); // empty
0294     nNull.append(e);
0295     QCOMPARE(nNull.argCount(), 1); // n-arg expression can have null elements
0296     nNull = KDbNArgExpression();
0297     QCOMPARE(nNull.argCount(), 0); // cleared
0298 
0299     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
0300     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 1);
0301     n.append(c1);
0302     QVERIFY(!n.isEmpty());
0303     QCOMPARE(n.argCount(), 1);
0304     QCOMPARE(n.arg(0).toConst(), c1);
0305     QCOMPARE(c1.parent().toNArg(), n);
0306 
0307     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
0308     n.append(n);
0309     QCOMPARE(n.argCount(), 0); // append should fail since appending expression
0310                                // to itself is not allowed
0311     n.prepend(n);
0312     QCOMPARE(n.argCount(), 0); // append should fail since prepending expression
0313                                // to itself is not allowed
0314 
0315     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
0316     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 2);
0317     n.append(c1);
0318     n.append(c1); // cannot append the same expression twice
0319     QCOMPARE(n.argCount(), 1);
0320     QCOMPARE(n.arg(0).toConst(), c1);
0321 
0322     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
0323     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 3);
0324     n.prepend(c1);
0325     n.prepend(c1); // cannot prepend the same expression twice
0326     QCOMPARE(n.argCount(), 1);
0327     QCOMPARE(n.arg(0).toConst(), c1);
0328     n.append(c1); // cannot append/prepend the same expression twice
0329     QCOMPARE(n.argCount(), 1);
0330     QCOMPARE(n.arg(0).toConst(), c1);
0331 
0332     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
0333     n2 = KDbNArgExpression(KDb::ArithmeticExpression, '+');
0334     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 4);
0335     n.append(c1);
0336     n2.append(c1); // c moves from n to n2
0337     QVERIFY(n.isEmpty());
0338     QCOMPARE(n2.argCount(), 1);
0339     QCOMPARE(c1.parent().toNArg(), n2);
0340     n.prepend(c1); // c moves from n2 to n
0341     QCOMPARE(n.argCount(), 1);
0342     QVERIFY(n2.isEmpty());
0343     QCOMPARE(c1.parent().toNArg(), n);
0344 
0345     // -- insert(int, KDbExpression)
0346     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
0347     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 3);
0348     c2 = KDbConstExpression(KDbToken::INTEGER_CONST, 4);
0349     // it must be a valid index position in the list (i.e., 0 <= i < argCount()).
0350     n.insert(-10, c1);
0351     QVERIFY(n.isEmpty());
0352     n.insert(1, c1);
0353     QVERIFY(n.isEmpty());
0354     // if i is 0, the expression is prepended to the list of arguments
0355     n.insert(0, c1);
0356     QCOMPARE(n.arg(0).toConst(), c1);
0357     QCOMPARE(n.argCount(), 1);
0358     QCOMPARE(c1.parent().toNArg(), n);
0359     n.insert(0, c2);
0360     QCOMPARE(n.argCount(), 2);
0361     QCOMPARE(n.arg(0).toConst(), c2);
0362     QCOMPARE(n.arg(1).toConst(), c1);
0363 
0364     // if i is argCount(), the value is appended to the list of arguments
0365     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
0366     n.insert(0, c1);
0367     n.insert(1, c2);
0368     QCOMPARE(n.argCount(), 2);
0369     QCOMPARE(n.arg(0).toConst(), c1);
0370     QCOMPARE(n.arg(1).toConst(), c2);
0371 
0372     // expression cannot be own child
0373     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
0374     n.insert(0, n);
0375     QVERIFY(n.isEmpty());
0376 
0377     // cannot insert child twice
0378     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
0379     n.insert(0, c1);
0380     n.insert(1, c1);
0381     QCOMPARE(n.argCount(), 1);
0382 
0383     // -- remove(KDbExpression)
0384     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
0385     n.append(c1);
0386     n.append(c2);
0387     n.remove(c1); // remove first
0388     QCOMPARE(n.argCount(), 1);
0389     QCOMPARE(n.arg(0).toConst(), c2);
0390 
0391     // -- remove(KDbExpression)
0392     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
0393     n.prepend(c1);
0394     n.append(c2);
0395     c3 = KDbConstExpression(KDbToken::INTEGER_CONST, 5);
0396     QVERIFY(!n.remove(c3)); // not found
0397     QCOMPARE(n.argCount(), 2);
0398     n.append(c3);
0399     QCOMPARE(n.argCount(), 3);
0400     QVERIFY(n.remove(c2)); // remove 2nd of 3, leaves c1 and c3
0401     QCOMPARE(n.argCount(), 2);
0402     QCOMPARE(n.arg(0).toConst(), c1);
0403     QCOMPARE(n.arg(1).toConst(), c3);
0404 
0405     // -- removeAt(int)
0406     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
0407     n.prepend(c1);
0408     n.append(c2);
0409     n.removeAt(-1); // not found
0410     QCOMPARE(n.argCount(), 2);
0411     n.removeAt(3); // not found
0412     QCOMPARE(n.argCount(), 2);
0413     n.append(c3);
0414     n.removeAt(1); // remove 2nd of 3, leaves c1 and c3
0415     QCOMPARE(n.argCount(), 2);
0416     QCOMPARE(n.arg(0).toConst(), c1);
0417     QCOMPARE(n.arg(1).toConst(), c3);
0418     n.removeAt(0);
0419     QCOMPARE(n.argCount(), 1);
0420     n.removeAt(0);
0421     QCOMPARE(n.argCount(), 0);
0422 
0423     // -- takeAt(int)
0424     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
0425     n2 = n;
0426     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 1);
0427     c2 = KDbConstExpression(KDbToken::INTEGER_CONST, 2);
0428     c3 = KDbConstExpression(KDbToken::INTEGER_CONST, 3);
0429     n.append(c1);
0430     n.append(c2);
0431     n.append(c3);
0432     n.takeAt(-1); // not found
0433     QCOMPARE(n.argCount(), 3);
0434     n.takeAt(3); // not found
0435     QCOMPARE(n.argCount(), 3);
0436     e = n.takeAt(1);
0437     QCOMPARE(e.toConst(), c2); // e is 2nd
0438     QCOMPARE(n.argCount(), 2); // 1 arg taken
0439     QCOMPARE(n, n2);
0440 
0441     // -- indexOf(KDbExpression, int)
0442     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
0443     c = KDbConstExpression(KDbToken::INTEGER_CONST, 0);
0444     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 1);
0445     c2 = KDbConstExpression(KDbToken::INTEGER_CONST, 2);
0446     c3 = KDbConstExpression(KDbToken::INTEGER_CONST, 3);
0447     n.append(c1);
0448     n.append(c2);
0449     n.append(c3);
0450 
0451     QCOMPARE(n.indexOf(c), -1);
0452     QCOMPARE(n.indexOf(c1), 0);
0453     QCOMPARE(n.indexOf(c2), 1);
0454     QCOMPARE(n.indexOf(c3), 2);
0455     QCOMPARE(n.indexOf(c1, 1), -1);
0456     QCOMPARE(n.indexOf(c2, 1), 1);
0457 
0458     // -- lastIndexOf(KDbExpression, int)
0459     QCOMPARE(n.lastIndexOf(c), -1);
0460     QCOMPARE(n.lastIndexOf(c1), 0);
0461     QCOMPARE(n.lastIndexOf(c2), 1);
0462     QCOMPARE(n.lastIndexOf(c3), 2);
0463     QCOMPARE(n.lastIndexOf(c1, 1), 0);
0464     QCOMPARE(n.lastIndexOf(c2, 0), -1);
0465 
0466     // -- a list of arguments
0467     n = KDbNArgExpression(KDb::ArgumentListExpression, ',');
0468     n.append(KDbConstExpression(KDbToken::INTEGER_CONST, 1));
0469     n.append(KDbConstExpression(KDbToken::INTEGER_CONST, 2));
0470     n.append(KDbConstExpression(KDbToken::INTEGER_CONST, 3));
0471     QCOMPARE(n.toString(nullptr), KDbEscapedString("1, 2, 3"));
0472     QCOMPARE(n.argCount(), 3);
0473     QVERIFY(!n.containsInvalidArgument());
0474     QVERIFY(!n.containsNullArgument());
0475 
0476     // -- a list of arguments contains invalid argument
0477     n = KDbNArgExpression(KDb::ArgumentListExpression, ',');
0478     n.append(KDbConstExpression(KDbToken::INTEGER_CONST, 1));
0479     n.append(KDbExpression());
0480     n.append(KDbConstExpression(KDbToken::INTEGER_CONST, 3));
0481     QVERIFY(n.containsInvalidArgument());
0482     QVERIFY(!n.containsNullArgument());
0483     QVERIFY(!n.isNull());
0484     QCOMPARE(n.toString(nullptr), KDbEscapedString("1, <UNKNOWN!>, 3"));
0485 
0486     // -- a list of arguments contains null argument
0487     n = KDbNArgExpression(KDb::ArgumentListExpression, ',');
0488     n.append(KDbConstExpression(KDbToken::INTEGER_CONST, 1));
0489     n.append(KDbConstExpression(KDbToken::SQL_NULL, QVariant()));
0490     n.prepend(KDbConstExpression(KDbToken::INTEGER_CONST, 0));
0491     QVERIFY(!n.containsInvalidArgument());
0492     QVERIFY(n.containsNullArgument());
0493     QCOMPARE(n.toString(nullptr), KDbEscapedString("0, 1, NULL"));
0494 }
0495 
0496 void ExpressionsTest::testUnaryExpression()
0497 {
0498     KDbUnaryExpression u;
0499     KDbUnaryExpression u2;
0500     KDbConstExpression c;
0501     KDbConstExpression c1;
0502 
0503     // -- empty
0504     KDbUnaryExpression emptyUnary;
0505     QVERIFY(emptyUnary.isUnary());
0506     QVERIFY(emptyUnary.clone().isUnary());
0507     QVERIFY(emptyUnary.arg().isNull());
0508 
0509     u = KDbUnaryExpression('-', KDbExpression());
0510     QVERIFY(u.arg().isNull());
0511 
0512     // -- copy ctor & cloning
0513     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 7);
0514     u = KDbUnaryExpression('-', c1);
0515     testCloneExpression(u);
0516     QCOMPARE(u.token().name(), QString("-"));
0517     QCOMPARE(u.toString(nullptr), KDbEscapedString("-7"));
0518     QCOMPARE(c1, u.arg().toConst());
0519 
0520     u2 = KDbUnaryExpression('-', u);
0521     testCloneExpression(u);
0522     QCOMPARE(u2.token().name(), QString("-"));
0523     QCOMPARE(u2.toString(nullptr), KDbEscapedString("--7"));
0524     QCOMPARE(u, u2.arg().toUnary());
0525 
0526     u = KDbUnaryExpression('(', c1);
0527     testCloneExpression(u);
0528     QCOMPARE(u.toString(nullptr), KDbEscapedString("(7)"));
0529     QCOMPARE(c1, u.arg().toConst());
0530 
0531     c1 = KDbConstExpression(KDbToken::SQL_TRUE, true);
0532     u = KDbUnaryExpression(KDbToken::NOT, c1);
0533     testCloneExpression(u);
0534     QCOMPARE(u.toString(nullptr), KDbEscapedString("NOT TRUE"));
0535     QCOMPARE(c1, u.arg().toConst());
0536 
0537     c1 = KDbConstExpression(KDbToken::SQL_NULL, QVariant());
0538     u = KDbUnaryExpression(KDbToken::NOT, c1);
0539     testCloneExpression(u);
0540     QCOMPARE(u.toString(nullptr), KDbEscapedString("NOT NULL"));
0541     QCOMPARE(c1, u.arg().toConst());
0542 
0543     c1 = KDbConstExpression(KDbToken::SQL_NULL, QVariant());
0544     u = KDbUnaryExpression(KDbToken::SQL_IS_NULL, c1);
0545     testCloneExpression(u);
0546     QCOMPARE(u.toString(nullptr), KDbEscapedString("NULL IS NULL"));
0547     QCOMPARE(c1, u.arg().toConst());
0548 
0549     c1 = KDbConstExpression(KDbToken::SQL_NULL, QVariant());
0550     u = KDbUnaryExpression(KDbToken::SQL_IS_NOT_NULL, c1);
0551     testCloneExpression(u);
0552     QCOMPARE(u.toString(nullptr), KDbEscapedString("NULL IS NOT NULL"));
0553     QCOMPARE(c1, u.arg().toConst());
0554 
0555     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 17);
0556     u = KDbUnaryExpression(KDbToken::SQL, c1);
0557     testCloneExpression(u);
0558     QCOMPARE(u.toString(nullptr), KDbEscapedString("SQL 17"));
0559     QCOMPARE(c1, u.arg().toConst());
0560 
0561     // -- exchanging arg between two unary expressions
0562     c = KDbConstExpression(KDbToken::INTEGER_CONST, 17);
0563     u = KDbUnaryExpression('-', c);
0564     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 3);
0565     u2 = KDbUnaryExpression('+', c1);
0566     u2.setArg(c); // this should take c arg from u to u2
0567     QCOMPARE(c, u2.arg().toConst()); // c is now in u2
0568     QVERIFY(u.arg().isNull()); // u has null arg now
0569 
0570     c = KDbConstExpression(KDbToken::INTEGER_CONST, 17);
0571     u = KDbUnaryExpression('-', c);
0572     u2 = KDbUnaryExpression('+', c);
0573     // u2 takes c arg from u
0574     QCOMPARE(c, u2.arg().toConst()); // c is now in u2
0575     QVERIFY(u.arg().isNull()); // u has null arg now
0576 
0577     // -- cycles
0578     c = KDbConstExpression(KDbToken::INTEGER_CONST, 17);
0579     u = KDbUnaryExpression('-', c);
0580     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 3);
0581     u2 = KDbUnaryExpression('+', c1);
0582     u2.setArg(u);
0583     u.setArg(u2);
0584 
0585     QTest::ignoreMessage(QtWarningMsg, R"w(Cycle detected in expression (depth 2):
0586 1: Unary -
0587 2: Unary +)w");
0588     QCOMPARE(u.toString(nullptr), KDbEscapedString("-+<CYCLE!>"));
0589 
0590     QTest::ignoreMessage(QtWarningMsg, R"w(Cycle detected in expression (depth 2):
0591 1: Unary +
0592 2: Unary -)w");
0593     QCOMPARE(u2.toString(nullptr), KDbEscapedString("+-<CYCLE!>"));
0594 }
0595 
0596 void ExpressionsTest::testBinaryExpression()
0597 {
0598     KDbBinaryExpression b;
0599     KDbBinaryExpression b2;
0600     KDbConstExpression c;
0601     KDbConstExpression c1;
0602 
0603     // -- empty
0604     KDbBinaryExpression emptyBinary;
0605     QVERIFY(emptyBinary.isNull());
0606     QVERIFY(emptyBinary.isBinary());
0607     QVERIFY(emptyBinary.clone().isBinary());
0608     QVERIFY(emptyBinary.left().isNull());
0609     QVERIFY(emptyBinary.right().isNull());
0610 
0611     QTest::ignoreMessage(QtWarningMsg,
0612                          "Setting KDbBinaryExpression to null because left argument is not specified");
0613     b = KDbBinaryExpression(KDbExpression(), '-', KDbExpression());
0614     QVERIFY(b.left().isNull());
0615     QVERIFY(b.right().isNull());
0616     QVERIFY(b.isNull()); // it's null because args are null
0617     //qDebug() << b.toString(nullptr);
0618     QCOMPARE(b.toString(nullptr), KDbEscapedString("<UNKNOWN!>"));
0619     c = KDbConstExpression(KDbToken::INTEGER_CONST, 10);
0620     QTest::ignoreMessage(QtWarningMsg,
0621                          "Setting KDbBinaryExpression to null because right argument is not specified");
0622     b = KDbBinaryExpression(c, '-', KDbExpression());
0623     QVERIFY(b.left().isNull());
0624     QVERIFY(b.right().isNull());
0625     QVERIFY(b.isNull()); // it's null because one arg is null
0626     //qDebug() << b.toString(nullptr);
0627     QCOMPARE(b.toString(nullptr), KDbEscapedString("<UNKNOWN!>"));
0628     QTest::ignoreMessage(QtWarningMsg,
0629                          "Setting KDbBinaryExpression to null because left argument is not specified");
0630     b = KDbBinaryExpression(KDbExpression(), '-', c);
0631     QVERIFY(b.left().isNull());
0632     QVERIFY(b.right().isNull());
0633     QVERIFY(b.isNull()); // it's null because one arg is null
0634     //qDebug() << b.toString(nullptr);
0635     QCOMPARE(b.toString(nullptr), KDbEscapedString("<UNKNOWN!>"));
0636 
0637     // -- copy ctor & cloning
0638     c = KDbConstExpression(KDbToken::INTEGER_CONST, 3);
0639     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 4);
0640     b = KDbBinaryExpression(c, '/', c1);
0641     testCloneExpression(b);
0642     QCOMPARE(b.token().name(), QString("/"));
0643     QCOMPARE(b.toString(nullptr), KDbEscapedString("3 / 4"));
0644     QCOMPARE(c1, b.right().toConst());
0645 
0646     b2 = KDbBinaryExpression(b, '*', b.clone());
0647     testCloneExpression(b2);
0648     QCOMPARE(b2.token().name(), QString("*"));
0649     QCOMPARE(b2.toString(nullptr), KDbEscapedString("3 / 4 * 3 / 4"));
0650     QCOMPARE(b, b2.left().toBinary());
0651 
0652     // -- cycles
0653     // --- ref to parent
0654     b = KDbBinaryExpression(
0655             KDbConstExpression(KDbToken::INTEGER_CONST, 1), '+', KDbConstExpression(KDbToken::INTEGER_CONST, 2));
0656     KDbEscapedString s = b.toString(nullptr);
0657     QTest::ignoreMessage(QtWarningMsg,
0658                          QRegularExpression("Expression BinaryExp(.*) cannot be set as own child"));
0659     b.setLeft(b); // should not work
0660     //qDebug() << b.toString(nullptr);
0661     QCOMPARE(s, b.toString(nullptr));
0662     // --- cannot set twice
0663     c = b.left().toConst();
0664     b.setLeft(c);
0665     QCOMPARE(s, b.toString(nullptr));
0666     // --- ref to grandparent
0667     b = KDbBinaryExpression(
0668             KDbConstExpression(KDbToken::INTEGER_CONST, 1), '+', KDbConstExpression(KDbToken::INTEGER_CONST, 2));
0669     c = KDbConstExpression(KDbToken::INTEGER_CONST, 10);
0670     b2 = KDbBinaryExpression(b, '-', c);
0671     //qDebug() << b2.toString(nullptr);
0672     QCOMPARE(b2.toString(nullptr), KDbEscapedString("1 + 2 - 10"));
0673     QTest::ignoreMessage(QtWarningMsg, R"w(Cycle detected in expression (depth 2):
0674 1: Arithm -
0675 2: Arithm +)w");
0676     b.setRight(b2);
0677     //qDebug() << b2.toString(nullptr);
0678     QCOMPARE(b2.toString(nullptr), KDbEscapedString("1 + <CYCLE!> - 10"));
0679 
0680     // -- moving right argument to left should remove right arg
0681     b = KDbBinaryExpression(
0682             KDbConstExpression(KDbToken::INTEGER_CONST, 1), '+', KDbConstExpression(KDbToken::INTEGER_CONST, 2));
0683     c = b.right().toConst();
0684     b.setLeft(c);
0685     //qDebug() << b.toString(nullptr);
0686     QCOMPARE(b.toString(nullptr), KDbEscapedString("2 + <UNKNOWN!>"));
0687 
0688     // -- moving left argument to right should remove left arg
0689     b = KDbBinaryExpression(
0690             KDbConstExpression(KDbToken::INTEGER_CONST, 1), '+', KDbConstExpression(KDbToken::INTEGER_CONST, 2));
0691     c = b.left().toConst();
0692     b.setRight(c);
0693     //qDebug() << b.toString(nullptr);
0694     QCOMPARE(b.toString(nullptr), KDbEscapedString("<UNKNOWN!> + 1"));
0695 }
0696 
0697 void ExpressionsTest::testBinaryExpressionCloning_data()
0698 {
0699     QTest::addColumn<KDbToken>("type1");
0700     QTest::addColumn<QVariant>("const1");
0701     QTest::addColumn<KDbToken>("token");
0702     QTest::addColumn<KDbToken>("type2");
0703     QTest::addColumn<QVariant>("const2");
0704     QTest::addColumn<QString>("string");
0705 
0706 #define T(type1, const1, token, type2, const2, string) \
0707         QTest::newRow(qPrintable(TO_TOKEN(token).name())) \
0708             << type1 << QVariant(const1) << TO_TOKEN(token) \
0709             << type2 << QVariant(const2) << QString(string)
0710 
0711     T(KDbToken::INTEGER_CONST, 3, '/', KDbToken::INTEGER_CONST, 4, "3 / 4");
0712     T(KDbToken::INTEGER_CONST, 3, KDbToken::BITWISE_SHIFT_RIGHT, KDbToken::INTEGER_CONST, 4, "3 >> 4");
0713     T(KDbToken::INTEGER_CONST, 3, KDbToken::BITWISE_SHIFT_LEFT, KDbToken::INTEGER_CONST, 4, "3 << 4");
0714     T(KDbToken::INTEGER_CONST, 3, KDbToken::NOT_EQUAL, KDbToken::INTEGER_CONST, 4, "3 <> 4");
0715     T(KDbToken::INTEGER_CONST, 3, KDbToken::NOT_EQUAL2, KDbToken::INTEGER_CONST, 4, "3 != 4");
0716     T(KDbToken::INTEGER_CONST, 3, KDbToken::LESS_OR_EQUAL, KDbToken::INTEGER_CONST, 4, "3 <= 4");
0717     T(KDbToken::INTEGER_CONST, 3, KDbToken::GREATER_OR_EQUAL, KDbToken::INTEGER_CONST, 4, "3 >= 4");
0718     T(KDbToken::CHARACTER_STRING_LITERAL, "ABC", KDbToken::LIKE, KDbToken::CHARACTER_STRING_LITERAL, "A%", "'ABC' LIKE 'A%'");
0719     T(KDbToken::INTEGER_CONST, 3, KDbToken::SQL_IN, KDbToken::INTEGER_CONST, 4, "3 IN 4");
0720     T(KDbToken::INTEGER_CONST, 3, KDbToken::SIMILAR_TO, KDbToken::INTEGER_CONST, 4, "3 SIMILAR TO 4");
0721     T(KDbToken::INTEGER_CONST, 3, KDbToken::NOT_SIMILAR_TO, KDbToken::INTEGER_CONST, 4, "3 NOT SIMILAR TO 4");
0722     T(KDbToken::SQL_TRUE, true, KDbToken::OR, KDbToken::SQL_FALSE, false, "TRUE OR FALSE");
0723     T(KDbToken::INTEGER_CONST, 3, KDbToken::AND, KDbToken::INTEGER_CONST, 4, "3 AND 4");
0724     T(KDbToken::INTEGER_CONST, 3, KDbToken::XOR, KDbToken::INTEGER_CONST, 4, "3 XOR 4");
0725     T(KDbToken::CHARACTER_STRING_LITERAL, "AB", KDbToken::CONCATENATION, KDbToken::CHARACTER_STRING_LITERAL, "CD", "'AB' || 'CD'");
0726     T(KDbToken::CHARACTER_STRING_LITERAL, "AB", '+', KDbToken::CHARACTER_STRING_LITERAL, "CD", "'AB' + 'CD'");
0727 #undef T
0728 }
0729 
0730 void ExpressionsTest::testBinaryExpressionCloning()
0731 {
0732     QFETCH(KDbToken, type1);
0733     QFETCH(QVariant, const1);
0734     QFETCH(KDbToken, token);
0735     QFETCH(KDbToken, type2);
0736     QFETCH(QVariant, const2);
0737     QFETCH(QString, string);
0738 
0739     KDbConstExpression c(type1, const1);
0740     KDbConstExpression c1(type2, const2);
0741     KDbBinaryExpression b(c, token, c1);
0742     testCloneExpression(b);
0743     QCOMPARE(b.token(), token);
0744     QCOMPARE(b.token().name(), token.name());
0745     //qDebug() << token << b;
0746     QCOMPARE(b.toString(nullptr), KDbEscapedString(string));
0747     QCOMPARE(c, b.left().toConst());
0748     QCOMPARE(c1, b.right().toConst());
0749 }
0750 
0751 void ExpressionsTest::testFunctionExpression()
0752 {
0753     KDbFunctionExpression emptyFunction;
0754     QVERIFY(emptyFunction.isFunction());
0755     QVERIFY(emptyFunction.clone().isFunction());
0756     QVERIFY(emptyFunction.arguments().isEmpty());
0757     QVERIFY(emptyFunction.isNull());
0758 
0759     KDbNArgExpression args;
0760     args.append(KDbConstExpression(KDbToken::CHARACTER_STRING_LITERAL, "abc"));
0761     args.append(KDbConstExpression(KDbToken::INTEGER_CONST, 2));
0762     KDbFunctionExpression f_substr("SUBSTR", args);
0763     //qDebug() << f_substr.toString();
0764     //qDebug() << f_substr.token().name();
0765     //qDebug() << f_substr.token().toString();
0766 
0767     testCloneExpression(f_substr);
0768     QCOMPARE(f_substr.type(), KDbField::Text);
0769 
0770     args.append(KDbConstExpression(KDbToken::INTEGER_CONST, 1));
0771     KDbFunctionExpression f_substr2("SUBSTR", args);
0772     testCloneExpression(f_substr2);
0773     QCOMPARE(f_substr2.type(), KDbField::Text);
0774     //qDebug() << f_substr.toString();
0775     //qDebug() << f_substr2.toString();
0776     QVERIFY(f_substr != f_substr2); // other objects
0777     QCOMPARE(f_substr.toString(nullptr), f_substr2.toString(nullptr)); // the same signatures
0778     QCOMPARE(f_substr.arguments(), f_substr2.arguments()); // the same arg lists
0779 
0780     // clone the args
0781     KDbNArgExpression args2 = args.clone().toNArg();
0782     //qDebug() << f_substr2;
0783     f_substr2.setArguments(args2);
0784     //qDebug() << f_substr2;
0785     QCOMPARE(f_substr.toString(nullptr), f_substr2.toString(nullptr)); // still the same signatures
0786     QVERIFY(f_substr.arguments() != f_substr2.arguments()); // not the same arg lists
0787 
0788     KDbExpression e = f_substr;
0789     QCOMPARE(e.toFunction(), f_substr);
0790     QCOMPARE(e.toFunction(), f_substr.toFunction());
0791     QVERIFY(e.isFunction());
0792 
0793     // nested functions
0794     f_substr2.arguments().replace(0, f_substr);
0795     QCOMPARE(f_substr2.type(), KDbField::Text);
0796 }
0797 
0798 void ExpressionsTest::testConstExpressionValidate()
0799 {
0800     KDbConstExpression c;
0801 
0802     c = KDbConstExpression(KDbToken::SQL_NULL, QVariant());
0803     QCOMPARE(c.type(), KDbField::Null);
0804     QVERIFY(c.isValid());
0805     QVERIFY(!c.isNull());
0806     QVERIFY(validate(&c));
0807 
0808     // null
0809     c = KDbConstExpression(KDbToken::SQL_NULL, QVariant());
0810     QCOMPARE(c.type(), KDbField::Null);
0811     QVERIFY(validate(&c));
0812     testCloneExpression(c);
0813     //qDebug() << c;
0814 
0815     // integer
0816     c = KDbConstExpression(KDbToken::INTEGER_CONST, -0x7f);
0817     QCOMPARE(c.type(), KDbField::Byte);
0818     QVERIFY(c.isValid());
0819     QVERIFY(c.isNumericType());
0820     QVERIFY(!c.isNull());
0821     QCOMPARE(c.value(), QVariant(-0x7f));
0822     QVERIFY(validate(&c));
0823     testCloneExpression(c);
0824     c.setValue(-0x80);
0825     QCOMPARE(c.type(), KDbField::ShortInteger); // type has been changed by setValue
0826     QCOMPARE(c.value(), QVariant(-0x80));
0827     QVERIFY(validate(&c));
0828     testCloneExpression(c);
0829     //qDebug() << c;
0830 
0831     c = KDbConstExpression(KDbToken::INTEGER_CONST, -10);
0832     QCOMPARE(c.type(), KDbField::Byte);
0833     QVERIFY(c.isValid());
0834     QVERIFY(c.isNumericType());
0835     QCOMPARE(c.value(), QVariant(-10));
0836     QVERIFY(validate(&c));
0837     testCloneExpression(c);
0838     //qDebug() << c;
0839 
0840     c = KDbConstExpression(KDbToken::INTEGER_CONST, 0);
0841     QCOMPARE(c.type(), KDbField::Byte);
0842     QVERIFY(c.isValid());
0843     QVERIFY(c.isNumericType());
0844     QCOMPARE(c.value(), QVariant(0));
0845     QVERIFY(validate(&c));
0846     testCloneExpression(c);
0847     //qDebug() << c;
0848 
0849     c = KDbConstExpression(KDbToken::INTEGER_CONST, 20);
0850     QCOMPARE(c.type(), KDbField::Byte);
0851     QVERIFY(c.isValid());
0852     QVERIFY(c.isNumericType());
0853     QCOMPARE(c.value(), QVariant(20));
0854     QVERIFY(validate(&c));
0855     testCloneExpression(c);
0856     //qDebug() << c;
0857 
0858     c = KDbConstExpression(KDbToken::INTEGER_CONST, 255);
0859     QCOMPARE(c.type(), KDbField::Byte);
0860     QVERIFY(c.isValid());
0861     QVERIFY(c.isNumericType());
0862     QCOMPARE(c.value(), QVariant(255));
0863     QVERIFY(validate(&c));
0864     testCloneExpression(c);
0865     //qDebug() << c;
0866 
0867     c = KDbConstExpression(KDbToken::INTEGER_CONST, -0x80);
0868     QCOMPARE(c.type(), KDbField::ShortInteger);
0869     QVERIFY(c.isValid());
0870     QVERIFY(c.isNumericType());
0871     QCOMPARE(c.value(), QVariant(-0x80));
0872     QVERIFY(validate(&c));
0873     testCloneExpression(c);
0874     //qDebug() << c;
0875 
0876     c = KDbConstExpression(KDbToken::INTEGER_CONST, -0x7fff);
0877     QCOMPARE(c.type(), KDbField::ShortInteger);
0878     QVERIFY(c.isValid());
0879     QVERIFY(c.isNumericType());
0880     QCOMPARE(c.value(), QVariant(-0x7fff));
0881     QVERIFY(validate(&c));
0882     testCloneExpression(c);
0883     //qDebug() << c;
0884 
0885     c = KDbConstExpression(KDbToken::INTEGER_CONST, 256);
0886     QCOMPARE(c.type(), KDbField::ShortInteger);
0887     QVERIFY(c.isValid());
0888     QVERIFY(c.isNumericType());
0889     QCOMPARE(c.value(), QVariant(256));
0890     QVERIFY(validate(&c));
0891     testCloneExpression(c);
0892     //qDebug() << c;
0893 
0894     c = KDbConstExpression(KDbToken::INTEGER_CONST, 0xffff);
0895     QCOMPARE(c.type(), KDbField::ShortInteger);
0896     QVERIFY(c.isValid());
0897     QVERIFY(c.isNumericType());
0898     QCOMPARE(c.value(), QVariant(0xffff));
0899     QVERIFY(validate(&c));
0900     testCloneExpression(c);
0901     //qDebug() << c;
0902 
0903     c = KDbConstExpression(KDbToken::INTEGER_CONST, -0x8000);
0904     QCOMPARE(c.type(), KDbField::Integer);
0905     QVERIFY(c.isValid());
0906     QVERIFY(c.isNumericType());
0907     QCOMPARE(c.value(), QVariant(-0x8000));
0908     QVERIFY(validate(&c));
0909     testCloneExpression(c);
0910     //qDebug() << c;
0911 
0912     c = KDbConstExpression(KDbToken::INTEGER_CONST, uint(0x10000));
0913     QCOMPARE(c.type(), KDbField::Integer);
0914     QVERIFY(c.isValid());
0915     QVERIFY(c.isNumericType());
0916     QCOMPARE(c.value(), QVariant(0x10000));
0917     QVERIFY(validate(&c));
0918     testCloneExpression(c);
0919     //qDebug() << c;
0920 
0921     c = KDbConstExpression(KDbToken::INTEGER_CONST, qlonglong(-0x100000));
0922     QCOMPARE(c.type(), KDbField::BigInteger);
0923     QVERIFY(c.isValid());
0924     QVERIFY(c.isNumericType());
0925     QCOMPARE(c.value(), QVariant(-0x100000));
0926     QVERIFY(validate(&c));
0927     testCloneExpression(c);
0928     //qDebug() << c;
0929 
0930     c = KDbConstExpression(KDbToken::INTEGER_CONST, qulonglong(0x1000000));
0931     QCOMPARE(c.type(), KDbField::BigInteger);
0932     QVERIFY(c.isValid());
0933     QVERIFY(c.isNumericType());
0934     QCOMPARE(c.value(), QVariant(0x1000000));
0935     QVERIFY(validate(&c));
0936     testCloneExpression(c);
0937     //qDebug() << c;
0938 
0939     // string
0940     int oldMaxLen = KDbField::defaultMaxLength(); // save
0941     KDbField::setDefaultMaxLength(0);
0942     c = KDbConstExpression(KDbToken::CHARACTER_STRING_LITERAL, "01234567890");
0943     QVERIFY(c.isValid());
0944     QVERIFY(c.isTextType());
0945     QCOMPARE(c.type(), KDbField::Text);
0946     QCOMPARE(c.value(), QVariant("01234567890"));
0947     QVERIFY(validate(&c));
0948     testCloneExpression(c);
0949     //qDebug() << c;
0950 
0951     KDbField::setDefaultMaxLength(10);
0952     c = KDbConstExpression(KDbToken::CHARACTER_STRING_LITERAL, QString());
0953     QCOMPARE(c.type(), KDbField::Text);
0954     QVERIFY(c.isValid());
0955     QVERIFY(c.isTextType());
0956     QCOMPARE(c.value(), QVariant(QString()));
0957     QVERIFY(validate(&c));
0958     testCloneExpression(c);
0959     //qDebug() << c;
0960 
0961     c = KDbConstExpression(KDbToken::CHARACTER_STRING_LITERAL, QVariant());
0962     QCOMPARE(c.type(), KDbField::Text);
0963     QVERIFY(c.isValid());
0964     QVERIFY(c.isTextType());
0965     QCOMPARE(c.value(), QVariant());
0966     QVERIFY(validate(&c));
0967     testCloneExpression(c);
0968     //qDebug() << c;
0969 
0970     c = KDbConstExpression(KDbToken::CHARACTER_STRING_LITERAL, "01234567890");
0971     QCOMPARE(c.type(), KDbField::LongText);
0972     QVERIFY(c.isValid());
0973     QVERIFY(c.isTextType());
0974     QCOMPARE(c.value(), QVariant("01234567890"));
0975     QVERIFY(validate(&c));
0976     //qDebug() << c;
0977     c.setValue("ąćę");
0978     QCOMPARE(c.value(), QVariant("ąćę"));
0979     QCOMPARE(c.type(), KDbField::Text);
0980     QVERIFY(validate(&c));
0981     testCloneExpression(c);
0982     //qDebug() << c;
0983 
0984     KDbField::setDefaultMaxLength(oldMaxLen); // restore
0985 
0986     // bool
0987     c = KDbConstExpression(KDbToken::SQL_TRUE, true);
0988     QCOMPARE(c.type(), KDbField::Boolean);
0989     QVERIFY(c.isValid());
0990     QVERIFY(!c.isTextType());
0991     QVERIFY(!c.isNumericType());
0992     QCOMPARE(c.value(), QVariant(true));
0993     QVERIFY(validate(&c));
0994     //qDebug() << c;
0995     c.setValue(false);
0996     QCOMPARE(c.value(), QVariant(false));
0997     QVERIFY(validate(&c));
0998     testCloneExpression(c);
0999     //qDebug() << c;
1000 
1001     c = KDbConstExpression(KDbToken::SQL_FALSE, false);
1002     QCOMPARE(c.type(), KDbField::Boolean);
1003     QVERIFY(c.isValid());
1004     QVERIFY(!c.isTextType());
1005     QVERIFY(!c.isNumericType());
1006     QCOMPARE(c.value(), QVariant(false));
1007     QVERIFY(validate(&c));
1008     testCloneExpression(c);
1009     //qDebug() << c;
1010 
1011     // real
1012     c = KDbConstExpression(KDbToken::REAL_CONST, QVariant());
1013     QCOMPARE(c.type(), KDbField::Double);
1014     QVERIFY(c.isValid());
1015     QVERIFY(c.isNumericType());
1016     QVERIFY(c.isFPNumericType());
1017     QCOMPARE(c.value(), QVariant());
1018     QCOMPARE(c.toString(nullptr), KDbEscapedString());
1019     QVERIFY(validate(&c));
1020     testCloneExpression(c);
1021     //qDebug() << c;
1022 
1023     c = KDbConstExpression(KDbToken::REAL_CONST, 3.14159);
1024     QCOMPARE(c.type(), KDbField::Double);
1025     QVERIFY(c.isValid());
1026     QVERIFY(c.isNumericType());
1027     QVERIFY(c.isFPNumericType());
1028     QCOMPARE(c.value(), QVariant(3.14159));
1029     QString piString("3.14159");
1030     // limit precision because it depends on the OS
1031     QCOMPARE(c.toString(nullptr).toString().left(piString.length() - 1), piString.left(piString.length() - 1));
1032     QVERIFY(validate(&c));
1033     //qDebug() << c;
1034     c.setValue(-18.012);
1035     QCOMPARE(c.value(), QVariant(-18.012));
1036     QCOMPARE(c.toString(nullptr), KDbEscapedString("-18.012"));
1037     QVERIFY(validate(&c));
1038     testCloneExpression(c);
1039     //qDebug() << c;
1040 
1041     QByteArray largeDecimal("2147483647.2147483647");
1042     c = KDbConstExpression(KDbToken::REAL_CONST, largeDecimal);
1043     QCOMPARE(c.type(), KDbField::Double);
1044     QVERIFY(c.isValid());
1045     QVERIFY(c.isNumericType());
1046     QVERIFY(c.isFPNumericType());
1047     QCOMPARE(c.value(), QVariant(largeDecimal));
1048     QCOMPARE(c.toString(nullptr), KDbEscapedString(largeDecimal));
1049     largeDecimal = "-10.2147483647";
1050     QVERIFY(validate(&c));
1051     testCloneExpression(c);
1052     //qDebug() << c;
1053     c = KDbConstExpression(KDbToken::REAL_CONST, largeDecimal);
1054     QCOMPARE(c.value(), QVariant(largeDecimal));
1055     QCOMPARE(c.toString(nullptr), KDbEscapedString(largeDecimal));
1056     QVERIFY(validate(&c));
1057     testCloneExpression(c);
1058     //qDebug() << c;
1059 
1060     // date
1061     QDate date(QDate::currentDate());
1062     c = KDbConstExpression(KDbToken::DATE_CONST, date);
1063     QVERIFY(c.isValid());
1064     QVERIFY(c.isDateTimeType());
1065     QCOMPARE(c.type(), KDbField::Date);
1066     QCOMPARE(c.value(), QVariant(date));
1067     QVERIFY(validate(&c));
1068     testCloneExpression(c);
1069     //qDebug() << c;
1070     date = date.addDays(17);
1071     c.setValue(date);
1072     QCOMPARE(c.value(), QVariant(date));
1073     QVERIFY(c.isValid());
1074     QVERIFY(c.isDateTimeType());
1075     QVERIFY(validate(&c));
1076     testCloneExpression(c);
1077 
1078     KDbDate dateKDb(KDbYear("2018"), "11", "27");
1079     c.setValue(QVariant::fromValue(dateKDb));
1080     QCOMPARE(c.value(), QVariant::fromValue(dateKDb));
1081     QVERIFY(c.isValid());
1082     QVERIFY(c.isDateTimeType());
1083     QVERIFY(validate(&c));
1084     testCloneExpression(c);
1085 
1086     // time
1087     QTime time(QTime::currentTime());
1088     c = KDbConstExpression(KDbToken::TIME_CONST, time);
1089     QCOMPARE(c.type(), KDbField::Time);
1090     QVERIFY(c.isValid());
1091     QVERIFY(c.isDateTimeType());
1092     QCOMPARE(c.value(), QVariant(time));
1093     testCloneExpression(c);
1094     QVERIFY(validate(&c));
1095     time = time.addMSecs(1200123);
1096     c.setValue(time);
1097     QCOMPARE(c.value(), QVariant(time));
1098     QVERIFY(c.isValid());
1099     QVERIFY(c.isDateTimeType());
1100     QVERIFY(validate(&c));
1101     testCloneExpression(c);
1102 
1103     KDbTime timeKDb("12", "34", "56", "789");
1104     c.setValue(QVariant::fromValue(timeKDb));
1105     QCOMPARE(c.value(), QVariant::fromValue(timeKDb));
1106     QVERIFY(c.isValid());
1107     QVERIFY(c.isDateTimeType());
1108     QVERIFY(validate(&c));
1109     testCloneExpression(c);
1110 
1111     // date/time
1112     QDateTime dateTime(QDateTime::currentDateTime());
1113     c = KDbConstExpression(KDbToken::DATETIME_CONST, dateTime);
1114     QCOMPARE(c.type(), KDbField::DateTime);
1115     QVERIFY(c.isValid());
1116     QVERIFY(c.isDateTimeType());
1117     QCOMPARE(c.value(), QVariant(dateTime));
1118     QVERIFY(validate(&c));
1119     testCloneExpression(c);
1120     //qDebug() << c;
1121     dateTime = dateTime.addDays(-17);
1122     c.setValue(dateTime);
1123     QCOMPARE(c.value(), QVariant(dateTime));
1124     QVERIFY(c.isValid());
1125     QVERIFY(c.isDateTimeType());
1126     QVERIFY(validate(&c));
1127     testCloneExpression(c);
1128     //qDebug() << c;
1129     KDbDateTime dateTimeKDb(dateKDb, timeKDb);
1130     c.setValue(QVariant::fromValue(dateTimeKDb));
1131 //    qDebug() << QVariant::fromValue(dateTimeKDb);
1132 //    qDebug() << QVariant::fromValue(dateTimeKDb).isValid();
1133 //    qDebug() << c.value();
1134 //    qDebug() << c.value().isValid();
1135 //    qDebug() << (QVariant::fromValue(dateTimeKDb) == c.value());
1136     QCOMPARE(c.value(), QVariant::fromValue(dateTimeKDb));
1137     QVERIFY(c.isValid());
1138     QVERIFY(c.isDateTimeType());
1139     QVERIFY(validate(&c));
1140     testCloneExpression(c);
1141 
1142     // setValue()
1143     c = KDbConstExpression(KDbToken::INTEGER_CONST, 124);
1144     QCOMPARE(c.value(), QVariant(124));
1145     c.setValue(299);
1146     QCOMPARE(c.value(), QVariant(299));
1147     QVERIFY(c.isValid());
1148     QVERIFY(c.isNumericType());
1149     QVERIFY(!c.isFPNumericType());
1150     testCloneExpression(c);
1151     //qDebug() << c;
1152 }
1153 
1154 void ExpressionsTest::testUnaryExpressionValidate()
1155 {
1156     KDbConstExpression c;
1157     KDbConstExpression c1;
1158     KDbUnaryExpression u;
1159     KDbUnaryExpression u2;
1160 
1161     // cycles detected by validate()
1162     c = KDbConstExpression(KDbToken::INTEGER_CONST, 17);
1163     u = KDbUnaryExpression('-', c);
1164     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 3);
1165     u2 = KDbUnaryExpression('+', c1);
1166     u2.setArg(u);
1167     u.setArg(u2);
1168     const char *warning = R"w(Cycle detected in expression (depth 2):
1169 1: Unary -
1170 2: Unary +)w";
1171     QTest::ignoreMessage(QtWarningMsg, warning);
1172     warning = R"w(Cycle detected in expression (depth 2):
1173 1: Unary +
1174 2: Unary -)w";
1175     QTest::ignoreMessage(QtWarningMsg, warning);
1176     warning = R"w(Cycle detected in expression (depth 2):
1177 1: Unary -
1178 2: Unary +)w";
1179     QTest::ignoreMessage(QtWarningMsg, warning);
1180     QTest::ignoreMessage(QtWarningMsg, warning);
1181     QVERIFY(!validate(&u));
1182     ////qDebug() << c << u << c1 << u2;
1183 
1184     // NOT NULL is NULL
1185     c = KDbConstExpression(KDbToken::SQL_NULL, QVariant());
1186     u = KDbUnaryExpression(KDbToken::NOT, c);
1187     QCOMPARE(u.type(), KDbField::Null);
1188     QVERIFY(validate(&u));
1189     testCloneExpression(u);
1190 
1191     // NOT "abc" is INVALID
1192     c = KDbConstExpression(KDbToken::CHARACTER_STRING_LITERAL, "abc");
1193     u = KDbUnaryExpression(KDbToken::NOT, c);
1194     QCOMPARE(u.type(), KDbField::InvalidType);
1195     QVERIFY(!validate(&u));
1196     testCloneExpression(u);
1197 }
1198 
1199 void ExpressionsTest::testNArgExpressionValidate()
1200 {
1201     KDbNArgExpression n;
1202     KDbConstExpression c;
1203     KDbConstExpression c1;
1204     KDbConstExpression c2;
1205 
1206     c = KDbConstExpression(KDbToken::SQL_NULL, QVariant());
1207     QCOMPARE(c.type(), KDbField::Null);
1208     QVERIFY(validate(&c));
1209 
1210     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
1211     c = KDbConstExpression(KDbToken::INTEGER_CONST, 0);
1212     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 1);
1213     n.append(c);
1214     n.append(c1);
1215     QCOMPARE(n.type(), KDbField::Tuple);
1216     QVERIFY(validate(&n));
1217     testCloneExpression(n);
1218     ////qDebug() << c << c1 << n;
1219 
1220     // -- a list of arguments
1221     n = KDbNArgExpression(KDb::ArgumentListExpression, ',');
1222     c = KDbConstExpression(KDbToken::INTEGER_CONST, 1);
1223     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 2);
1224     c2 = KDbConstExpression(KDbToken::INTEGER_CONST, 3);
1225     n.append(c);
1226     n.append(c1);
1227     n.append(c2);
1228     QCOMPARE(n.type(), KDbField::Tuple);
1229     QVERIFY(validate(&n));
1230     QVERIFY(n.isValid());
1231 }
1232 
1233 void ExpressionsTest::testBinaryExpressionValidate_data()
1234 {
1235     QTest::addColumn<KDbToken>("type1");
1236     QTest::addColumn<QVariant>("const1");
1237     QTest::addColumn<KDbToken>("token");
1238     QTest::addColumn<KDbToken>("type2");
1239     QTest::addColumn<QVariant>("const2");
1240     QTest::addColumn<KDbField::Type>("type3");
1241 
1242     // invalid
1243     KDbConstExpression c(KDbToken::INTEGER_CONST, 7);
1244     QTest::ignoreMessage(QtWarningMsg,
1245                          "Setting KDbBinaryExpression to null because right argument is not specified");
1246     KDbBinaryExpression b(c, '+', KDbExpression());
1247     QCOMPARE(b.type(), KDbField::InvalidType);
1248     QVERIFY(!validate(&b));
1249     testCloneExpression(b);
1250     //qDebug() << b;
1251 
1252     QTest::ignoreMessage(QtWarningMsg,
1253                          "Setting KDbBinaryExpression to null because left argument is not specified");
1254     b = KDbBinaryExpression(KDbExpression(), '/', KDbExpression());
1255     QCOMPARE(b.type(), KDbField::InvalidType);
1256     QVERIFY(!validate(&b)); // unknown class
1257     testCloneExpression(b);
1258     //qDebug() << b;
1259 
1260     // invalid left or right
1261     QTest::ignoreMessage(QtWarningMsg,
1262                          "Setting KDbBinaryExpression to null because left argument is not specified");
1263     KDbBinaryExpression b2(b, '*', c.clone());
1264     QCOMPARE(b2.type(), KDbField::InvalidType);
1265     QVERIFY(!validate(&b2)); // unknown class
1266     testCloneExpression(b2);
1267     //qDebug() << b2;
1268 
1269     QTest::ignoreMessage(QtWarningMsg,
1270                          "Setting KDbBinaryExpression to null because right argument is not specified");
1271     KDbBinaryExpression b3(c.clone(), '*', b);
1272     QCOMPARE(b3.type(), KDbField::InvalidType);
1273     QVERIFY(!validate(&b3)); // unknown class
1274     testCloneExpression(b3);
1275     //qDebug() << b3;
1276 
1277 #define TNAME(type) type.name().toLatin1()
1278 
1279 #define T1(type1, const1, tokenOrChar, type2, const2, type3) \
1280         QTest::newRow( \
1281             qPrintable(QString::number(__LINE__) + ": " + TNAME(type1) + " " \
1282              + QVariant(const1).toString() + " " \
1283              + TNAME(TO_TOKEN(tokenOrChar)) + " " \
1284              + TNAME(type2) + " " \
1285              + QVariant(const2).toString().toLatin1())) \
1286             << type1 << QVariant(const1) \
1287             << TO_TOKEN(tokenOrChar) << type2 << QVariant(const2) \
1288             << type3
1289 // tests both f(x, y) and f(y, x)
1290 #define T(type1, const1, token, type2, const2, type3) \
1291         T1(type1, const1, token, type2, const2, type3); \
1292         T1(type2, const2, token, type1, const1, type3)
1293 
1294     // null
1295     T(KDbToken::SQL_NULL, QVariant(), '+', KDbToken::INTEGER_CONST, 7, KDbField::Null);
1296     // NULL OR true is true
1297     T(KDbToken::SQL_NULL, QVariant(), KDbToken::OR, KDbToken::SQL_TRUE, true, KDbField::Boolean);
1298     // NULL AND true is NULL
1299     T(KDbToken::SQL_NULL, QVariant(), KDbToken::AND, KDbToken::SQL_TRUE, true, KDbField::Null);
1300     // NULL OR false is NULL
1301     T(KDbToken::SQL_NULL, QVariant(), KDbToken::OR, KDbToken::SQL_FALSE, false, KDbField::Null);
1302     // NULL AND false is false
1303     T(KDbToken::SQL_NULL, QVariant(), KDbToken::AND, KDbToken::SQL_FALSE, false, KDbField::Boolean);
1304     // NULL AND NULL is NULL
1305     T(KDbToken::SQL_NULL, QVariant(), KDbToken::AND, KDbToken::SQL_NULL, QVariant(), KDbField::Null);
1306     // NULL OR NULL is NULL
1307     T(KDbToken::SQL_NULL, QVariant(), KDbToken::OR, KDbToken::SQL_NULL, QVariant(), KDbField::Null);
1308     // NULL XOR TRUE is NULL
1309     T(KDbToken::SQL_NULL, QVariant(), KDbToken::XOR, KDbToken::SQL_TRUE, true, KDbField::Null);
1310     // NULL XOR NULL is NULL
1311     T(KDbToken::SQL_NULL, QVariant(), KDbToken::XOR, KDbToken::SQL_NULL, QVariant(), KDbField::Null);
1312     // NULL AND "xyz" is invalid
1313     T(KDbToken::SQL_NULL, QVariant(), KDbToken::OR, KDbToken::CHARACTER_STRING_LITERAL, "xyz", KDbField::InvalidType);
1314     // integer
1315     // -- KDb::ArithmeticExpression only: resulting type is Integer or more
1316     //    see explanation for KDb::maximumForIntegerFieldTypes()
1317     T(KDbToken::INTEGER_CONST, 50, '+', KDbToken::INTEGER_CONST, 20, KDbField::Integer);
1318     T(KDbToken::INTEGER_CONST, 50, '-', KDbToken::INTEGER_CONST, 20, KDbField::Integer);
1319     T(KDbToken::INTEGER_CONST, 50, '*', KDbToken::INTEGER_CONST, 20, KDbField::Integer);
1320     T(KDbToken::INTEGER_CONST, 50, '/', KDbToken::INTEGER_CONST, 20, KDbField::Integer);
1321     T(KDbToken::INTEGER_CONST, 50, '&', KDbToken::INTEGER_CONST, 20, KDbField::Integer);
1322     T(KDbToken::INTEGER_CONST, 50, '|', KDbToken::INTEGER_CONST, 20, KDbField::Integer);
1323     T(KDbToken::INTEGER_CONST, 50, '%', KDbToken::INTEGER_CONST, 20, KDbField::Integer);
1324     T(KDbToken::INTEGER_CONST, 50, KDbToken::BITWISE_SHIFT_RIGHT, KDbToken::INTEGER_CONST, 20, KDbField::Integer);
1325     T(KDbToken::INTEGER_CONST, 50, KDbToken::BITWISE_SHIFT_LEFT, KDbToken::INTEGER_CONST, 20, KDbField::Integer);
1326     T(KDbToken::INTEGER_CONST, 300, '+', KDbToken::INTEGER_CONST, 20, KDbField::Integer);
1327     T(KDbToken::INTEGER_CONST, 300, '+', KDbToken::INTEGER_CONST, 300, KDbField::Integer);
1328     T(KDbToken::INTEGER_CONST, 300, '+', KDbToken::INTEGER_CONST, 300, KDbField::Integer);
1329     T(KDbToken::INTEGER_CONST, 50, '+', KDbToken::INTEGER_CONST, qulonglong(INT_MAX), KDbField::BigInteger);
1330     T(KDbToken::INTEGER_CONST, INT_MAX, '+', KDbToken::INTEGER_CONST, qulonglong(INT_MAX), KDbField::BigInteger);
1331 
1332     T(KDbToken::INTEGER_CONST, 50, '<', KDbToken::INTEGER_CONST, 20, KDbField::Boolean);
1333     T(KDbToken::INTEGER_CONST, 50, '=', KDbToken::INTEGER_CONST, 20, KDbField::Boolean);
1334     T(KDbToken::INTEGER_CONST, 50, '>', KDbToken::INTEGER_CONST, 20, KDbField::Boolean);
1335     T(KDbToken::INTEGER_CONST, 50, '<', KDbToken::INTEGER_CONST, INT_MAX, KDbField::Boolean);
1336     T(KDbToken::INTEGER_CONST, 50, '<', KDbToken::INTEGER_CONST, qulonglong(INT_MAX), KDbField::Boolean);
1337     T(KDbToken::INTEGER_CONST, qulonglong(INT_MAX), '<', KDbToken::INTEGER_CONST, INT_MAX, KDbField::Boolean);
1338     T(KDbToken::INTEGER_CONST, 300, KDbToken::LESS_OR_EQUAL, KDbToken::INTEGER_CONST, 20, KDbField::Boolean);
1339     T(KDbToken::INTEGER_CONST, 300, KDbToken::GREATER_OR_EQUAL, KDbToken::INTEGER_CONST, 300, KDbField::Boolean);
1340     T(KDbToken::INTEGER_CONST, 300, '>', KDbToken::INTEGER_CONST, 300, KDbField::Boolean);
1341 
1342     T(KDbToken::INTEGER_CONST, 300, KDbToken::OR, KDbToken::INTEGER_CONST, 20, KDbField::InvalidType);
1343     T(KDbToken::INTEGER_CONST, 300, KDbToken::AND, KDbToken::INTEGER_CONST, 20, KDbField::InvalidType);
1344     T(KDbToken::INTEGER_CONST, 300, KDbToken::XOR, KDbToken::INTEGER_CONST, 20, KDbField::InvalidType);
1345     T(KDbToken::INTEGER_CONST, 300, KDbToken::OR, KDbToken::SQL_NULL, QVariant(), KDbField::InvalidType);
1346     // real
1347     T(KDbToken::REAL_CONST, 0.5, '+', KDbToken::REAL_CONST, -9.4, KDbField::Double);
1348     T(KDbToken::REAL_CONST, 0.5, '-', KDbToken::REAL_CONST, -9.4, KDbField::Double);
1349     T(KDbToken::REAL_CONST, 0.5, '*', KDbToken::REAL_CONST, -9.4, KDbField::Double);
1350     T(KDbToken::REAL_CONST, 0.5, '/', KDbToken::REAL_CONST, -9.4, KDbField::Double);
1351     T(KDbToken::REAL_CONST, 0.5, '&', KDbToken::REAL_CONST, -9.4, KDbField::Integer);
1352     T(KDbToken::REAL_CONST, 0.5, '&', KDbToken::INTEGER_CONST, 9, KDbField::Byte);
1353     T(KDbToken::REAL_CONST, 0.5, '&', KDbToken::INTEGER_CONST, 1000, KDbField::ShortInteger);
1354     T(KDbToken::REAL_CONST, 0.5, '&', KDbToken::INTEGER_CONST, qulonglong(INT_MAX), KDbField::BigInteger);
1355     T(KDbToken::REAL_CONST, 0.5, '%', KDbToken::REAL_CONST, -9.4, KDbField::Double);
1356     T(KDbToken::REAL_CONST, 0.5, KDbToken::BITWISE_SHIFT_RIGHT, KDbToken::REAL_CONST, 9.4, KDbField::Integer);
1357     T(KDbToken::REAL_CONST, 0.5, KDbToken::BITWISE_SHIFT_LEFT, KDbToken::REAL_CONST, 9.4, KDbField::Integer);
1358     T(KDbToken::REAL_CONST, 0.5, '+', KDbToken::INTEGER_CONST, 300, KDbField::Double);
1359     T(KDbToken::REAL_CONST, 0.5, '-', KDbToken::INTEGER_CONST, 300, KDbField::Double);
1360     T(KDbToken::REAL_CONST, 0.5, '/', KDbToken::INTEGER_CONST, 300, KDbField::Double);
1361     T(KDbToken::REAL_CONST, 0.5, '-', KDbToken::SQL_NULL, QVariant(), KDbField::Null);
1362 
1363     T(KDbToken::REAL_CONST, 0.5, '>', KDbToken::REAL_CONST, -9.4, KDbField::Boolean);
1364     T(KDbToken::REAL_CONST, 0.5, '>', KDbToken::INTEGER_CONST, 300, KDbField::Boolean);
1365     T(KDbToken::REAL_CONST, 0.5, '=', KDbToken::INTEGER_CONST, 300, KDbField::Boolean);
1366     T(KDbToken::REAL_CONST, 0.5, '<', KDbToken::INTEGER_CONST, qulonglong(INT_MAX), KDbField::Boolean);
1367     T(KDbToken::REAL_CONST, 0.5, KDbToken::LESS_OR_EQUAL, KDbToken::INTEGER_CONST, 300, KDbField::Boolean);
1368     T(KDbToken::REAL_CONST, 0.5, KDbToken::GREATER_OR_EQUAL, KDbToken::INTEGER_CONST, 300, KDbField::Boolean);
1369     T(KDbToken::REAL_CONST, 0.5, '>', KDbToken::SQL_NULL, QVariant(), KDbField::Null);
1370 
1371     T(KDbToken::REAL_CONST, 30.2, KDbToken::OR, KDbToken::REAL_CONST, 20, KDbField::InvalidType);
1372     T(KDbToken::REAL_CONST, 30.2, KDbToken::AND, KDbToken::REAL_CONST, 20, KDbField::InvalidType);
1373     T(KDbToken::REAL_CONST, 30.2, KDbToken::XOR, KDbToken::REAL_CONST, 20, KDbField::InvalidType);
1374     // string
1375     T(KDbToken::CHARACTER_STRING_LITERAL, "ab", KDbToken::CONCATENATION, KDbToken::CHARACTER_STRING_LITERAL, "cd", KDbField::Text);
1376     T(KDbToken::CHARACTER_STRING_LITERAL, "ab", '+', KDbToken::CHARACTER_STRING_LITERAL, "cd", KDbField::Text);
1377 
1378     T(KDbToken::SQL_NULL, QVariant(), KDbToken::CONCATENATION, KDbToken::CHARACTER_STRING_LITERAL, "cd", KDbField::Null);
1379     T(KDbToken::SQL_NULL, QVariant(), '+', KDbToken::CHARACTER_STRING_LITERAL, "cd", KDbField::Null);
1380 
1381     T(KDbToken::INTEGER_CONST, 50, KDbToken::CONCATENATION, KDbToken::INTEGER_CONST, 20, KDbField::InvalidType);
1382     T(KDbToken::CHARACTER_STRING_LITERAL, "ab", KDbToken::CONCATENATION, KDbToken::INTEGER_CONST, 20, KDbField::InvalidType);
1383     T(KDbToken::CHARACTER_STRING_LITERAL, "ab", '+', KDbToken::INTEGER_CONST, 20, KDbField::InvalidType);
1384 
1385     T(KDbToken::CHARACTER_STRING_LITERAL, "ab", KDbToken::GREATER_OR_EQUAL, KDbToken::CHARACTER_STRING_LITERAL, "cd", KDbField::Boolean);
1386     T(KDbToken::CHARACTER_STRING_LITERAL, "ab", '<', KDbToken::INTEGER_CONST, 3, KDbField::InvalidType);
1387     T(KDbToken::CHARACTER_STRING_LITERAL, "ab", '+', KDbToken::CHARACTER_STRING_LITERAL, "cd", KDbField::Text);
1388     T(KDbToken::CHARACTER_STRING_LITERAL, "A", KDbToken::OR, KDbToken::REAL_CONST, 20, KDbField::InvalidType);
1389     T(KDbToken::CHARACTER_STRING_LITERAL, "A", KDbToken::AND, KDbToken::REAL_CONST, 20, KDbField::InvalidType);
1390     T(KDbToken::CHARACTER_STRING_LITERAL, "A", KDbToken::XOR, KDbToken::REAL_CONST, 20, KDbField::InvalidType);
1391     // bool
1392     T(KDbToken::SQL_TRUE, true, '<', KDbToken::SQL_FALSE, false, KDbField::Boolean);
1393     T(KDbToken::SQL_TRUE, true, '=', KDbToken::SQL_FALSE, false, KDbField::Boolean);
1394     T(KDbToken::SQL_TRUE, true, '+', KDbToken::SQL_FALSE, false, KDbField::InvalidType);
1395     T(KDbToken::SQL_TRUE, true, '<', KDbToken::INTEGER_CONST, 20, KDbField::Boolean);
1396     T(KDbToken::SQL_TRUE, true, '<', KDbToken::REAL_CONST, -10.1, KDbField::Boolean);
1397     T(KDbToken::SQL_TRUE, true, '-', KDbToken::SQL_NULL, QVariant(), KDbField::Null);
1398     T(KDbToken::SQL_TRUE, true, '<', KDbToken::SQL_NULL, QVariant(), KDbField::Null);
1399     T(KDbToken::SQL_TRUE, true, KDbToken::OR, KDbToken::SQL_FALSE, false, KDbField::Boolean);
1400     T(KDbToken::SQL_TRUE, true, KDbToken::AND, KDbToken::SQL_FALSE, false, KDbField::Boolean);
1401     T(KDbToken::SQL_TRUE, true, KDbToken::XOR, KDbToken::SQL_FALSE, false, KDbField::Boolean);
1402     // date/time
1403     T(KDbToken::DATE_CONST, QDate(2001, 1, 2), '=', KDbToken::DATE_CONST, QDate(2002, 1, 2), KDbField::Boolean);
1404     T(KDbToken::DATETIME_CONST, QDateTime(QDate(2001, 1, 2), QTime(1, 2, 3)), KDbToken::LESS_OR_EQUAL, KDbToken::DATE_CONST, QDateTime::currentDateTime(), KDbField::Boolean);
1405     T(KDbToken::TIME_CONST, QTime(1, 2, 3), '<', KDbToken::TIME_CONST, QTime::currentTime(), KDbField::Boolean);
1406     T(KDbToken::DATE_CONST, QDate(2001, 1, 2), '=', KDbToken::INTEGER_CONST, 17, KDbField::InvalidType);
1407     T(KDbToken::DATE_CONST, QDate(2001, 1, 2), '=', KDbToken::SQL_NULL, QVariant(), KDbField::Null);
1408     T(KDbToken::DATE_CONST, QDate(2001, 1, 2), KDbToken::OR, KDbToken::SQL_FALSE, false, KDbField::InvalidType);
1409     T(KDbToken::DATE_CONST, QDate(2001, 1, 2), KDbToken::AND, KDbToken::SQL_FALSE, false, KDbField::InvalidType);
1410     T(KDbToken::DATE_CONST, QDate(2001, 1, 2), KDbToken::XOR, KDbToken::SQL_FALSE, false, KDbField::InvalidType);
1411 #undef T
1412 #undef T1
1413 #undef TNAME
1414 }
1415 
1416 void ExpressionsTest::testBinaryExpressionValidate()
1417 {
1418     QFETCH(KDbToken, type1);
1419     QFETCH(QVariant, const1);
1420     QFETCH(KDbToken, token);
1421     QFETCH(KDbToken, type2);
1422     QFETCH(QVariant, const2);
1423     QFETCH(KDbField::Type, type3);
1424 
1425     KDbConstExpression c(type1, const1);
1426     KDbConstExpression c1(type2, const2);
1427     KDbBinaryExpression b(c, token, c1);
1428     //qDebug() << b.type();
1429     //qDebug() << type3;
1430     QCOMPARE(b.type(), type3);
1431     QVERIFY(validate(&b) == (type3 != KDbField::InvalidType));
1432     testCloneExpression(b);
1433 }
1434 
1435 void ExpressionsTest::testFunctionExpressionValidate()
1436 {
1437     KDbFunctionExpression emptyFunction;
1438     QVERIFY(!validate(&emptyFunction));
1439 
1440     KDbNArgExpression args;
1441     args.append(KDbConstExpression(KDbToken::CHARACTER_STRING_LITERAL, "abc"));
1442     args.append(KDbConstExpression(KDbToken::INTEGER_CONST, 2));
1443     KDbFunctionExpression f_substr("SUBSTR", args);
1444     QVERIFY(validate(&f_substr));
1445 
1446     args.append(KDbConstExpression(KDbToken::INTEGER_CONST, 1));
1447     KDbFunctionExpression f_substr2("SUBSTR", args);
1448     QVERIFY(validate(&f_substr2));
1449 
1450     // clone the args
1451     KDbNArgExpression args2 = args.clone().toNArg();
1452     f_substr2.setArguments(args2);
1453     QVERIFY(validate(&f_substr2));
1454 
1455     // wrong type (1st arg)
1456     args = KDbNArgExpression();
1457     args.append(KDbConstExpression(KDbToken::DATETIME_CONST, QDateTime::currentDateTime()));
1458     args.append(KDbConstExpression(KDbToken::INTEGER_CONST, 1));
1459     f_substr2.setArguments(args);
1460     QVERIFY(!validate(&f_substr2));
1461 
1462     // fixed type
1463     KDbConstExpression first = args.arg(0).toConst();
1464     first.setToken(KDbToken::CHARACTER_STRING_LITERAL);
1465     first.setValue("xyz");
1466     QVERIFY(validate(&f_substr2));
1467 
1468     // wrong type (2nd arg)
1469     KDbConstExpression second = args.arg(1).toConst();
1470     second.setToken(KDbToken::REAL_CONST);
1471     second.setValue(3.14);
1472     QVERIFY(!validate(&f_substr2));
1473 
1474     // nested functions
1475     KDbFunctionExpression f_substr3 = f_substr.clone().toFunction();
1476     f_substr3.arguments().replace(0, f_substr.clone());
1477     QVERIFY(validate(&f_substr3));
1478 
1479     // fixed type
1480     args.replace(1, KDbConstExpression(KDbToken::INTEGER_CONST, 1));
1481     QVERIFY(validate(&f_substr2));
1482 
1483     // wrong type (3rd arg)
1484     args.append(KDbConstExpression(KDbToken::REAL_CONST, 1.111));
1485     //qDebug() << args;
1486     //qDebug() << f_substr2;
1487     QVERIFY(!validate(&f_substr2));
1488 
1489     // wrong number of args
1490     f_substr2.setArguments(KDbNArgExpression());
1491     args.append(KDbConstExpression(KDbToken::INTEGER_CONST, 77));
1492     QVERIFY(!validate(&f_substr2));
1493 
1494     KDbFunctionExpression f_noname("", args);
1495     QVERIFY(!validate(&f_noname));
1496 }
1497 
1498 void ExpressionsTest::cleanupTestCase()
1499 {
1500 }