1
/* This file is part of the KDE project
2
Copyright (C) 2003-2007 Jarosław Staniek <staniek@kde.org>
4
Based on nexp.cpp : Parser module of Python-like language
5
(C) 2001 Jarosław Staniek, MIMUW (www.mimuw.edu.pl)
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.
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.
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.
23
#include "expression.h"
25
#include "parser/sqlparser.h"
26
#include "parser/parser_p.h"
33
CALLIGRADB_EXPORT QString KexiDB::exprClassName(int c)
35
if (c == KexiDBExpr_Unary)
37
else if (c == KexiDBExpr_Arithm)
39
else if (c == KexiDBExpr_Logical)
41
else if (c == KexiDBExpr_Relational)
43
else if (c == KexiDBExpr_SpecialBinary)
44
return "SpecialBinary";
45
else if (c == KexiDBExpr_Const)
47
else if (c == KexiDBExpr_Variable)
49
else if (c == KexiDBExpr_Function)
51
else if (c == KexiDBExpr_Aggregation)
53
else if (c == KexiDBExpr_TableList)
55
else if (c == KexiDBExpr_QueryParameter)
56
return "QueryParameter";
61
using namespace KexiDB;
63
//=========================================
65
BaseExpr::BaseExpr(int token)
66
: m_cl(KexiDBExpr_Unknown)
76
Field::Type BaseExpr::type()
78
return Field::InvalidType;
81
QString BaseExpr::debugString()
83
return QString("BaseExpr(%1,type=%1)").arg(m_token).arg(Driver::defaultSQLTypeName(type()));
86
bool BaseExpr::validate(ParseInfo& /*parseInfo*/)
91
extern const char* tname(int offset);
92
#define safe_tname(token) ((token>=255 && token<=__LAST_TOKEN) ? tname(token-255) : "")
94
QString BaseExpr::tokenToDebugString(int token)
98
return QString(QChar(uchar(token)));
100
return QString::number(token);
102
return QString(safe_tname(token));
105
QString BaseExpr::tokenToString()
107
if (m_token < 255 && isprint(m_token))
108
return tokenToDebugString();
112
NArgExpr* BaseExpr::toNArg()
114
return dynamic_cast<NArgExpr*>(this);
116
UnaryExpr* BaseExpr::toUnary()
118
return dynamic_cast<UnaryExpr*>(this);
120
BinaryExpr* BaseExpr::toBinary()
122
return dynamic_cast<BinaryExpr*>(this);
124
ConstExpr* BaseExpr::toConst()
126
return dynamic_cast<ConstExpr*>(this);
128
VariableExpr* BaseExpr::toVariable()
130
return dynamic_cast<VariableExpr*>(this);
132
FunctionExpr* BaseExpr::toFunction()
134
return dynamic_cast<FunctionExpr*>(this);
136
QueryParameterExpr* BaseExpr::toQueryParameter()
138
return dynamic_cast<QueryParameterExpr*>(this);
141
//=========================================
143
NArgExpr::NArgExpr(int aClass, int token)
149
NArgExpr::NArgExpr(const NArgExpr& expr)
152
foreach(BaseExpr* e, expr.list) {
157
NArgExpr::~NArgExpr()
162
NArgExpr* NArgExpr::copy() const
164
return new NArgExpr(*this);
167
QString NArgExpr::debugString()
169
QString s = QString("NArgExpr(")
170
+ "class=" + exprClassName(m_cl);
171
foreach(BaseExpr *expr, list) {
173
s += expr->debugString();
179
QString NArgExpr::toString(QuerySchemaParameterValueListIterator* params)
183
foreach(BaseExpr* e, list) {
186
s += e->toString(params);
191
void NArgExpr::getQueryParameters(QuerySchemaParameterList& params)
193
foreach(BaseExpr *e, list) {
194
e->getQueryParameters(params);
198
BaseExpr* NArgExpr::arg(int nr)
203
void NArgExpr::add(BaseExpr *expr)
206
expr->setParent(this);
209
void NArgExpr::prepend(BaseExpr *expr)
212
expr->setParent(this);
220
bool NArgExpr::validate(ParseInfo& parseInfo)
222
if (!BaseExpr::validate(parseInfo))
225
foreach(BaseExpr *e, list) {
226
if (!e->validate(parseInfo))
232
//=========================================
233
UnaryExpr::UnaryExpr(int token, BaseExpr *arg)
237
m_cl = KexiDBExpr_Unary;
239
m_arg->setParent(this);
242
UnaryExpr::UnaryExpr(const UnaryExpr& expr)
244
, m_arg(expr.m_arg ? expr.m_arg->copy() : 0)
247
m_arg->setParent(this);
250
UnaryExpr::~UnaryExpr()
255
UnaryExpr* UnaryExpr::copy() const
257
return new UnaryExpr(*this);
260
QString UnaryExpr::debugString()
263
+ tokenToDebugString() + "', "
264
+ (m_arg ? m_arg->debugString() : QString("<NONE>"))
265
+ QString(",type=%1)").arg(Driver::defaultSQLTypeName(type()));
268
QString UnaryExpr::toString(QuerySchemaParameterValueListIterator* params)
270
if (m_token == '(') //parentheses (special case)
271
return "(" + (m_arg ? m_arg->toString(params) : "<NULL>") + ")";
272
if (m_token < 255 && isprint(m_token))
273
return tokenToDebugString() + (m_arg ? m_arg->toString(params) : "<NULL>");
275
return "NOT " + (m_arg ? m_arg->toString(params) : "<NULL>");
276
if (m_token == SQL_IS_NULL)
277
return (m_arg ? m_arg->toString(params) : "<NULL>") + " IS NULL";
278
if (m_token == SQL_IS_NOT_NULL)
279
return (m_arg ? m_arg->toString(params) : "<NULL>") + " IS NOT NULL";
280
return QString("{INVALID_OPERATOR#%1} ").arg(m_token) + (m_arg ? m_arg->toString(params) : "<NULL>");
283
void UnaryExpr::getQueryParameters(QuerySchemaParameterList& params)
286
m_arg->getQueryParameters(params);
289
Field::Type UnaryExpr::type()
291
//NULL IS NOT NULL : BOOLEAN
292
//NULL IS NULL : BOOLEAN
295
case SQL_IS_NOT_NULL:
296
return Field::Boolean;
298
const Field::Type t = m_arg->type();
299
if (t == Field::Null)
302
return Field::Boolean;
307
bool UnaryExpr::validate(ParseInfo& parseInfo)
309
if (!BaseExpr::validate(parseInfo))
312
if (!m_arg->validate(parseInfo))
315
//! @todo compare types... e.g. NOT applied to Text makes no sense...
318
if (m_arg->toQueryParameter()) {
319
m_arg->toQueryParameter()->setType(type());
324
BaseExpr *n = l.at(0);
328
const bool dla "NOT <bool>" (negacja)
329
int dla "# <str>" (dlugosc stringu)
332
if (is(NOT) && n->nodeTypeIs(TYP_BOOL)) {
333
node_type = new NConstType(TYP_BOOL);
334
} else if (is('#') && n->nodeTypeIs(TYP_STR)) {
335
node_type = new NConstType(TYP_INT);
336
} else if ((is('+') || is('-')) && n->nodeTypeIs(TYP_INT)) {
337
node_type = new NConstType(TYP_INT);
339
ERR("Niepoprawny argument typu '%s' dla operatora '%s'",
340
n->nodeTypeName(), is(NOT) ? QString("not") : QChar(typ()));
345
//=========================================
346
BinaryExpr::BinaryExpr(int aClass, BaseExpr *left_expr, int token, BaseExpr *right_expr)
353
m_larg->setParent(this);
355
m_rarg->setParent(this);
358
BinaryExpr::BinaryExpr(const BinaryExpr& expr)
360
, m_larg(expr.m_larg ? expr.m_larg->copy() : 0)
361
, m_rarg(expr.m_rarg ? expr.m_rarg->copy() : 0)
365
BinaryExpr::~BinaryExpr()
371
BinaryExpr* BinaryExpr::copy() const
373
return new BinaryExpr(*this);
376
bool BinaryExpr::validate(ParseInfo& parseInfo)
378
if (!BaseExpr::validate(parseInfo))
381
if (!m_larg->validate(parseInfo))
383
if (!m_rarg->validate(parseInfo))
386
//! @todo compare types..., BITWISE_SHIFT_RIGHT requires integers, etc...
388
//update type for query parameters
389
QueryParameterExpr * queryParameter = m_larg->toQueryParameter();
391
queryParameter->setType(m_rarg->type());
392
queryParameter = m_rarg->toQueryParameter();
394
queryParameter->setType(m_larg->type());
399
Field::Type BinaryExpr::type()
401
const Field::Type lt = m_larg->type(), rt = m_rarg->type();
402
if (lt == Field::InvalidType || rt == Field::InvalidType)
403
return Field::InvalidType;
404
if (lt == Field::Null || rt == Field::Null) {
405
if (m_token != OR) //note that NULL OR something != NULL
410
case BITWISE_SHIFT_RIGHT:
411
case BITWISE_SHIFT_LEFT:
416
const bool ltInt = Field::isIntegerType(lt);
417
const bool rtInt = Field::isIntegerType(rt);
419
return KexiDB::maximumForIntegerTypes(lt, rt);
421
if (Field::isFPNumericType(lt) && (rtInt || lt == rt))
423
if (Field::isFPNumericType(rt) && (ltInt || lt == rt))
426
return Field::Boolean;
429
QString BinaryExpr::debugString()
431
return QString("BinaryExpr(")
432
+ "class=" + exprClassName(m_cl)
433
+ "," + (m_larg ? m_larg->debugString() : QString("<NONE>"))
434
+ ",'" + tokenToDebugString() + "',"
435
+ (m_rarg ? m_rarg->debugString() : QString("<NONE>"))
436
+ QString(",type=%1)").arg(Driver::defaultSQLTypeName(type()));
439
QString BinaryExpr::tokenToString()
441
if (m_token < 255 && isprint(m_token))
442
return tokenToDebugString();
443
// other arithmetic operations: << >>
445
case BITWISE_SHIFT_RIGHT: return ">>";
446
case BITWISE_SHIFT_LEFT: return "<<";
447
// other relational operations: <= >= <> (or !=) LIKE IN
448
case NOT_EQUAL: return "<>";
449
case NOT_EQUAL2: return "!=";
450
case LESS_OR_EQUAL: return "<=";
451
case GREATER_OR_EQUAL: return ">=";
452
case LIKE: return "LIKE";
453
case SQL_IN: return "IN";
454
// other logical operations: OR (or ||) AND (or &&) XOR
455
case SIMILAR_TO: return "SIMILAR TO";
456
case NOT_SIMILAR_TO: return "NOT SIMILAR TO";
457
case OR: return "OR";
458
case AND: return "AND";
459
case XOR: return "XOR";
460
// other string operations: || (as CONCATENATION)
461
case CONCATENATION: return "||";
462
// SpecialBinary "pseudo operators":
463
/* not handled here */
466
return QString("{INVALID_BINARY_OPERATOR#%1} ").arg(m_token);
469
QString BinaryExpr::toString(QuerySchemaParameterValueListIterator* params)
472
(m_larg ? m_larg->toString(params) : "<NULL>") + " " + a + " " + (m_rarg ? m_rarg->toString(params) : "<NULL>")
473
return INFIX(tokenToString());
476
void BinaryExpr::getQueryParameters(QuerySchemaParameterList& params)
479
m_larg->getQueryParameters(params);
481
m_rarg->getQueryParameters(params);
484
//=========================================
485
ConstExpr::ConstExpr(int token, const QVariant& val)
489
m_cl = KexiDBExpr_Const;
492
ConstExpr::ConstExpr(const ConstExpr& expr)
498
ConstExpr::~ConstExpr()
502
ConstExpr* ConstExpr::copy() const
504
return new ConstExpr(*this);
507
Field::Type ConstExpr::type()
509
if (m_token == SQL_NULL)
511
else if (m_token == INTEGER_CONST) {
513
//TODO: add sign info?
514
if (value.type() == QVariant::Int || value.type() == QVariant::UInt) {
515
qint64 v = value.toInt();
516
if (v <= 0xff && v > -0x80)
518
if (v <= 0xffff && v > -0x8000)
519
return Field::ShortInteger;
520
return Field::Integer;
522
return Field::BigInteger;
523
} else if (m_token == CHARACTER_STRING_LITERAL) {
524
//TODO: Field::defaultTextLength() is hardcoded now!
525
if (Field::defaultMaxLength() > 0
526
&& uint(value.toString().length()) > Field::defaultMaxLength())
528
return Field::LongText;
532
} else if (m_token == REAL_CONST)
533
return Field::Double;
534
else if (m_token == DATE_CONST)
536
else if (m_token == DATETIME_CONST)
537
return Field::DateTime;
538
else if (m_token == TIME_CONST)
541
return Field::InvalidType;
544
QString ConstExpr::debugString()
546
return QString("ConstExpr('") + tokenToDebugString() + "'," + toString()
547
+ QString(",type=%1)").arg(Driver::defaultSQLTypeName(type()));
550
QString ConstExpr::toString(QuerySchemaParameterValueListIterator* params)
553
if (m_token == SQL_NULL)
555
else if (m_token == CHARACTER_STRING_LITERAL)
556
//TODO: better escaping!
557
return "'" + value.toString() + "'";
558
else if (m_token == REAL_CONST)
559
return QString::number(value.toPoint().x()) + "." + QString::number(value.toPoint().y());
560
else if (m_token == DATE_CONST)
561
return "'" + value.toDate().toString(Qt::ISODate) + "'";
562
else if (m_token == DATETIME_CONST)
563
return "'" + value.toDateTime().date().toString(Qt::ISODate)
564
+ " " + value.toDateTime().time().toString(Qt::ISODate) + "'";
565
else if (m_token == TIME_CONST)
566
return "'" + value.toTime().toString(Qt::ISODate) + "'";
568
return value.toString();
571
void ConstExpr::getQueryParameters(QuerySchemaParameterList& params)
576
bool ConstExpr::validate(ParseInfo& parseInfo)
578
if (!BaseExpr::validate(parseInfo))
581
return type() != Field::InvalidType;
584
//=========================================
585
QueryParameterExpr::QueryParameterExpr(const QString& message)
586
: ConstExpr(QUERY_PARAMETER, message)
587
, m_type(Field::Text)
589
m_cl = KexiDBExpr_QueryParameter;
592
QueryParameterExpr::QueryParameterExpr(const QueryParameterExpr& expr)
594
, m_type(expr.m_type)
598
QueryParameterExpr::~QueryParameterExpr()
602
QueryParameterExpr* QueryParameterExpr::copy() const
604
return new QueryParameterExpr(*this);
607
Field::Type QueryParameterExpr::type()
612
void QueryParameterExpr::setType(Field::Type type)
617
QString QueryParameterExpr::debugString()
619
return QString("QueryParameterExpr('") + QString::fromLatin1("[%2]").arg(value.toString())
620
+ QString("',type=%1)").arg(Driver::defaultSQLTypeName(type()));
623
QString QueryParameterExpr::toString(QuerySchemaParameterValueListIterator* params)
625
return params ? params->getPreviousValueAsString(type()) : QString::fromLatin1("[%2]").arg(value.toString());
628
void QueryParameterExpr::getQueryParameters(QuerySchemaParameterList& params)
630
QuerySchemaParameter param;
631
param.message = value.toString();
633
params.append(param);
636
bool QueryParameterExpr::validate(ParseInfo& parseInfo)
639
return type() != Field::InvalidType;
642
//=========================================
643
VariableExpr::VariableExpr(const QString& _name)
644
: BaseExpr(0/*undefined*/)
647
, tablePositionForField(-1)
648
, tableForQueryAsterisk(0)
650
m_cl = KexiDBExpr_Variable;
653
VariableExpr::VariableExpr(const VariableExpr& expr)
657
, tablePositionForField(expr.tablePositionForField)
658
, tableForQueryAsterisk(expr.tableForQueryAsterisk)
662
VariableExpr::~VariableExpr()
666
VariableExpr* VariableExpr::copy() const
668
return new VariableExpr(*this);
671
QString VariableExpr::debugString()
673
return QString("VariableExpr(") + name
674
+ QString(",type=%1)").arg(field ? Driver::defaultSQLTypeName(type()) : QString("FIELD NOT DEFINED YET"));
677
QString VariableExpr::toString(QuerySchemaParameterValueListIterator* params)
683
void VariableExpr::getQueryParameters(QuerySchemaParameterList& params)
688
//! We're assuming it's called after VariableExpr::validate()
689
Field::Type VariableExpr::type()
692
return field->type();
694
//BTW, asterisks are not stored in VariableExpr outside of parser, so ok.
695
return Field::InvalidType;
698
#define IMPL_ERROR(errmsg) parseInfo.errMsg = "Implementation error"; parseInfo.errDescr = errmsg
700
bool VariableExpr::validate(ParseInfo& parseInfo)
702
if (!BaseExpr::validate(parseInfo))
705
tablePositionForField = -1;
706
tableForQueryAsterisk = 0;
708
/* taken from parser's addColumn(): */
709
KexiDBDbg << "checking variable name: " << name;
710
int dotPos = name.indexOf('.');
711
QString tableName, fieldName;
712
//TODO: shall we also support db name?
714
tableName = name.left(dotPos);
715
fieldName = name.mid(dotPos + 1);
717
if (tableName.isEmpty()) {//fieldname only
719
if (fieldName == "*") {
720
// querySchema->addAsterisk( new QueryAsterisk(querySchema) );
724
//find first table that has this field
725
Field *firstField = 0;
726
foreach(TableSchema *table, *parseInfo.querySchema->tables()) {
727
Field *f = table->field(fieldName);
731
} else if (f->table() != firstField->table()) {
732
//ambiguous field name
733
parseInfo.errMsg = i18n("Ambiguous field name");
734
parseInfo.errDescr = i18n("Both table \"%1\" and \"%2\" have defined \"%3\" field. "
735
"Use \"<tableName>.%4\" notation to specify table name.",
736
firstField->table()->name(), f->table()->name(),
737
fieldName, fieldName);
743
parseInfo.errMsg = i18n("Field not found");
744
parseInfo.errDescr = i18n("Table containing \"%1\" field not found", fieldName);
748
field = firstField; //store
749
// querySchema->addField(firstField);
753
//table.fieldname or tableAlias.fieldname
754
tableName = tableName;
755
TableSchema *ts = parseInfo.querySchema->table(tableName);
756
if (ts) {//table.fieldname
757
//check if "table" is covered by an alias
758
const QList<int> tPositions = parseInfo.querySchema->tablePositions(tableName);
759
QByteArray tableAlias;
761
foreach(int position, tPositions) {
762
tableAlias = parseInfo.querySchema->tableAlias(position);
763
if (tableAlias.isEmpty() || tableAlias.toLower() == tableName.toLatin1()) {
764
covered = false; //uncovered
767
KexiDBDbg << " --" << "covered by " << tableAlias << " alias";
770
parseInfo.errMsg = i18n("Could not access the table directly using its name");
771
parseInfo.errDescr = i18n("Table \"%1\" is covered by aliases. Instead of \"%2\", "
772
"you can write \"%3\"", tableName, tableName + "." + fieldName, tableAlias + "." + QString(fieldName));
777
int tablePosition = -1;
778
if (!ts) {//try to find tableAlias
779
tablePosition = parseInfo.querySchema->tablePositionForAlias(tableName.toLatin1());
780
if (tablePosition >= 0) {
781
ts = parseInfo.querySchema->tables()->at(tablePosition);
783
// KexiDBDbg << " --it's a tableAlias.name";
789
parseInfo.errMsg = i18n("Table not found");
790
parseInfo.errDescr = i18n("Unknown table \"%1\"", tableName);
794
if (!parseInfo.repeatedTablesAndAliases.contains(tableName)) { //for sanity
795
IMPL_ERROR(tableName + "." + fieldName + ", !positionsList ");
798
const QList<int> positionsList(parseInfo.repeatedTablesAndAliases.value(tableName));
801
if (fieldName == "*") {
802
if (positionsList.count() > 1) {
803
parseInfo.errMsg = i18n("Ambiguous \"%1.*\" expression", tableName);
804
parseInfo.errDescr = i18n("More than one \"%1\" table or alias defined", tableName);
807
tableForQueryAsterisk = ts;
808
// querySchema->addAsterisk( new QueryAsterisk(querySchema, ts) );
812
// KexiDBDbg << " --it's a table.name";
813
Field *realField = ts->field(fieldName);
815
parseInfo.errMsg = i18n("Field not found");
816
parseInfo.errDescr = i18n("Table \"%1\" has no \"%2\" field", tableName, fieldName);
820
// check if table or alias is used twice and both have the same column
821
// (so the column is ambiguous)
822
int numberOfTheSameFields = 0;
823
foreach(int position, positionsList) {
824
TableSchema *otherTS = parseInfo.querySchema->tables()->at(position);
825
if (otherTS->field(fieldName))
826
numberOfTheSameFields++;
827
if (numberOfTheSameFields > 1) {
828
parseInfo.errMsg = i18n("Ambiguous \"%1.%2\" expression", tableName, fieldName);
829
parseInfo.errDescr = i18n("More than one \"%1\" table or alias defined containing \"%2\" field",
830
tableName, fieldName);
834
field = realField; //store
835
tablePositionForField = tablePosition;
836
// querySchema->addField(realField, tablePosition);
841
//=========================================
843
static const char* FunctionExpr_builtIns_[] = {"SUM", "MIN", "MAX", "AVG", "COUNT", "STD", "STDDEV", "VARIANCE", 0 };
845
class BuiltInAggregates : public QSet<QByteArray>
848
BuiltInAggregates() : QSet<QByteArray>() {
849
for (const char **p = FunctionExpr_builtIns_; *p; p++)
850
insert(QByteArray::fromRawData(*p, qstrlen(*p)));
854
K_GLOBAL_STATIC(BuiltInAggregates, _builtInAggregates)
856
//=========================================
858
FunctionExpr::FunctionExpr(const QString& _name, NArgExpr* args_)
859
: BaseExpr(0/*undefined*/)
863
if (isBuiltInAggregate(name.toLatin1()))
864
m_cl = KexiDBExpr_Aggregation;
866
m_cl = KexiDBExpr_Function;
868
args->setParent(this);
871
FunctionExpr::FunctionExpr(const FunctionExpr& expr)
872
: BaseExpr(0/*undefined*/)
874
, args(expr.args ? args->copy() : 0)
877
args->setParent(this);
880
FunctionExpr::~FunctionExpr()
885
FunctionExpr* FunctionExpr::copy() const
887
return new FunctionExpr(*this);
890
QString FunctionExpr::debugString()
893
res.append(QString("FunctionExpr(") + name);
895
res.append(QString(",") + args->debugString());
896
res.append(QString(",type=%1)").arg(Driver::defaultSQLTypeName(type())));
900
QString FunctionExpr::toString(QuerySchemaParameterValueListIterator* params)
902
return name + "(" + (args ? args->toString(params) : QString()) + ")";
905
void FunctionExpr::getQueryParameters(QuerySchemaParameterList& params)
907
args->getQueryParameters(params);
910
Field::Type FunctionExpr::type()
913
return Field::InvalidType;
916
bool FunctionExpr::validate(ParseInfo& parseInfo)
918
if (!BaseExpr::validate(parseInfo))
921
return args ? args->validate(parseInfo) : true;
924
bool FunctionExpr::isBuiltInAggregate(const QByteArray& fname)
926
return _builtInAggregates->contains(fname.toUpper());