KDb

KDbBinaryExpression.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2003-2016 Jarosław Staniek <staniek@kde.org>
3
4 Based on nexp.cpp : Parser module of Python-like language
5 (C) 2001 Jarosław Staniek, MIMUW (www.mimuw.edu.pl)
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
16
17 You should have received a copy of the GNU Library General Public License
18 along with this library; see the file COPYING.LIB. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include "KDbExpression.h"
24#include "KDb.h"
25#include "KDbQuerySchema.h"
26#include "KDbDriver.h"
27#include "KDbParser_p.h"
28#include "kdb_debug.h"
29#include "generated/sqlparser.h"
30
31KDbBinaryExpressionData::KDbBinaryExpressionData()
33{
34 ExpressionDebug << "BinaryExpressionData" << ref;
35}
36
37KDbBinaryExpressionData::~KDbBinaryExpressionData()
38{
39}
40
42{
43 if (children.count() != 2)
44 return false;
45
46 if (!left()->validate(parseInfo, callStack))
47 return false;
48 if (!right()->validate(parseInfo, callStack))
49 return false;
50
51 //update type for query parameters
52//! @todo IMPORTANT: update type for query parameters
53#if 0
54 if (left()->isQueryParameter()) {
55 KDbQueryParameterExpression queryParameter = left()->toQueryParameter();
56 queryParameter->setType(left()->type());
57 }
58 if (right()->isQueryParameter()) {
59 KDbQueryParameterExpression queryParameter = right()->toQueryParameter();
60 queryParameter->setType(right()->type());
61 }
62#endif
63 if (typeInternal(callStack) == KDbField::InvalidType) {
64 parseInfo->setErrorMessage(tr("Incompatible types of arguments"));
65 parseInfo->setErrorDescription(
66 tr("Expression \"%1\" requires compatible types of arguments. "
67 "Specified arguments are of type %2 and %3.",
68 "Binary expression arguments type error")
69 .arg(toStringInternal(nullptr, nullptr, callStack).toString(),
70 KDbField::typeName(left()->type()),
71 KDbField::typeName(right()->type())));
72 return false;
73 }
74 return true;
75}
76
78{
79 if (children.count() != 2 || expressionClass == KDb::UnknownExpression)
81 const KDbField::Type lt = left()->type(callStack);
82 const KDbField::Type rt = right()->type(callStack);
85
86 const bool ltNull = lt == KDbField::Null;
87 const bool rtNull = rt == KDbField::Null;
88 const bool ltText = KDbField::isTextType(lt);
89 const bool rtText = KDbField::isTextType(rt);
90 const bool ltInt = KDbField::isIntegerType(lt);
91 const bool rtInt = KDbField::isIntegerType(rt);
92 const bool ltFP = KDbField::isFPNumericType(lt);
93 const bool rtFP = KDbField::isFPNumericType(rt);
94 const bool ltBool = lt == KDbField::Boolean;
95 const bool rtBool = rt == KDbField::Boolean;
96 const KDbField::TypeGroup ltGroup = KDbField::typeGroup(lt);
97 const KDbField::TypeGroup rtGroup = KDbField::typeGroup(rt);
98 const bool lAny = left()->convertConst<KDbQueryParameterExpressionData>();
99 const bool rAny = right()->convertConst<KDbQueryParameterExpressionData>();
100
101 if (ltNull || rtNull) {
102 switch (token.value()) {
103 //! @todo add general support, e.g. for "NULL AND (1 == 1)"; for now we only support
104 //! constants because there's no evaluation and operations with NULL depend on whether we have TRUE or FALSE
105 //! See https://www.postgresql.org/docs/9.4/static/functions-logical.html
106 //! https://dev.mysql.com/doc/refman/5.0/en/logical-operators.html
107 case OR: {
108 const KDbConstExpressionData *leftConst = left()->convertConst<KDbConstExpressionData>();
109 const KDbConstExpressionData *rightConst = right()->convertConst<KDbConstExpressionData>();
110 if ((ltBool && leftConst && leftConst->value.toBool()) // true OR NULL is true
111 || (rtBool && rightConst && rightConst->value.toBool())) // NULL OR true is true
112 {
113 return KDbField::Boolean;
114 }
115 else if ((ltBool && leftConst && !leftConst->value.toBool()) // false OR NULL is NULL
116 || (rtBool && rightConst && !rightConst->value.toBool()) // NULL OR false is NULL
117 || lAny // Any OR NULL may be NULL
118 || rAny) // NULL OR Any may be NULL
119 //! @todo Any OR NULL may be also TRUE -- but this needs support of fuzzy/multivalue types
120 //! @todo NULL OR Any may be also TRUE -- but this needs support of fuzzy/multivalue types
121 {
122 return KDbField::Null;
123 }
124 break;
125 }
126 case AND: {
127 const KDbConstExpressionData *leftConst = left()->convertConst<KDbConstExpressionData>();
128 const KDbConstExpressionData *rightConst = right()->convertConst<KDbConstExpressionData>();
129 if ((ltBool && leftConst && !leftConst->value.toBool()) // false AND NULL is false
130 || (rtBool && rightConst && !rightConst->value.toBool())) // NULL AND false is false
131 {
132 return KDbField::Boolean;
133 }
134 else if ((ltBool && leftConst && leftConst->value.toBool()) // true AND NULL is NULL
135 || (rtBool && rightConst && rightConst->value.toBool()) // NULL AND true is NULL
136 || lAny // Any AND NULL may be NULL
137 || rAny) // NULL AND Any may be NULL
138 //! @todo Any AND NULL may be also FALSE -- but this needs support of fuzzy/multivalue types
139 //! @todo NULL AND Any may be also FALSE -- but this needs support of fuzzy/multivalue types
140 {
141 return KDbField::Null;
142 }
143 break;
144 }
145 case XOR: {// Logical XOR. Returns NULL if either operand is NULL. For non-NULL operands,
146 // evaluates to 1 if an odd number of operands is nonzero, otherwise 0 is returned.
147 // a XOR b is mathematically equal to (a AND (NOT b)) OR ((NOT a) and b).
148 // https://dev.mysql.com/doc/refman/5.0/en/logical-operators.html#operator_xor
149 return KDbField::Null;
150 }
151 default:
152 return KDbField::Null;
153 }
154 }
155
156 switch (token.value()) {
157 case OR:
158 case AND:
159 case XOR: {
160 if (ltNull && rtNull) {
161 return KDbField::Null;
162 } else if ((ltBool && rtBool)
163 || (ltBool && rAny)
164 || (lAny && rtBool)
165 || (lAny && rAny))
166 {
167 return KDbField::Boolean;
168 }
170 }
171 case '+':
172 case CONCATENATION:
173 if (lt == KDbField::Text && rt == KDbField::Text) {
174 return KDbField::Text;
175 }
176 else if ((ltText && rtText)
177 || (ltText && rAny)
178 || (lAny && rtText))
179 {
180 return KDbField::LongText;
181 }
182 else if ((ltText && rtNull)
183 || (ltNull && rtText)
184 || (lAny && rtNull)
185 || (ltNull && rAny))
186 {
187 return KDbField::Null;
188 } else if (token.value() == CONCATENATION) {
189 if (lAny && rAny) {
190 return KDbField::LongText;
191 }
193 }
194 break; // '+' can still be handled below for non-text types
195 default:;
196 }
197
198 if (expressionClass == KDb::RelationalExpression) {
199 if ((ltText && rtText)
200 || (ltText && rAny)
201 || (lAny && rtText)
202 || (lAny && rAny)
203
204 || (ltInt && rtInt)
205 || (ltInt && rAny)
206 || (lAny && rtInt)
207
208 || (ltFP && rtFP) || (ltInt && rtFP) || (ltFP && rtInt)
209 || (ltFP && rAny) || (lAny && rtFP)
210
211 || (ltBool && rtBool) || (ltBool && rtInt) || (ltInt && rtBool)
212 || (ltBool && rAny) || (lAny && rtBool)
213
214 || (ltBool && rtFP) || (ltFP && rtBool)
215
216 || (ltGroup == KDbField::DateTimeGroup && rtGroup == KDbField::DateTimeGroup)
217 || (ltGroup == KDbField::DateTimeGroup && rAny)
218 || (lAny && rtGroup == KDbField::DateTimeGroup))
219 {
220 return KDbField::Boolean;
221 }
223 }
224
225 if (expressionClass == KDb::ArithmeticExpression) {
226 if (lAny && rAny) {
227 return KDbField::Integer;
228 } else if ((ltInt && rtInt)
229 || (ltInt && rAny)
230 || (lAny && rtInt))
231 {
232 /* From documentation of KDb::maximumForIntegerFieldTypes():
233 In case of KDb::ArithmeticExpression:
234 returned type may not fit to the result of evaluated expression that involves the arguments.
235 For example, 100 is within Byte type, maximumForIntegerFieldTypes(Byte, Byte) is Byte but result
236 of 100 * 100 exceeds the range of Byte.
237
238 Solution: for types smaller than Integer (e.g. Byte and ShortInteger) we are returning
239 Integer type.
240 */
242 if (lAny) {
243 t = rt;
244 } else if (rAny) {
245 t = lt;
246 } else {
248 }
249 if (t == KDbField::Byte || t == KDbField::ShortInteger) {
250 return KDbField::Integer;
251 }
252 return t;
253 }
254
255 switch (token.value()) {
256 case '&':
257 case BITWISE_SHIFT_RIGHT:
258 case BITWISE_SHIFT_LEFT:
259 if ((ltFP && rtFP)
260 || (ltFP && rAny) //! @todo can be other Integer too
261 || (lAny && rtFP)) //! @todo can be other Integer too
262 {
263 return KDbField::Integer;
264 }
265 else if ((ltFP && rtInt) // inherit from right
266 || (lAny && rtInt))
267 {
268 return rt;
269 }
270 else if ((ltInt && rtFP) // inherit from left
271 || (ltInt && rAny))
272 {
273 return lt;
274 }
275 break;
276 default:;
277 }
278
279 /* inherit floating point (Float or Double) type */
280 if (ltFP && (rtInt || lt == rt || rAny))
281 return lt;
282 if (rtFP && (ltInt || lt == rt || lAny))
283 return rt;
284 }
286}
287
288KDbBinaryExpressionData* KDbBinaryExpressionData::clone()
289{
290 ExpressionDebug << "BinaryExpressionData::clone" << *this;
291 return new KDbBinaryExpressionData(*this);
292}
293
295{
296 dbg.nospace() << "BinaryExp(class="
297 << expressionClassName(expressionClass)
298 << ",";
299 if (children.count() == 2 && left().constData()) {
300 left()->debug(dbg, callStack);
301 }
302 else {
303 dbg.nospace() << "<NONE>";
304 }
305 dbg.nospace() << "," << token << ",";
306 if (children.count() == 2 && right().constData()) {
307 right()->debug(dbg, callStack);
308 }
309 else {
310 dbg.nospace() << "<NONE>";
311 }
312 dbg.nospace() << ",type=" << KDbDriver::defaultSqlTypeName(type()) << ")";
313}
314
315KDbEscapedString KDbBinaryExpressionData::toStringInternal(
316 const KDbDriver *driver,
318 KDb::ExpressionCallStack* callStack) const
319{
320 switch (token.value()) {
321 case '+':
322 case CONCATENATION: {
323 if (driver && KDbField::isTextType(type())) {
324 const KDbBinaryExpression binaryExpr(const_cast<KDbBinaryExpressionData*>(this));
325 return driver->concatenateFunctionToString(binaryExpr, params, callStack);
326 }
327 break;
328 }
329 default:;
330 }
331
332#define INFIX(a) \
333 (left().constData() ? left()->toString(driver, params, callStack) : KDbEscapedString("<NULL>")) \
334 + " " + a + " " + (right().constData() ? right()->toString(driver, params, callStack) : KDbEscapedString("<NULL>"))
335 return INFIX(token.toString(driver));
336#undef INFIX
337}
338
339void KDbBinaryExpressionData::getQueryParameters(QList<KDbQuerySchemaParameter>* params)
340{
341 Q_ASSERT(params);
342 if (left().constData())
343 left()->getQueryParameters(params);
344 if (right().constData())
345 right()->getQueryParameters(params);
346}
347
348ExplicitlySharedExpressionDataPointer KDbBinaryExpressionData::left() const
349{
350 return (children.count() > 0) ? children.at(0) : ExplicitlySharedExpressionDataPointer();
351}
352ExplicitlySharedExpressionDataPointer KDbBinaryExpressionData::right() const
353{
354 return (children.count() > 1) ? children.at(1) : ExplicitlySharedExpressionDataPointer();
355}
356
357//=========================================
358
359static KDb::ExpressionClass classForArgs(const KDbExpression& leftExpr,
360 KDbToken token,
361 const KDbExpression& rightExpr)
362{
363 if (leftExpr.isNull()) {
364 kdbWarning() << "Setting KDbBinaryExpression to null because left argument is not specified";
365 return KDb::UnknownExpression;
366 }
367 if (rightExpr.isNull()) {
368 kdbWarning() << "Setting KDbBinaryExpression to null because right argument is not specified";
369 return KDb::UnknownExpression;
370 }
371 return KDbExpression::classForToken(token);
372}
373
376{
377 ExpressionDebug << "KDbBinaryExpression() ctor" << *this;
378}
379
381 KDbToken token,
382 const KDbExpression& rightExpr)
383 : KDbExpression(new KDbBinaryExpressionData, classForArgs(leftExpr, token, rightExpr), token)
384{
385 if (!isNull()) {
386 appendChild(leftExpr.d);
387 appendChild(rightExpr.d);
388 }
389}
390
395
397 : KDbExpression(data)
398{
399 ExpressionDebug << "KDbBinaryExpression(KDbExpressionData*) ctor" << *this;
400}
401
403 : KDbExpression(ptr)
404{
405}
406
407KDbBinaryExpression::~KDbBinaryExpression()
408{
409}
410
411KDbExpression KDbBinaryExpression::left() const
412{
413 return (d->children.count() > 0) ? KDbExpression(d->children.at(0)) : KDbExpression();
414}
415
416void KDbBinaryExpression::setLeft(const KDbExpression& leftExpr)
417{
419}
420
421KDbExpression KDbBinaryExpression::right() const
422{
423 return (d->children.count() > 1) ? KDbExpression(d->children.at(1)) : KDbExpression();
424}
425
426void KDbBinaryExpression::setRight(const KDbExpression& rightExpr)
427{
428 KDbExpression::setLeftOrRight(rightExpr, 1);
429}
Internal data class used to implement implicitly shared class KDbBinaryExpression.
void debugInternal(QDebug dbg, KDb::ExpressionCallStack *callStack) const override
Sends information about this expression to debug output dbg (internal).
KDbField::Type typeInternal(KDb::ExpressionCallStack *callStack) const override
bool validateInternal(KDbParseInfo *parseInfo, KDb::ExpressionCallStack *callStack) override
The KDbBinaryExpression class represents binary operation.
Internal data class used to implement implicitly shared class KDbConstExpression.
Database driver's abstraction.
Definition KDbDriver.h:50
static QString defaultSqlTypeName(KDbField::Type type)
KDbEscapedString concatenateFunctionToString(const KDbBinaryExpression &args, KDbQuerySchemaParameterValueListIterator *params, KDb::ExpressionCallStack *callStack) const
Generates native (driver-specific) function call for concatenation of two strings.
Specialized string for escaping.
Internal data class used to implement implicitly shared class KDbExpression.
KDbField::Type type() const
KDb::ExpressionClass expressionClass
The KDbExpression class represents a base class for all expressions.
bool isNull() const
static KDb::ExpressionClass classForToken(KDbToken token)
void setLeftOrRight(const KDbExpression &right, int index)
Only for KDbBinaryExpression::setLeft() and KDbBinaryExpression::setRight()
ExplicitlySharedExpressionDataPointer d
bool isTextType() const
Definition KDbField.h:353
QString typeName() const
Definition KDbField.h:377
@ Integer
Definition KDbField.h:90
@ Boolean
Definition KDbField.h:92
@ ShortInteger
Definition KDbField.h:89
@ InvalidType
Definition KDbField.h:86
@ LongText
Definition KDbField.h:99
bool isFPNumericType() const
Definition KDbField.h:335
bool isIntegerType() const
Definition KDbField.h:326
TypeGroup typeGroup() const
Definition KDbField.h:382
Internal data class used to implement implicitly shared class KDbQueryParameterExpression.
The KDbQueryParameterExpression class represents query parameter expression.
An iterator for a list of values of query schema parameters Allows to iterate over parameters and ret...
A type-safe KDbSQL token It can be used in KDb expressions.
Definition KDbToken.h:37
QString toString(const KDbDriver *driver=nullptr) const
Definition KDbToken.cpp:54
int value() const
Definition KDbToken.h:85
KDB_EXPORT KDbField::Type maximumForIntegerFieldTypes(KDbField::Type t1, KDbField::Type t2)
ExpressionClass
Classes of expressions.
QDebug & nospace()
bool toBool() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 12 2024 12:05:27 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.