1 /* This file is part of the KDE project
2    Copyright (C) 2011-2016 Jarosław Staniek <staniek@kde.org>
3 
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Library General Public
6    License as published by the Free Software Foundation; either
7    version 2 of the License, or (at your option) any later version.
8 
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Library General Public License for more details.
13 
14    You should have received a copy of the GNU Library General Public License
15    along with this library; see the file COPYING.LIB.  If not, write to
16    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18 */
19 
20 #include "ExpressionsTest.h"
21 
22 #include <QtTest>
23 
24 #include <KDbDateTime>
25 #include <KDbExpression>
26 #include "parser/generated/sqlparser.h"
27 #include "parser/KDbParser_p.h"
28 
29 Q_DECLARE_METATYPE(KDb::ExpressionClass)
30 Q_DECLARE_METATYPE(KDbEscapedString)
31 Q_DECLARE_METATYPE(KDbField::Type)
32 Q_DECLARE_METATYPE(KDbToken)
33 
34 namespace QTest {
35     template<>
toString(const KDbEscapedString & string)36     char *toString(const KDbEscapedString &string)
37     {
38         return qstrdup(qPrintable(string.toString()));
39     }
40 
41     template<>
toString(const KDbField::Type & type)42     char *toString(const KDbField::Type &type)
43     {
44         return qstrdup(qPrintable(KDbField::typeString(type)));
45     }
46 
47     //! Adds a quote if this is the single-character token to match the format of Bison
48     template<>
toString(const KDbToken & token)49     char* toString(const KDbToken &token)
50     {
51         return qstrdup(qPrintable(
52                            token.toChar() ? QString::fromLatin1("'%1'").arg(token.toString())
53                                           : token.toString()
54                       ));
55     }
56 }
57 
QTEST_GUILESS_MAIN(ExpressionsTest)58 QTEST_GUILESS_MAIN(ExpressionsTest)
59 
60 //! Used in macros so characters and KDbTokens can be used interchangeably
61 static inline KDbToken TO_TOKEN(char charValue)
62 {
63     return KDbToken(charValue);
64 }
65 
66 //! Used in macros so characters and KDbTokens can be used interchangeably
TO_TOKEN(KDbToken token)67 static inline KDbToken TO_TOKEN(KDbToken token)
68 {
69     return token;
70 }
71 
initTestCase()72 void ExpressionsTest::initTestCase()
73 {
74 }
75 
76 //! compares two expression @a e1 and @a e2 based on strings/debug strings
77 //! and token strings
78 template <typename T1, typename T2>
compareStrings(const T1 & e1,const T2 & e2)79 static void compareStrings(const T1 &e1, const T2 &e2)
80 {
81     //qDebug() << "compareStrings():"
82     //         << "\ne1:" << e1.toString() << e1.token() << e1.token().ToString()
83     //         << "\ne2:" << e2.toString() << e2.token() << e2.token().toString();
84     QCOMPARE(e1.toString(nullptr), e2.toString(nullptr));
85     QCOMPARE(e1.token(), e2.token());
86     QCOMPARE(e1.token().value(), e2.token().value());
87     QCOMPARE(e1.token().toString(), e2.token().toString());
88 }
89 
90 //! tests clone and copy ctor for @a e1
91 template <typename T>
testCloneExpression(const T & e1)92 static void testCloneExpression(const T &e1)
93 {
94     KDbExpression e1clone = e1.clone();
95     //qDebug() << e1;
96     //qDebug() << e1clone;
97     QVERIFY(e1 != e1.clone());
98     QVERIFY(e1 != e1clone);
99     QVERIFY(e1.clone() != e1clone);
100     QVERIFY(e1.expressionClass() == e1clone.expressionClass());
101     QVERIFY(e1.token() == e1clone.token());
102     compareStrings(e1, e1clone);
103 
104     const T copied(e1);
105     QVERIFY(e1 == copied);
106     QVERIFY(e1.clone() != copied);
107     QVERIFY(e1.expressionClass() == copied.expressionClass());
108     QVERIFY(e1.token() == copied.token());
109     compareStrings(e1, copied);
110 }
111 
112 //! Validates expression @a expr and shows error message on failure
validate(KDbExpression * expr)113 static bool validate(KDbExpression *expr)
114 {
115     KDbParseInfoInternal parseInfo(nullptr);
116     bool ok = expr->validate(&parseInfo);
117     if (!ok) {
118         qInfo() << "Validation of" << *expr << "FAILED.";
119         if (!parseInfo.errorMessage().isEmpty()) {
120             qInfo() << "Error message:" << parseInfo.errorMessage();
121         }
122         if (!parseInfo.errorDescription().isEmpty()) {
123             qInfo() << "Error description:" << parseInfo.errorDescription();
124         }
125     }
126     return ok;
127 }
128 
testNullExpression()129 void ExpressionsTest::testNullExpression()
130 {
131     QVERIFY(KDbExpression() != KDbExpression());
132 
133     KDbExpression e1;
134     KDbExpression e2;
135     QVERIFY(e1.isNull());
136     QVERIFY(!e1.isValid());
137     QVERIFY(!e1.isBinary());
138     QVERIFY(e1.toBinary().isNull());
139     QVERIFY(!e1.isConst());
140     QVERIFY(e1.toConst().isNull());
141     QVERIFY(!e1.isFunction());
142     QVERIFY(e1.toFunction().isNull());
143     QVERIFY(!e1.isNArg());
144     QVERIFY(e1.toNArg().isNull());
145     QVERIFY(!e1.isQueryParameter());
146     QVERIFY(e1.toQueryParameter().isNull());
147     QVERIFY(!e1.isUnary());
148     QVERIFY(e1.toUnary().isNull());
149     QVERIFY(!e1.isVariable());
150     QVERIFY(e1.toVariable().isNull());
151     QCOMPARE(e1.expressionClass(), KDb::UnknownExpression);
152     QCOMPARE(e1.token(), KDbToken());
153     QVERIFY(e1 != KDbExpression());
154     QVERIFY(e1 == e1);
155     QVERIFY(e1 != e2);
156 
157     e1 = e2;
158     QVERIFY(e1.isNull());
159     QCOMPARE(e1, e2);
160     QCOMPARE(e1.toString(nullptr), KDbEscapedString("<UNKNOWN!>"));
161     QCOMPARE(e1.token().name(), QLatin1String("<INVALID_TOKEN>"));
162     QCOMPARE(e1.token().toString(nullptr), QString("<INVALID_TOKEN>"));
163     compareStrings(e1, e2);
164 
165     KDbExpression e3(e2);
166     QVERIFY(e3.isNull());
167     QCOMPARE(e2, e3);
168     compareStrings(e2, e3);
169     //ExpressionDebug << "$$$" << e1.toString() << e1.token() << e1.token().toString();
170 
171     e1 = KDbExpression();
172     testCloneExpression(e1);
173 }
174 
testExpressionClassName_data()175 void ExpressionsTest::testExpressionClassName_data()
176 {
177     QTest::addColumn<KDb::ExpressionClass>("expClass");
178     QTest::addColumn<QString>("name");
179 
180     int c = 0;
181 #define T(n, t) ++c; QTest::newRow(n) << t << n
182     T("Unknown", KDb::UnknownExpression);
183     T("Unary", KDb::UnaryExpression);
184     T("Arithm", KDb::ArithmeticExpression);
185     T("Logical", KDb::LogicalExpression);
186     T("Relational", KDb::RelationalExpression);
187     T("SpecialBinary", KDb::SpecialBinaryExpression);
188     T("Const", KDb::ConstExpression);
189     T("Variable", KDb::VariableExpression);
190     T("Function", KDb::FunctionExpression);
191     T("Aggregation", KDb::AggregationExpression);
192     T("FieldList", KDb::FieldListExpression);
193     T("TableList", KDb::TableListExpression);
194     T("ArgumentList", KDb::ArgumentListExpression);
195     T("QueryParameter", KDb::QueryParameterExpression);
196 #undef T
197     QCOMPARE(c, int(KDb::LastExpressionClass) + 1);
198 }
199 
testExpressionClassName()200 void ExpressionsTest::testExpressionClassName()
201 {
202     QFETCH(KDb::ExpressionClass, expClass);
203     QTEST(expressionClassName(expClass), "name");
204 }
205 
206 #include "KDbUtils_p.h"
207 
testExpressionToken()208 void ExpressionsTest::testExpressionToken()
209 {
210     KDbExpression e1;
211     QVERIFY(!e1.isValid());
212     QVERIFY(!KDbToken().isValid());
213     QCOMPARE(e1.token(), KDbToken());
214     QVERIFY(KDbToken('+').toChar() > 0);
215     QCOMPARE(KDbToken('*'), KDbToken('*'));
216     QCOMPARE(KDbToken('*').toChar(), '*');
217     QCOMPARE(KDbToken('*').value(), int('*'));
218     QCOMPARE(KDbToken('*').name(), QString::fromLatin1("*"));
219     QCOMPARE(KDbToken('*').toString(), QString::fromLatin1("*"));
220     QCOMPARE(KDbToken::LEFT.toChar(), char(0));
221     QCOMPARE(KDbToken().toChar(), char(0));
222     QVERIFY(KDbToken::LEFT.isValid());
223     QVERIFY(KDbToken::maxCharTokenValue > 0);
224     QVERIFY(KDbToken::LEFT.value() > KDbToken::maxCharTokenValue);
225     const QList<KDbToken> allTokens(KDbToken::allTokens());
226     QVERIFY(!allTokens.isEmpty());
227 
228     for(const KDbToken &t : allTokens) {
229         //qDebug() << t << t.value();
230         if (t.toChar() > 0) {
231             QVERIFY(t.value() <= KDbToken::maxCharTokenValue);
232             QCOMPARE(t, KDbToken(char(t.value())));
233             QCOMPARE(t.name(), isprint(t.value()) ? QString(QLatin1Char(uchar(t.value())))
234                                                   : QString::number(t.value()));
235             QCOMPARE(QTest::toString(t), QString::fromLatin1(g_tokenName(t.value())).toLatin1().data());
236         }
237         else {
238             QCOMPARE(t.name(), QString::fromLatin1(g_tokenName(t.value())));
239         }
240     }
241 }
242 
testNArgExpression()243 void ExpressionsTest::testNArgExpression()
244 {
245     KDbNArgExpression n;
246     KDbNArgExpression n2;
247     KDbConstExpression c;
248     KDbConstExpression c1;
249     KDbConstExpression c2;
250     KDbConstExpression c3;
251 
252     // -- empty
253     KDbNArgExpression emptyNarg;
254     QVERIFY(emptyNarg.isNArg());
255     QVERIFY(emptyNarg.clone().isNArg());
256     QVERIFY(emptyNarg.isEmpty());
257     QCOMPARE(emptyNarg.argCount(), 0);
258     QVERIFY(emptyNarg.arg(-1).isNull());
259     QVERIFY(emptyNarg.arg(0).isNull());
260     QVERIFY(!emptyNarg.containsInvalidArgument());
261     QVERIFY(!emptyNarg.containsNullArgument());
262 
263     // -- copy ctor & cloning
264     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
265     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 7);
266     c2 = KDbConstExpression(KDbToken::INTEGER_CONST, 8);
267     n.append(c1);
268     n.append(c2);
269     testCloneExpression(n);
270 
271     // copy on stack
272     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
273     {
274         KDbConstExpression s1(KDbToken::INTEGER_CONST, 7);
275         KDbConstExpression s2(KDbToken::INTEGER_CONST, 8);
276         n.append(s1);
277         n.append(s2);
278         c1 = s1;
279         c2 = s2;
280     }
281     QCOMPARE(n.argCount(), 2);
282     QCOMPARE(n.arg(0).toConst(), c1);
283     QCOMPARE(n.arg(1).toConst(), c2);
284 
285     QCOMPARE(n.token().name(), QString("+"));
286     QCOMPARE(n.toString(nullptr), KDbEscapedString("7, 8"));
287     n.setToken('*');
288     QCOMPARE(n.token().name(), QString("*"));
289 
290     // -- append(KDbExpression), prepend(KDbExpression)
291     KDbExpression e;
292     KDbNArgExpression nNull;
293     QCOMPARE(nNull.argCount(), 0); // empty
294     nNull.append(e);
295     QCOMPARE(nNull.argCount(), 1); // n-arg expression can have null elements
296     nNull = KDbNArgExpression();
297     QCOMPARE(nNull.argCount(), 0); // cleared
298 
299     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
300     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 1);
301     n.append(c1);
302     QVERIFY(!n.isEmpty());
303     QCOMPARE(n.argCount(), 1);
304     QCOMPARE(n.arg(0).toConst(), c1);
305     QCOMPARE(c1.parent().toNArg(), n);
306 
307     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
308     n.append(n);
309     QCOMPARE(n.argCount(), 0); // append should fail since appending expression
310                                // to itself is not allowed
311     n.prepend(n);
312     QCOMPARE(n.argCount(), 0); // append should fail since prepending expression
313                                // to itself is not allowed
314 
315     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
316     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 2);
317     n.append(c1);
318     n.append(c1); // cannot append the same expression twice
319     QCOMPARE(n.argCount(), 1);
320     QCOMPARE(n.arg(0).toConst(), c1);
321 
322     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
323     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 3);
324     n.prepend(c1);
325     n.prepend(c1); // cannot prepend the same expression twice
326     QCOMPARE(n.argCount(), 1);
327     QCOMPARE(n.arg(0).toConst(), c1);
328     n.append(c1); // cannot append/prepend the same expression twice
329     QCOMPARE(n.argCount(), 1);
330     QCOMPARE(n.arg(0).toConst(), c1);
331 
332     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
333     n2 = KDbNArgExpression(KDb::ArithmeticExpression, '+');
334     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 4);
335     n.append(c1);
336     n2.append(c1); // c moves from n to n2
337     QVERIFY(n.isEmpty());
338     QCOMPARE(n2.argCount(), 1);
339     QCOMPARE(c1.parent().toNArg(), n2);
340     n.prepend(c1); // c moves from n2 to n
341     QCOMPARE(n.argCount(), 1);
342     QVERIFY(n2.isEmpty());
343     QCOMPARE(c1.parent().toNArg(), n);
344 
345     // -- insert(int, KDbExpression)
346     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
347     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 3);
348     c2 = KDbConstExpression(KDbToken::INTEGER_CONST, 4);
349     // it must be a valid index position in the list (i.e., 0 <= i < argCount()).
350     n.insert(-10, c1);
351     QVERIFY(n.isEmpty());
352     n.insert(1, c1);
353     QVERIFY(n.isEmpty());
354     // if i is 0, the expression is prepended to the list of arguments
355     n.insert(0, c1);
356     QCOMPARE(n.arg(0).toConst(), c1);
357     QCOMPARE(n.argCount(), 1);
358     QCOMPARE(c1.parent().toNArg(), n);
359     n.insert(0, c2);
360     QCOMPARE(n.argCount(), 2);
361     QCOMPARE(n.arg(0).toConst(), c2);
362     QCOMPARE(n.arg(1).toConst(), c1);
363 
364     // if i is argCount(), the value is appended to the list of arguments
365     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
366     n.insert(0, c1);
367     n.insert(1, c2);
368     QCOMPARE(n.argCount(), 2);
369     QCOMPARE(n.arg(0).toConst(), c1);
370     QCOMPARE(n.arg(1).toConst(), c2);
371 
372     // expression cannot be own child
373     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
374     n.insert(0, n);
375     QVERIFY(n.isEmpty());
376 
377     // cannot insert child twice
378     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
379     n.insert(0, c1);
380     n.insert(1, c1);
381     QCOMPARE(n.argCount(), 1);
382 
383     // -- remove(KDbExpression)
384     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
385     n.append(c1);
386     n.append(c2);
387     n.remove(c1); // remove first
388     QCOMPARE(n.argCount(), 1);
389     QCOMPARE(n.arg(0).toConst(), c2);
390 
391     // -- remove(KDbExpression)
392     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
393     n.prepend(c1);
394     n.append(c2);
395     c3 = KDbConstExpression(KDbToken::INTEGER_CONST, 5);
396     QVERIFY(!n.remove(c3)); // not found
397     QCOMPARE(n.argCount(), 2);
398     n.append(c3);
399     QCOMPARE(n.argCount(), 3);
400     QVERIFY(n.remove(c2)); // remove 2nd of 3, leaves c1 and c3
401     QCOMPARE(n.argCount(), 2);
402     QCOMPARE(n.arg(0).toConst(), c1);
403     QCOMPARE(n.arg(1).toConst(), c3);
404 
405     // -- removeAt(int)
406     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
407     n.prepend(c1);
408     n.append(c2);
409     n.removeAt(-1); // not found
410     QCOMPARE(n.argCount(), 2);
411     n.removeAt(3); // not found
412     QCOMPARE(n.argCount(), 2);
413     n.append(c3);
414     n.removeAt(1); // remove 2nd of 3, leaves c1 and c3
415     QCOMPARE(n.argCount(), 2);
416     QCOMPARE(n.arg(0).toConst(), c1);
417     QCOMPARE(n.arg(1).toConst(), c3);
418     n.removeAt(0);
419     QCOMPARE(n.argCount(), 1);
420     n.removeAt(0);
421     QCOMPARE(n.argCount(), 0);
422 
423     // -- takeAt(int)
424     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
425     n2 = n;
426     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 1);
427     c2 = KDbConstExpression(KDbToken::INTEGER_CONST, 2);
428     c3 = KDbConstExpression(KDbToken::INTEGER_CONST, 3);
429     n.append(c1);
430     n.append(c2);
431     n.append(c3);
432     n.takeAt(-1); // not found
433     QCOMPARE(n.argCount(), 3);
434     n.takeAt(3); // not found
435     QCOMPARE(n.argCount(), 3);
436     e = n.takeAt(1);
437     QCOMPARE(e.toConst(), c2); // e is 2nd
438     QCOMPARE(n.argCount(), 2); // 1 arg taken
439     QCOMPARE(n, n2);
440 
441     // -- indexOf(KDbExpression, int)
442     n = KDbNArgExpression(KDb::ArithmeticExpression, '+');
443     c = KDbConstExpression(KDbToken::INTEGER_CONST, 0);
444     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 1);
445     c2 = KDbConstExpression(KDbToken::INTEGER_CONST, 2);
446     c3 = KDbConstExpression(KDbToken::INTEGER_CONST, 3);
447     n.append(c1);
448     n.append(c2);
449     n.append(c3);
450 
451     QCOMPARE(n.indexOf(c), -1);
452     QCOMPARE(n.indexOf(c1), 0);
453     QCOMPARE(n.indexOf(c2), 1);
454     QCOMPARE(n.indexOf(c3), 2);
455     QCOMPARE(n.indexOf(c1, 1), -1);
456     QCOMPARE(n.indexOf(c2, 1), 1);
457 
458     // -- lastIndexOf(KDbExpression, int)
459     QCOMPARE(n.lastIndexOf(c), -1);
460     QCOMPARE(n.lastIndexOf(c1), 0);
461     QCOMPARE(n.lastIndexOf(c2), 1);
462     QCOMPARE(n.lastIndexOf(c3), 2);
463     QCOMPARE(n.lastIndexOf(c1, 1), 0);
464     QCOMPARE(n.lastIndexOf(c2, 0), -1);
465 
466     // -- a list of arguments
467     n = KDbNArgExpression(KDb::ArgumentListExpression, ',');
468     n.append(KDbConstExpression(KDbToken::INTEGER_CONST, 1));
469     n.append(KDbConstExpression(KDbToken::INTEGER_CONST, 2));
470     n.append(KDbConstExpression(KDbToken::INTEGER_CONST, 3));
471     QCOMPARE(n.toString(nullptr), KDbEscapedString("1, 2, 3"));
472     QCOMPARE(n.argCount(), 3);
473     QVERIFY(!n.containsInvalidArgument());
474     QVERIFY(!n.containsNullArgument());
475 
476     // -- a list of arguments contains invalid argument
477     n = KDbNArgExpression(KDb::ArgumentListExpression, ',');
478     n.append(KDbConstExpression(KDbToken::INTEGER_CONST, 1));
479     n.append(KDbExpression());
480     n.append(KDbConstExpression(KDbToken::INTEGER_CONST, 3));
481     QVERIFY(n.containsInvalidArgument());
482     QVERIFY(!n.containsNullArgument());
483     QVERIFY(!n.isNull());
484     QCOMPARE(n.toString(nullptr), KDbEscapedString("1, <UNKNOWN!>, 3"));
485 
486     // -- a list of arguments contains null argument
487     n = KDbNArgExpression(KDb::ArgumentListExpression, ',');
488     n.append(KDbConstExpression(KDbToken::INTEGER_CONST, 1));
489     n.append(KDbConstExpression(KDbToken::SQL_NULL, QVariant()));
490     n.prepend(KDbConstExpression(KDbToken::INTEGER_CONST, 0));
491     QVERIFY(!n.containsInvalidArgument());
492     QVERIFY(n.containsNullArgument());
493     QCOMPARE(n.toString(nullptr), KDbEscapedString("0, 1, NULL"));
494 }
495 
testUnaryExpression()496 void ExpressionsTest::testUnaryExpression()
497 {
498     KDbUnaryExpression u;
499     KDbUnaryExpression u2;
500     KDbConstExpression c;
501     KDbConstExpression c1;
502 
503     // -- empty
504     KDbUnaryExpression emptyUnary;
505     QVERIFY(emptyUnary.isUnary());
506     QVERIFY(emptyUnary.clone().isUnary());
507     QVERIFY(emptyUnary.arg().isNull());
508 
509     u = KDbUnaryExpression('-', KDbExpression());
510     QVERIFY(u.arg().isNull());
511 
512     // -- copy ctor & cloning
513     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 7);
514     u = KDbUnaryExpression('-', c1);
515     testCloneExpression(u);
516     QCOMPARE(u.token().name(), QString("-"));
517     QCOMPARE(u.toString(nullptr), KDbEscapedString("-7"));
518     QCOMPARE(c1, u.arg().toConst());
519 
520     u2 = KDbUnaryExpression('-', u);
521     testCloneExpression(u);
522     QCOMPARE(u2.token().name(), QString("-"));
523     QCOMPARE(u2.toString(nullptr), KDbEscapedString("--7"));
524     QCOMPARE(u, u2.arg().toUnary());
525 
526     u = KDbUnaryExpression('(', c1);
527     testCloneExpression(u);
528     QCOMPARE(u.toString(nullptr), KDbEscapedString("(7)"));
529     QCOMPARE(c1, u.arg().toConst());
530 
531     c1 = KDbConstExpression(KDbToken::SQL_TRUE, true);
532     u = KDbUnaryExpression(KDbToken::NOT, c1);
533     testCloneExpression(u);
534     QCOMPARE(u.toString(nullptr), KDbEscapedString("NOT TRUE"));
535     QCOMPARE(c1, u.arg().toConst());
536 
537     c1 = KDbConstExpression(KDbToken::SQL_NULL, QVariant());
538     u = KDbUnaryExpression(KDbToken::NOT, c1);
539     testCloneExpression(u);
540     QCOMPARE(u.toString(nullptr), KDbEscapedString("NOT NULL"));
541     QCOMPARE(c1, u.arg().toConst());
542 
543     c1 = KDbConstExpression(KDbToken::SQL_NULL, QVariant());
544     u = KDbUnaryExpression(KDbToken::SQL_IS_NULL, c1);
545     testCloneExpression(u);
546     QCOMPARE(u.toString(nullptr), KDbEscapedString("NULL IS NULL"));
547     QCOMPARE(c1, u.arg().toConst());
548 
549     c1 = KDbConstExpression(KDbToken::SQL_NULL, QVariant());
550     u = KDbUnaryExpression(KDbToken::SQL_IS_NOT_NULL, c1);
551     testCloneExpression(u);
552     QCOMPARE(u.toString(nullptr), KDbEscapedString("NULL IS NOT NULL"));
553     QCOMPARE(c1, u.arg().toConst());
554 
555     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 17);
556     u = KDbUnaryExpression(KDbToken::SQL, c1);
557     testCloneExpression(u);
558     QCOMPARE(u.toString(nullptr), KDbEscapedString("SQL 17"));
559     QCOMPARE(c1, u.arg().toConst());
560 
561     // -- exchanging arg between two unary expressions
562     c = KDbConstExpression(KDbToken::INTEGER_CONST, 17);
563     u = KDbUnaryExpression('-', c);
564     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 3);
565     u2 = KDbUnaryExpression('+', c1);
566     u2.setArg(c); // this should take c arg from u to u2
567     QCOMPARE(c, u2.arg().toConst()); // c is now in u2
568     QVERIFY(u.arg().isNull()); // u has null arg now
569 
570     c = KDbConstExpression(KDbToken::INTEGER_CONST, 17);
571     u = KDbUnaryExpression('-', c);
572     u2 = KDbUnaryExpression('+', c);
573     // u2 takes c arg from u
574     QCOMPARE(c, u2.arg().toConst()); // c is now in u2
575     QVERIFY(u.arg().isNull()); // u has null arg now
576 
577     // -- cycles
578     c = KDbConstExpression(KDbToken::INTEGER_CONST, 17);
579     u = KDbUnaryExpression('-', c);
580     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 3);
581     u2 = KDbUnaryExpression('+', c1);
582     u2.setArg(u);
583     u.setArg(u2);
584 
585     QTest::ignoreMessage(QtWarningMsg, R"w(Cycle detected in expression (depth 2):
586 1: Unary -
587 2: Unary +)w");
588     QCOMPARE(u.toString(nullptr), KDbEscapedString("-+<CYCLE!>"));
589 
590     QTest::ignoreMessage(QtWarningMsg, R"w(Cycle detected in expression (depth 2):
591 1: Unary +
592 2: Unary -)w");
593     QCOMPARE(u2.toString(nullptr), KDbEscapedString("+-<CYCLE!>"));
594 }
595 
testBinaryExpression()596 void ExpressionsTest::testBinaryExpression()
597 {
598     KDbBinaryExpression b;
599     KDbBinaryExpression b2;
600     KDbConstExpression c;
601     KDbConstExpression c1;
602 
603     // -- empty
604     KDbBinaryExpression emptyBinary;
605     QVERIFY(emptyBinary.isNull());
606     QVERIFY(emptyBinary.isBinary());
607     QVERIFY(emptyBinary.clone().isBinary());
608     QVERIFY(emptyBinary.left().isNull());
609     QVERIFY(emptyBinary.right().isNull());
610 
611     QTest::ignoreMessage(QtWarningMsg,
612                          "Setting KDbBinaryExpression to null because left argument is not specified");
613     b = KDbBinaryExpression(KDbExpression(), '-', KDbExpression());
614     QVERIFY(b.left().isNull());
615     QVERIFY(b.right().isNull());
616     QVERIFY(b.isNull()); // it's null because args are null
617     //qDebug() << b.toString(nullptr);
618     QCOMPARE(b.toString(nullptr), KDbEscapedString("<UNKNOWN!>"));
619     c = KDbConstExpression(KDbToken::INTEGER_CONST, 10);
620     QTest::ignoreMessage(QtWarningMsg,
621                          "Setting KDbBinaryExpression to null because right argument is not specified");
622     b = KDbBinaryExpression(c, '-', KDbExpression());
623     QVERIFY(b.left().isNull());
624     QVERIFY(b.right().isNull());
625     QVERIFY(b.isNull()); // it's null because one arg is null
626     //qDebug() << b.toString(nullptr);
627     QCOMPARE(b.toString(nullptr), KDbEscapedString("<UNKNOWN!>"));
628     QTest::ignoreMessage(QtWarningMsg,
629                          "Setting KDbBinaryExpression to null because left argument is not specified");
630     b = KDbBinaryExpression(KDbExpression(), '-', c);
631     QVERIFY(b.left().isNull());
632     QVERIFY(b.right().isNull());
633     QVERIFY(b.isNull()); // it's null because one arg is null
634     //qDebug() << b.toString(nullptr);
635     QCOMPARE(b.toString(nullptr), KDbEscapedString("<UNKNOWN!>"));
636 
637     // -- copy ctor & cloning
638     c = KDbConstExpression(KDbToken::INTEGER_CONST, 3);
639     c1 = KDbConstExpression(KDbToken::INTEGER_CONST, 4);
640     b = KDbBinaryExpression(c, '/', c1);
641     testCloneExpression(b);
642     QCOMPARE(b.token().name(), QString("/"));
643     QCOMPARE(b.toString(nullptr), KDbEscapedString("3 / 4"));
644     QCOMPARE(c1, b.right().toConst());
645 
646     b2 = KDbBinaryExpression(b, '*', b.clone());
647     testCloneExpression(b2);
648     QCOMPARE(b2.token().name(), QString("*"));
649     QCOMPARE(b2.toString(nullptr), KDbEscapedString("3 / 4 * 3 / 4"));
650     QCOMPARE(b, b2.left().toBinary());
651 
652     // -- cycles
653     // --- ref to parent
654     b = KDbBinaryExpression(
655             KDbConstExpression(KDbToken::INTEGER_CONST, 1), '+', KDbConstExpression(KDbToken::INTEGER_CONST, 2));
656     KDbEscapedString s = b.toString(nullptr);
657     QTest::ignoreMessage(QtWarningMsg,
658                          QRegularExpression("Expression BinaryExp(.*) cannot be set as own child"));
659     b.setLeft(b); // should not work
660     //qDebug() << b.toString(nullptr);
661     QCOMPARE(s, b.toString(nullptr));
662     // --- cannot set twice
663     c = b.left().toConst();
664     b.setLeft(c);
665     QCOMPARE(s, b.toString(nullptr));
666     // --- ref to grandparent
667     b = KDbBinaryExpression(
668             KDbConstExpression(KDbToken::INTEGER_CONST, 1), '+', KDbConstExpression(KDbToken::INTEGER_CONST, 2));
669     c = KDbConstExpression(KDbToken::INTEGER_CONST, 10);
670     b2 = KDbBinaryExpression(b, '-', c);
671     //qDebug() << b2.toString(nullptr);
672     QCOMPARE(b2.toString(nullptr), KDbEscapedString("1 + 2 - 10"));
673     QTest::ignoreMessage(QtWarningMsg, R"w(Cycle detected in expression (depth 2):
674 1: Arithm -
675 2: Arithm +)w");
676     b.setRight(b2);
677     //qDebug() << b2.toString(nullptr);
678     QCOMPARE(b2.toString(nullptr), KDbEscapedString("1 + <CYCLE!> - 10"));
679 
680     // -- moving right argument to left should remove right arg
681     b = KDbBinaryExpression(
682             KDbConstExpression(KDbToken::INTEGER_CONST, 1), '+', KDbConstExpression(KDbToken::INTEGER_CONST, 2));
683     c = b.right().toConst();
684     b.setLeft(c);
685     //qDebug() << b.toString(nullptr);
686     QCOMPARE(b.toString(nullptr), KDbEscapedString("2 + <UNKNOWN!>"));
687 
688     // -- moving left argument to right should remove left arg
689     b = KDbBinaryExpression(
690             KDbConstExpression(KDbToken::INTEGER_CONST, 1), '+', KDbConstExpression(KDbToken::INTEGER_CONST, 2));
691     c = b.left().toConst();
692     b.setRight(c);
693     //qDebug() << b.toString(nullptr);
694     QCOMPARE(b.toString(nullptr), KDbEscapedString("<UNKNOWN!> + 1"));
695 }
696 
testBinaryExpressionCloning_data()697 void ExpressionsTest::testBinaryExpressionCloning_data()
698 {
699     QTest::addColumn<KDbToken>("type1");
700     QTest::addColumn<QVariant>("const1");
701     QTest::addColumn<KDbToken>("token");
702     QTest::addColumn<KDbToken>("type2");
703     QTest::addColumn<QVariant>("const2");
704     QTest::addColumn<QString>("string");
705 
706 #define T(type1, const1, token, type2, const2, string) \
707         QTest::newRow(qPrintable(TO_TOKEN(token).name())) \
708             << type1 << QVariant(const1) << TO_TOKEN(token) \
709             << type2 << QVariant(const2) << QString(string)
710 
711     T(KDbToken::INTEGER_CONST, 3, '/', KDbToken::INTEGER_CONST, 4, "3 / 4");
712     T(KDbToken::INTEGER_CONST, 3, KDbToken::BITWISE_SHIFT_RIGHT, KDbToken::INTEGER_CONST, 4, "3 >> 4");
713     T(KDbToken::INTEGER_CONST, 3, KDbToken::BITWISE_SHIFT_LEFT, KDbToken::INTEGER_CONST, 4, "3 << 4");
714     T(KDbToken::INTEGER_CONST, 3, KDbToken::NOT_EQUAL, KDbToken::INTEGER_CONST, 4, "3 <> 4");
715     T(KDbToken::INTEGER_CONST, 3, KDbToken::NOT_EQUAL2, KDbToken::INTEGER_CONST, 4, "3 != 4");
716     T(KDbToken::INTEGER_CONST, 3, KDbToken::LESS_OR_EQUAL, KDbToken::INTEGER_CONST, 4, "3 <= 4");
717     T(KDbToken::INTEGER_CONST, 3, KDbToken::GREATER_OR_EQUAL, KDbToken::INTEGER_CONST, 4, "3 >= 4");
718     T(KDbToken::CHARACTER_STRING_LITERAL, "ABC", KDbToken::LIKE, KDbToken::CHARACTER_STRING_LITERAL, "A%", "'ABC' LIKE 'A%'");
719     T(KDbToken::INTEGER_CONST, 3, KDbToken::SQL_IN, KDbToken::INTEGER_CONST, 4, "3 IN 4");
720     T(KDbToken::INTEGER_CONST, 3, KDbToken::SIMILAR_TO, KDbToken::INTEGER_CONST, 4, "3 SIMILAR TO 4");
721     T(KDbToken::INTEGER_CONST, 3, KDbToken::NOT_SIMILAR_TO, KDbToken::INTEGER_CONST, 4, "3 NOT SIMILAR TO 4");
722     T(KDbToken::SQL_TRUE, true, KDbToken::OR, KDbToken::SQL_FALSE, false, "TRUE OR FALSE");
723     T(KDbToken::INTEGER_CONST, 3, KDbToken::AND, KDbToken::INTEGER_CONST, 4, "3 AND 4");
724     T(KDbToken::INTEGER_CONST, 3, KDbToken::XOR, KDbToken::INTEGER_CONST, 4, "3 XOR 4");
725     T(KDbToken::CHARACTER_STRING_LITERAL, "AB", KDbToken::CONCATENATION, KDbToken::CHARACTER_STRING_LITERAL, "CD", "'AB' || 'CD'");
726     T(KDbToken::CHARACTER_STRING_LITERAL, "AB", '+', KDbToken::CHARACTER_STRING_LITERAL, "CD", "'AB' + 'CD'");
727 #undef T
728 }
729 
testBinaryExpressionCloning()730 void ExpressionsTest::testBinaryExpressionCloning()
731 {
732     QFETCH(KDbToken, type1);
733     QFETCH(QVariant, const1);
734     QFETCH(KDbToken, token);
735     QFETCH(KDbToken, type2);
736     QFETCH(QVariant, const2);
737     QFETCH(QString, string);
738 
739     KDbConstExpression c(type1, const1);
740     KDbConstExpression c1(type2, const2);
741     KDbBinaryExpression b(c, token, c1);
742     testCloneExpression(b);
743     QCOMPARE(b.token(), token);
744     QCOMPARE(b.token().name(), token.name());
745     //qDebug() << token << b;
746     QCOMPARE(b.toString(nullptr), KDbEscapedString(string));
747     QCOMPARE(c, b.left().toConst());
748     QCOMPARE(c1, b.right().toConst());
749 }
750 
testFunctionExpression()751 void ExpressionsTest::testFunctionExpression()
752 {
753     KDbFunctionExpression emptyFunction;
754     QVERIFY(emptyFunction.isFunction());
755     QVERIFY(emptyFunction.clone().isFunction());
756     QVERIFY(emptyFunction.arguments().isEmpty());
757     QVERIFY(emptyFunction.isNull());
758 
759     KDbNArgExpression args;
760     args.append(KDbConstExpression(KDbToken::CHARACTER_STRING_LITERAL, "abc"));
761     args.append(KDbConstExpression(KDbToken::INTEGER_CONST, 2));
762     KDbFunctionExpression f_substr("SUBSTR", args);
763     //qDebug() << f_substr.toString();
764     //qDebug() << f_substr.token().name();
765     //qDebug() << f_substr.token().toString();
766 
767     testCloneExpression(f_substr);
768     QCOMPARE(f_substr.type(), KDbField::Text);
769 
770     args.append(KDbConstExpression(KDbToken::INTEGER_CONST, 1));
771     KDbFunctionExpression f_substr2("SUBSTR", args);
772     testCloneExpression(f_substr2);
773     QCOMPARE(f_substr2.type(), KDbField::Text);
774     //qDebug() << f_substr.toString();
775     //qDebug() << f_substr2.toString();
776     QVERIFY(f_substr != f_substr2); // other objects
777     QCOMPARE(f_substr.toString(nullptr), f_substr2.toString(nullptr)); // the same signatures
778     QCOMPARE(f_substr.arguments(), f_substr2.arguments()); // the same arg lists
779 
780     // clone the args
781     KDbNArgExpression args2 = args.clone().toNArg();
782     //qDebug() << f_substr2;
783     f_substr2.setArguments(args2);
784     //qDebug() << f_substr2;
785     QCOMPARE(f_substr.toString(nullptr), f_substr2.toString(nullptr)); // still the same signatures
786     QVERIFY(f_substr.arguments() != f_substr2.arguments()); // not the same arg lists
787 
788     KDbExpression e = f_substr;
789     QCOMPARE(e.toFunction(), f_substr);
790     QCOMPARE(e.toFunction(), f_substr.toFunction());
791     QVERIFY(e.isFunction());
792 
793     // nested functions
794     f_substr2.arguments().replace(0, f_substr);
795     QCOMPARE(f_substr2.type(), KDbField::Text);
796 }
797 
testConstExpressionValidate()798 void ExpressionsTest::testConstExpressionValidate()
799 {
800     KDbConstExpression c;
801 
802     c = KDbConstExpression(KDbToken::SQL_NULL, QVariant());
803     QCOMPARE(c.type(), KDbField::Null);
804     QVERIFY(c.isValid());
805     QVERIFY(!c.isNull());
806     QVERIFY(validate(&c));
807 
808     // null
809     c = KDbConstExpression(KDbToken::SQL_NULL, QVariant());
810     QCOMPARE(c.type(), KDbField::Null);
811     QVERIFY(validate(&c));
812     testCloneExpression(c);
813     //qDebug() << c;
814 
815     // integer
816     c = KDbConstExpression(KDbToken::INTEGER_CONST, -0x7f);
817     QCOMPARE(c.type(), KDbField::Byte);
818     QVERIFY(c.isValid());
819     QVERIFY(c.isNumericType());
820     QVERIFY(!c.isNull());
821     QCOMPARE(c.value(), QVariant(-0x7f));
822     QVERIFY(validate(&c));
823     testCloneExpression(c);
824     c.setValue(-0x80);
825     QCOMPARE(c.type(), KDbField::ShortInteger); // type has been changed by setValue
826     QCOMPARE(c.value(), QVariant(-0x80));
827     QVERIFY(validate(&c));
828     testCloneExpression(c);
829     //qDebug() << c;
830 
831     c = KDbConstExpression(KDbToken::INTEGER_CONST, -10);
832     QCOMPARE(c.type(), KDbField::Byte);
833     QVERIFY(c.isValid());
834     QVERIFY(c.isNumericType());
835     QCOMPARE(c.value(), QVariant(-10));
836     QVERIFY(validate(&c));
837     testCloneExpression(c);
838     //qDebug() << c;
839 
840     c = KDbConstExpression(KDbToken::INTEGER_CONST, 0);
841     QCOMPARE(c.type(), KDbField::Byte);
842     QVERIFY(c.isValid());
843     QVERIFY(c.isNumericType());
844     QCOMPARE(c.value(), QVariant(0));
845     QVERIFY(validate(&c));
846     testCloneExpression(c);
847     //qDebug() << c;
848 
849     c = KDbConstExpression(KDbToken::INTEGER_CONST, 20);
850     QCOMPARE(c.type(), KDbField::Byte);
851     QVERIFY(c.isValid());
852     QVERIFY(c.isNumericType());
853     QCOMPARE(c.value(), QVariant(20));
854     QVERIFY(validate(&c));
855     testCloneExpression(c);
856     //qDebug() << c;
857 
858     c = KDbConstExpression(KDbToken::INTEGER_CONST, 255);
859     QCOMPARE(c.type(), KDbField::Byte);
860     QVERIFY(c.isValid());
861     QVERIFY(c.isNumericType());
862     QCOMPARE(c.value(), QVariant(255));
863     QVERIFY(validate(&c));
864     testCloneExpression(c);
865     //qDebug() << c;
866 
867     c = KDbConstExpression(KDbToken::INTEGER_CONST, -0x80);
868     QCOMPARE(c.type(), KDbField::ShortInteger);
869     QVERIFY(c.isValid());
870     QVERIFY(c.isNumericType());
871     QCOMPARE(c.value(), QVariant(-0x80));
872     QVERIFY(validate(&c));
873     testCloneExpression(c);
874     //qDebug() << c;
875 
876     c = KDbConstExpression(KDbToken::INTEGER_CONST, -0x7fff);
877     QCOMPARE(c.type(), KDbField::ShortInteger);
878     QVERIFY(c.isValid());
879     QVERIFY(c.isNumericType());
880     QCOMPARE(c.value(), QVariant(-0x7fff));
881     QVERIFY(validate(&c));
882     testCloneExpression(c);
883     //qDebug() << c;
884 
885     c = KDbConstExpression(KDbToken::INTEGER_CONST, 256);
886     QCOMPARE(c.type(), KDbField::ShortInteger);
887     QVERIFY(c.isValid());
888     QVERIFY(c.isNumericType());
889     QCOMPARE(c.value(), QVariant(256));
890     QVERIFY(validate(&c));
891     testCloneExpression(c);
892     //qDebug() << c;
893 
894     c = KDbConstExpression(KDbToken::INTEGER_CONST, 0xffff);
895     QCOMPARE(c.type(), KDbField::ShortInteger);
896     QVERIFY(c.isValid());
897     QVERIFY(c.isNumericType());
898     QCOMPARE(c.value(), QVariant(0xffff));
899     QVERIFY(validate(&c));
900     testCloneExpression(c);
901     //qDebug() << c;
902 
903     c = KDbConstExpression(KDbToken::INTEGER_CONST, -0x8000);
904     QCOMPARE(c.type(), KDbField::Integer);
905     QVERIFY(c.isValid());
906     QVERIFY(c.isNumericType());
907     QCOMPARE(c.value(), QVariant(-0x8000));
908     QVERIFY(validate(&c));
909     testCloneExpression(c);
910     //qDebug() << c;
911 
912     c = KDbConstExpression(KDbToken::INTEGER_CONST, uint(0x10000));
913     QCOMPARE(c.type(), KDbField::Integer);
914     QVERIFY(c.isValid());
915     QVERIFY(c.isNumericType());
916     QCOMPARE(c.value(), QVariant(0x10000));
917     QVERIFY(validate(&c));
918     testCloneExpression(c);
919     //qDebug() << c;
920 
921     c = KDbConstExpression(KDbToken::INTEGER_CONST, qlonglong(-0x100000));
922     QCOMPARE(c.type(), KDbField::BigInteger);
923     QVERIFY(c.isValid());
924     QVERIFY(c.isNumericType());
925     QCOMPARE(c.value(), QVariant(-0x100000));
926     QVERIFY(validate(&c));
927     testCloneExpression(c);
928     //qDebug() << c;
929 
930     c = KDbConstExpression(KDbToken::INTEGER_CONST, qulonglong(0x1000000));
931     QCOMPARE(c.type(), KDbField::BigInteger);
932     QVERIFY(c.isValid());
933     QVERIFY(c.isNumericType());
934     QCOMPARE(c.value(), QVariant(0x1000000));
935     QVERIFY(validate(&c));
936     testCloneExpression(c);
937     //qDebug() << c;
938 
939     // string
940     int oldMaxLen = KDbField::defaultMaxLength(); // save
941     KDbField::setDefaultMaxLength(0);
942     c = KDbConstExpression(KDbToken::CHARACTER_STRING_LITERAL, "01234567890");
943     QVERIFY(c.isValid());
944     QVERIFY(c.isTextType());
945     QCOMPARE(c.type(), KDbField::Text);
946     QCOMPARE(c.value(), QVariant("01234567890"));
947     QVERIFY(validate(&c));
948     testCloneExpression(c);
949     //qDebug() << c;
950 
951     KDbField::setDefaultMaxLength(10);
952     c = KDbConstExpression(KDbToken::CHARACTER_STRING_LITERAL, QString());
953     QCOMPARE(c.type(), KDbField::Text);
954     QVERIFY(c.isValid());
955     QVERIFY(c.isTextType());
956     QCOMPARE(c.value(), QVariant(QString()));
957     QVERIFY(validate(&c));
958     testCloneExpression(c);
959     //qDebug() << c;
960 
961     c = KDbConstExpression(KDbToken::CHARACTER_STRING_LITERAL, QVariant());
962     QCOMPARE(c.type(), KDbField::Text);
963     QVERIFY(c.isValid());
964     QVERIFY(c.isTextType());
965     QCOMPARE(c.value(), QVariant());
966     QVERIFY(validate(&c));
967     testCloneExpression(c);
968     //qDebug() << c;
969 
970     c = KDbConstExpression(KDbToken::CHARACTER_STRING_LITERAL, "01234567890");
971     QCOMPARE(c.type(), KDbField::LongText);
972     QVERIFY(c.isValid());
973     QVERIFY(c.isTextType());
974     QCOMPARE(c.value(), QVariant("01234567890"));
975     QVERIFY(validate(&c));
976     //qDebug() << c;
977     c.setValue("ąćę");
978     QCOMPARE(c.value(), QVariant("ąćę"));
979     QCOMPARE(c.type(), KDbField::Text);
980     QVERIFY(validate(&c));
981     testCloneExpression(c);
982     //qDebug() << c;
983 
984     KDbField::setDefaultMaxLength(oldMaxLen); // restore
985 
986     // bool
987     c = KDbConstExpression(KDbToken::SQL_TRUE, true);
988     QCOMPARE(c.type(), KDbField::Boolean);
989     QVERIFY(c.isValid());
990     QVERIFY(!c.isTextType());
991     QVERIFY(!c.isNumericType());
992     QCOMPARE(c.value(), QVariant(true));
993     QVERIFY(validate(&c));
994     //qDebug() << c;
995     c.setValue(false);
996     QCOMPARE(c.value(), QVariant(false));
997     QVERIFY(validate(&c));
998     testCloneExpression(c);
999     //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 
testUnaryExpressionValidate()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 
testNArgExpressionValidate()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 
testBinaryExpressionValidate_data()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 
testBinaryExpressionValidate()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 
testFunctionExpressionValidate()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 
cleanupTestCase()1498 void ExpressionsTest::cleanupTestCase()
1499 {
1500 }
1501