KDb

KDbBinaryExpression.cpp
1 /* This file is part of the KDE project
2  Copyright (C) 2003-2016 JarosÅ‚aw Staniek <[email protected]>
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 
31 KDbBinaryExpressionData::KDbBinaryExpressionData()
33 {
34  ExpressionDebug << "BinaryExpressionData" << ref;
35 }
36 
37 KDbBinaryExpressionData::~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)
80  return KDbField::InvalidType;
81  const KDbField::Type lt = left()->type(callStack);
82  const KDbField::Type rt = right()->type(callStack);
84  return KDbField::InvalidType;
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  }
169  return KDbField::InvalidType;
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  }
192  return KDbField::InvalidType;
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  }
222  return KDbField::InvalidType;
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  */
241  KDbField::Type t;
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  }
285  return KDbField::InvalidType;
286 }
287 
288 KDbBinaryExpressionData* 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 
315 KDbEscapedString 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 
339 void 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 
348 ExplicitlySharedExpressionDataPointer KDbBinaryExpressionData::left() const
349 {
350  return (children.count() > 0) ? children.at(0) : ExplicitlySharedExpressionDataPointer();
351 }
352 ExplicitlySharedExpressionDataPointer KDbBinaryExpressionData::right() const
353 {
354  return (children.count() > 1) ? children.at(1) : ExplicitlySharedExpressionDataPointer();
355 }
356 
357 //=========================================
358 
359 static 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 
392  : KDbExpression(expr)
393 {
394 }
395 
397  : KDbExpression(data)
398 {
399  ExpressionDebug << "KDbBinaryExpression(KDbExpressionData*) ctor" << *this;
400 }
401 
403  : KDbExpression(ptr)
404 {
405 }
406 
407 KDbBinaryExpression::~KDbBinaryExpression()
408 {
409 }
410 
411 KDbExpression KDbBinaryExpression::left() const
412 {
413  return (d->children.count() > 0) ? KDbExpression(d->children.at(0)) : KDbExpression();
414 }
415 
416 void KDbBinaryExpression::setLeft(const KDbExpression& leftExpr)
417 {
418  KDbExpression::setLeftOrRight(leftExpr, 0);
419 }
420 
421 KDbExpression KDbBinaryExpression::right() const
422 {
423  return (d->children.count() > 1) ? KDbExpression(d->children.at(1)) : KDbExpression();
424 }
425 
426 void KDbBinaryExpression::setRight(const KDbExpression& rightExpr)
427 {
428  KDbExpression::setLeftOrRight(rightExpr, 1);
429 }
void setLeftOrRight(const KDbExpression &right, int index)
Only for KDbBinaryExpression::setLeft() and KDbBinaryExpression::setRight()
Internal data class used to implement implicitly shared class KDbExpression.
QTextStream & right(QTextStream &stream)
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:36
void debugInternal(QDebug dbg, KDb::ExpressionCallStack *callStack) const override
Sends information about this expression to debug output dbg (internal).
@ InvalidType
Definition: KDbField.h:86
The KDbBinaryExpression class represents binary operation.
Internal data class used to implement implicitly shared class KDbBinaryExpression.
@ Text
Definition: KDbField.h:98
void ref()
QTextStream & left(QTextStream &stream)
QDebug & nospace()
Specialized string for escaping.
Database driver's abstraction.
Definition: KDbDriver.h:49
Internal data class used to implement implicitly shared class KDbQueryParameterExpression.
The KDbQueryParameterExpression class represents query parameter expression.
static KDb::ExpressionClass classForToken(KDbToken token)
bool isFPNumericType() const
Definition: KDbField.h:335
static QString defaultSqlTypeName(KDbField::Type type)
Definition: KDbDriver.cpp:166
QString typeName() const
Definition: KDbField.h:377
bool isTextType() const
Definition: KDbField.h:353
bool isIntegerType() const
Definition: KDbField.h:326
@ Integer
Definition: KDbField.h:90
Internal data class used to implement implicitly shared class KDbConstExpression.
TypeGroup typeGroup() const
Definition: KDbField.h:382
KDB_EXPORT KDbField::Type maximumForIntegerFieldTypes(KDbField::Type t1, KDbField::Type t2)
Definition: KDb.cpp:1936
ExplicitlySharedExpressionDataPointer d
bool validateInternal(KDbParseInfo *parseInfo, KDb::ExpressionCallStack *callStack) override
ExpressionClass
Classes of expressions.
bool toBool() const const
@ LongText
Definition: KDbField.h:99
@ Byte
Definition: KDbField.h:87
@ Boolean
Definition: KDbField.h:92
bool isNull() const
KDbEscapedString concatenateFunctionToString(const KDbBinaryExpression &args, KDbQuerySchemaParameterValueListIterator *params, KDb::ExpressionCallStack *callStack) const
Generates native (driver-specific) function call for concatenation of two strings.
Definition: KDbDriver.cpp:390
The KDbExpression class represents a base class for all expressions.
Definition: KDbExpression.h:51
@ ShortInteger
Definition: KDbField.h:89
KDbField::Type typeInternal(KDb::ExpressionCallStack *callStack) const override
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Thu Nov 30 2023 04:10:46 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.