diff options
Diffstat (limited to 'kexi/kexidb/expression.cpp')
-rw-r--r-- | kexi/kexidb/expression.cpp | 914 |
1 files changed, 914 insertions, 0 deletions
diff --git a/kexi/kexidb/expression.cpp b/kexi/kexidb/expression.cpp new file mode 100644 index 00000000..49bb231a --- /dev/null +++ b/kexi/kexidb/expression.cpp @@ -0,0 +1,914 @@ +/* This file is part of the KDE project + Copyright (C) 2003-2007 Jaroslaw Staniek <[email protected]> + + Based on nexp.cpp : Parser module of Python-like language + (C) 2001 Jaroslaw Staniek, MIMUW (www.mimuw.edu.pl) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "expression.h" +#include "utils.h" +#include "parser/sqlparser.h" +#include "parser/parser_p.h" + +#include <ctype.h> + +#include <kdebug.h> +#include <klocale.h> + +#include <qdatetime.h> + +KEXI_DB_EXPORT QString KexiDB::exprClassName(int c) +{ + if (c==KexiDBExpr_Unary) + return "Unary"; + else if (c==KexiDBExpr_Arithm) + return "Arithm"; + else if (c==KexiDBExpr_Logical) + return "Logical"; + else if (c==KexiDBExpr_Relational) + return "Relational"; + else if (c==KexiDBExpr_SpecialBinary) + return "SpecialBinary"; + else if (c==KexiDBExpr_Const) + return "Const"; + else if (c==KexiDBExpr_Variable) + return "Variable"; + else if (c==KexiDBExpr_Function) + return "Function"; + else if (c==KexiDBExpr_Aggregation) + return "Aggregation"; + else if (c==KexiDBExpr_TableList) + return "TableList"; + else if (c==KexiDBExpr_QueryParameter) + return "QueryParameter"; + + return "Unknown"; +} + +using namespace KexiDB; + +//========================================= + +BaseExpr::BaseExpr(int token) + : m_cl(KexiDBExpr_Unknown) + , m_par(0) + , m_token(token) +{ +} + +BaseExpr::~BaseExpr() +{ +} + +Field::Type BaseExpr::type() +{ + return Field::InvalidType; +} + +QString BaseExpr::debugString() +{ + return QString("BaseExpr(%1,type=%1)").arg(m_token).arg(Driver::defaultSQLTypeName(type())); +} + +bool BaseExpr::validate(ParseInfo& /*parseInfo*/) +{ + return true; +} + +extern const char * const tname(int offset); +#define safe_tname(token) ((token>=255 && token<=__LAST_TOKEN) ? tname(token-255) : "") + +QString BaseExpr::tokenToDebugString(int token) +{ + if (token < 254) { + if (isprint(token)) + return QString(QChar(uchar(token))); + else + return QString::number(token); + } + return QString(safe_tname(token)); +} + +QString BaseExpr::tokenToString() +{ + if (m_token < 255 && isprint(m_token)) + return tokenToDebugString(); + return QString::null; +} + +NArgExpr* BaseExpr::toNArg() { return dynamic_cast<NArgExpr*>(this); } +UnaryExpr* BaseExpr::toUnary() { return dynamic_cast<UnaryExpr*>(this); } +BinaryExpr* BaseExpr::toBinary() { return dynamic_cast<BinaryExpr*>(this); } +ConstExpr* BaseExpr::toConst() { return dynamic_cast<ConstExpr*>(this); } +VariableExpr* BaseExpr::toVariable() { return dynamic_cast<VariableExpr*>(this); } +FunctionExpr* BaseExpr::toFunction() { return dynamic_cast<FunctionExpr*>(this); } +QueryParameterExpr* BaseExpr::toQueryParameter() { return dynamic_cast<QueryParameterExpr*>(this); } + +//========================================= + +NArgExpr::NArgExpr(int aClass, int token) + : BaseExpr(token) +{ + m_cl = aClass; + list.setAutoDelete(true); +} + +NArgExpr::NArgExpr(const NArgExpr& expr) + : BaseExpr(expr) +{ + foreach_list (BaseExpr::ListIterator, it, expr.list) + add( it.current()->copy() ); +} + +NArgExpr::~NArgExpr() +{ +} + +NArgExpr* NArgExpr::copy() const +{ + return new NArgExpr(*this); +} + +QString NArgExpr::debugString() +{ + QString s = QString("NArgExpr(") + + "class=" + exprClassName(m_cl); + for ( BaseExpr::ListIterator it(list); it.current(); ++it ) { + s+=", "; + s+=it.current()->debugString(); + } + s+=")"; + return s; +} + +QString NArgExpr::toString( QuerySchemaParameterValueListIterator* params ) +{ + QString s; + s.reserve(256); + foreach_list( BaseExpr::ListIterator, it, list) { + if (!s.isEmpty()) + s+=", "; + s+=it.current()->toString(params); + } + return s; +} + +void NArgExpr::getQueryParameters(QuerySchemaParameterList& params) +{ + foreach_list( BaseExpr::ListIterator, it, list) + it.current()->getQueryParameters(params); +} + +BaseExpr* NArgExpr::arg(int nr) +{ + return list.at(nr); +} + +void NArgExpr::add(BaseExpr *expr) +{ + list.append(expr); + expr->setParent(this); +} + +void NArgExpr::prepend(BaseExpr *expr) +{ + list.prepend(expr); + expr->setParent(this); +} + +int NArgExpr::args() +{ + return list.count(); +} + +bool NArgExpr::validate(ParseInfo& parseInfo) +{ + if (!BaseExpr::validate(parseInfo)) + return false; + + foreach_list(BaseExpr::ListIterator, it, list) { + if (!it.current()->validate(parseInfo)) + return false; + } + return true; +} + +//========================================= +UnaryExpr::UnaryExpr(int token, BaseExpr *arg) + : BaseExpr(token) + , m_arg(arg) +{ + m_cl = KexiDBExpr_Unary; + if (m_arg) + m_arg->setParent(this); +} + +UnaryExpr::UnaryExpr(const UnaryExpr& expr) + : BaseExpr(expr) + , m_arg( expr.m_arg ? expr.m_arg->copy() : 0 ) +{ + if (m_arg) + m_arg->setParent(this); +} + +UnaryExpr::~UnaryExpr() +{ + delete m_arg; +} + +UnaryExpr* UnaryExpr::copy() const +{ + return new UnaryExpr(*this); +} + +QString UnaryExpr::debugString() +{ + return "UnaryExpr('" + + tokenToDebugString() + "', " + + (m_arg ? m_arg->debugString() : QString("<NONE>")) + + QString(",type=%1)").arg(Driver::defaultSQLTypeName(type())); +} + +QString UnaryExpr::toString(QuerySchemaParameterValueListIterator* params) +{ + if (m_token=='(') //parentheses (special case) + return "(" + (m_arg ? m_arg->toString(params) : "<NULL>") + ")"; + if (m_token < 255 && isprint(m_token)) + return tokenToDebugString() + (m_arg ? m_arg->toString(params) : "<NULL>"); + if (m_token==NOT) + return "NOT " + (m_arg ? m_arg->toString(params) : "<NULL>"); + if (m_token==SQL_IS_NULL) + return (m_arg ? m_arg->toString(params) : "<NULL>") + " IS NULL"; + if (m_token==SQL_IS_NOT_NULL) + return (m_arg ? m_arg->toString(params) : "<NULL>") + " IS NOT NULL"; + return QString("{INVALID_OPERATOR#%1} ").arg(m_token) + (m_arg ? m_arg->toString(params) : "<NULL>"); +} + +void UnaryExpr::getQueryParameters(QuerySchemaParameterList& params) +{ + if (m_arg) + m_arg->getQueryParameters(params); +} + +Field::Type UnaryExpr::type() +{ + //NULL IS NOT NULL : BOOLEAN + //NULL IS NULL : BOOLEAN + switch (m_token) { + case SQL_IS_NULL: + case SQL_IS_NOT_NULL: + return Field::Boolean; + } + const Field::Type t = m_arg->type(); + if (t==Field::Null) + return Field::Null; + if (m_token==NOT) + return Field::Boolean; + + return t; +} + +bool UnaryExpr::validate(ParseInfo& parseInfo) +{ + if (!BaseExpr::validate(parseInfo)) + return false; + + if (!m_arg->validate(parseInfo)) + return false; + +//! @todo compare types... e.g. NOT applied to Text makes no sense... + + // update type + if (m_arg->toQueryParameter()) { + m_arg->toQueryParameter()->setType(type()); + } + + return true; +#if 0 + BaseExpr *n = l.at(0); + + n->check(); +/*typ wyniku: + const bool dla "NOT <bool>" (negacja) + int dla "# <str>" (dlugosc stringu) + int dla "+/- <int>" + */ + if (is(NOT) && n->nodeTypeIs(TYP_BOOL)) { + node_type=new NConstType(TYP_BOOL); + } + else if (is('#') && n->nodeTypeIs(TYP_STR)) { + node_type=new NConstType(TYP_INT); + } + else if ((is('+') || is('-')) && n->nodeTypeIs(TYP_INT)) { + node_type=new NConstType(TYP_INT); + } + else { + ERR("Niepoprawny argument typu '%s' dla operatora '%s'", + n->nodeTypeName(),is(NOT)?QString("not"):QChar(typ())); + } +#endif +} + +//========================================= +BinaryExpr::BinaryExpr(int aClass, BaseExpr *left_expr, int token, BaseExpr *right_expr) + : BaseExpr(token) + , m_larg(left_expr) + , m_rarg(right_expr) +{ + m_cl = aClass; + if (m_larg) + m_larg->setParent(this); + if (m_rarg) + m_rarg->setParent(this); +} + +BinaryExpr::BinaryExpr(const BinaryExpr& expr) + : BaseExpr(expr) + , m_larg( expr.m_larg ? expr.m_larg->copy() : 0 ) + , m_rarg( expr.m_rarg ? expr.m_rarg->copy() : 0 ) +{ +} + +BinaryExpr::~BinaryExpr() +{ + delete m_larg; + delete m_rarg; +} + +BinaryExpr* BinaryExpr::copy() const +{ + return new BinaryExpr(*this); +} + +bool BinaryExpr::validate(ParseInfo& parseInfo) +{ + if (!BaseExpr::validate(parseInfo)) + return false; + + if (!m_larg->validate(parseInfo)) + return false; + if (!m_rarg->validate(parseInfo)) + return false; + +//! @todo compare types..., BITWISE_SHIFT_RIGHT requires integers, etc... + + //update type for query parameters + QueryParameterExpr * queryParameter = m_larg->toQueryParameter(); + if (queryParameter) + queryParameter->setType(m_rarg->type()); + queryParameter = m_rarg->toQueryParameter(); + if (queryParameter) + queryParameter->setType(m_larg->type()); + + return true; +} + +Field::Type BinaryExpr::type() +{ + const Field::Type lt = m_larg->type(), rt = m_rarg->type(); + if (lt==Field::InvalidType || rt == Field::InvalidType) + return Field::InvalidType; + if (lt==Field::Null || rt == Field::Null) { + if (m_token!=OR) //note that NULL OR something != NULL + return Field::Null; + } + + switch (m_token) { + case BITWISE_SHIFT_RIGHT: + case BITWISE_SHIFT_LEFT: + case CONCATENATION: + return lt; + } + + const bool ltInt = Field::isIntegerType(lt); + const bool rtInt = Field::isIntegerType(rt); + if (ltInt && rtInt) + return KexiDB::maximumForIntegerTypes(lt, rt); + + if (Field::isFPNumericType(lt) && rtInt) + return lt; + if (Field::isFPNumericType(rt) && ltInt) + return rt; + if ((lt==Field::Double || lt==Field::Float) && rtInt) + return lt; + if ((rt==Field::Double || rt==Field::Float) && ltInt) + return rt; + + return Field::Boolean; +} + +QString BinaryExpr::debugString() +{ + return QString("BinaryExpr(") + + "class=" + exprClassName(m_cl) + + "," + (m_larg ? m_larg->debugString() : QString("<NONE>")) + + ",'" + tokenToDebugString() + "'," + + (m_rarg ? m_rarg->debugString() : QString("<NONE>")) + + QString(",type=%1)").arg(Driver::defaultSQLTypeName(type())); +} + +QString BinaryExpr::tokenToString() +{ + if (m_token < 255 && isprint(m_token)) + return tokenToDebugString(); + // other arithmetic operations: << >> + switch (m_token) { + case BITWISE_SHIFT_RIGHT: return ">>"; + case BITWISE_SHIFT_LEFT: return "<<"; + // other relational operations: <= >= <> (or !=) LIKE IN + case NOT_EQUAL: return "<>"; + case NOT_EQUAL2: return "!="; + case LESS_OR_EQUAL: return "<="; + case GREATER_OR_EQUAL: return ">="; + case LIKE: return "LIKE"; + case SQL_IN: return "IN"; + // other logical operations: OR (or ||) AND (or &&) XOR + case SIMILAR_TO: return "SIMILAR TO"; + case NOT_SIMILAR_TO: return "NOT SIMILAR TO"; + case OR: return "OR"; + case AND: return "AND"; + case XOR: return "XOR"; + // other string operations: || (as CONCATENATION) + case CONCATENATION: return "||"; + // SpecialBinary "pseudo operators": + /* not handled here */ + default:; + } + return QString("{INVALID_BINARY_OPERATOR#%1} ").arg(m_token); +} + +QString BinaryExpr::toString(QuerySchemaParameterValueListIterator* params) +{ +#define INFIX(a) \ + (m_larg ? m_larg->toString(params) : "<NULL>") + " " + a + " " + (m_rarg ? m_rarg->toString(params) : "<NULL>") + return INFIX(tokenToString()); +} + +void BinaryExpr::getQueryParameters(QuerySchemaParameterList& params) +{ + if (m_larg) + m_larg->getQueryParameters(params); + if (m_rarg) + m_rarg->getQueryParameters(params); +} + +//========================================= +ConstExpr::ConstExpr( int token, const QVariant& val) +: BaseExpr( token ) +, value(val) +{ + m_cl = KexiDBExpr_Const; +} + +ConstExpr::ConstExpr(const ConstExpr& expr) + : BaseExpr(expr) + , value(expr.value) +{ +} + +ConstExpr::~ConstExpr() +{ +} + +ConstExpr* ConstExpr::copy() const +{ + return new ConstExpr(*this); +} + +Field::Type ConstExpr::type() +{ + if (m_token==SQL_NULL) + return Field::Null; + else if (m_token==INTEGER_CONST) { +//TODO ok? +//TODO: add sign info? + if (value.type() == QVariant::Int || value.type() == QVariant::UInt) { + Q_LLONG v = value.toInt(); + if (v <= 0xff && v > -0x80) + return Field::Byte; + if (v <= 0xffff && v > -0x8000) + return Field::ShortInteger; + return Field::Integer; + } + return Field::BigInteger; + } + else if (m_token==CHARACTER_STRING_LITERAL) { +//TODO: Field::defaultTextLength() is hardcoded now! + if (value.toString().length() > Field::defaultTextLength()) + return Field::LongText; + else + return Field::Text; + } + else if (m_token==REAL_CONST) + return Field::Double; + else if (m_token==DATE_CONST) + return Field::Date; + else if (m_token==DATETIME_CONST) + return Field::DateTime; + else if (m_token==TIME_CONST) + return Field::Time; + + return Field::InvalidType; +} + +QString ConstExpr::debugString() +{ + return QString("ConstExpr('") + tokenToDebugString() +"'," + toString() + + QString(",type=%1)").arg(Driver::defaultSQLTypeName(type())); +} + +QString ConstExpr::toString(QuerySchemaParameterValueListIterator* params) +{ + Q_UNUSED(params); + if (m_token==SQL_NULL) + return "NULL"; + else if (m_token==CHARACTER_STRING_LITERAL) +//TODO: better escaping! + return "'" + value.toString() + "'"; + else if (m_token==REAL_CONST) + return QString::number(value.toPoint().x())+"."+QString::number(value.toPoint().y()); + else if (m_token==DATE_CONST) + return "'" + value.toDate().toString(Qt::ISODate) + "'"; + else if (m_token==DATETIME_CONST) + return "'" + value.toDateTime().date().toString(Qt::ISODate) + + " " + value.toDateTime().time().toString(Qt::ISODate) + "'"; + else if (m_token==TIME_CONST) + return "'" + value.toTime().toString(Qt::ISODate) + "'"; + + return value.toString(); +} + +void ConstExpr::getQueryParameters(QuerySchemaParameterList& params) +{ + Q_UNUSED(params); +} + +bool ConstExpr::validate(ParseInfo& parseInfo) +{ + if (!BaseExpr::validate(parseInfo)) + return false; + + return type()!=Field::InvalidType; +} + +//========================================= +QueryParameterExpr::QueryParameterExpr(const QString& message) +: ConstExpr( QUERY_PARAMETER, message ) +, m_type(Field::Text) +{ + m_cl = KexiDBExpr_QueryParameter; +} + +QueryParameterExpr::QueryParameterExpr(const QueryParameterExpr& expr) + : ConstExpr(expr) + , m_type(expr.m_type) +{ +} + +QueryParameterExpr::~QueryParameterExpr() +{ +} + +QueryParameterExpr* QueryParameterExpr::copy() const +{ + return new QueryParameterExpr(*this); +} + +Field::Type QueryParameterExpr::type() +{ + return m_type; +} + +void QueryParameterExpr::setType(Field::Type type) +{ + m_type = type; +} + +QString QueryParameterExpr::debugString() +{ + return QString("QueryParameterExpr('") + QString::fromLatin1("[%2]").arg(value.toString()) + + QString("',type=%1)").arg(Driver::defaultSQLTypeName(type())); +} + +QString QueryParameterExpr::toString(QuerySchemaParameterValueListIterator* params) +{ + return params ? params->getPreviousValueAsString(type()) : QString::fromLatin1("[%2]").arg(value.toString()); +} + +void QueryParameterExpr::getQueryParameters(QuerySchemaParameterList& params) +{ + QuerySchemaParameter param; + param.message = value.toString(); + param.type = type(); + params.append( param ); +} + +bool QueryParameterExpr::validate(ParseInfo& parseInfo) +{ + Q_UNUSED(parseInfo); + return type()!=Field::InvalidType; +} + +//========================================= +VariableExpr::VariableExpr(const QString& _name) +: BaseExpr( 0/*undefined*/ ) +, name(_name) +, field(0) +, tablePositionForField(-1) +, tableForQueryAsterisk(0) +{ + m_cl = KexiDBExpr_Variable; +} + +VariableExpr::VariableExpr(const VariableExpr& expr) + : BaseExpr(expr) + , name(expr.name) + , field(expr.field) + , tablePositionForField(expr.tablePositionForField) + , tableForQueryAsterisk(expr.tableForQueryAsterisk) +{ +} + +VariableExpr::~VariableExpr() +{ +} + +VariableExpr* VariableExpr::copy() const +{ + return new VariableExpr(*this); +} + +QString VariableExpr::debugString() +{ + return QString("VariableExpr(") + name + + QString(",type=%1)").arg(field ? Driver::defaultSQLTypeName(type()) : QString("FIELD NOT DEFINED YET")); +} + +QString VariableExpr::toString(QuerySchemaParameterValueListIterator* params) +{ + Q_UNUSED(params); + return name; +} + +void VariableExpr::getQueryParameters(QuerySchemaParameterList& params) +{ + Q_UNUSED(params); +} + +//! We're assuming it's called after VariableExpr::validate() +Field::Type VariableExpr::type() +{ + if (field) + return field->type(); + + //BTW, asterisks are not stored in VariableExpr outside of parser, so ok. + return Field::InvalidType; +} + +#define IMPL_ERROR(errmsg) parseInfo.errMsg = "Implementation error"; parseInfo.errDescr = errmsg + +bool VariableExpr::validate(ParseInfo& parseInfo) +{ + if (!BaseExpr::validate(parseInfo)) + return false; + field = 0; + tablePositionForField = -1; + tableForQueryAsterisk = 0; + +/* taken from parser's addColumn(): */ + KexiDBDbg << "checking variable name: " << name << endl; + int dotPos = name.find('.'); + QString tableName, fieldName; +//TODO: shall we also support db name? + if (dotPos>0) { + tableName = name.left(dotPos); + fieldName = name.mid(dotPos+1); + } + if (tableName.isEmpty()) {//fieldname only + fieldName = name; + if (fieldName=="*") { +// querySchema->addAsterisk( new QueryAsterisk(querySchema) ); + return true; + } + + //find first table that has this field + Field *firstField = 0; + foreach_list(TableSchema::ListIterator, it, *parseInfo.querySchema->tables()) { + Field *f = it.current()->field(fieldName); + if (f) { + if (!firstField) { + firstField = f; + } + else if (f->table()!=firstField->table()) { + //ambiguous field name + parseInfo.errMsg = i18n("Ambiguous field name"); + parseInfo.errDescr = i18n("Both table \"%1\" and \"%2\" have defined \"%3\" field. " + "Use \"<tableName>.%4\" notation to specify table name.") + .arg(firstField->table()->name()).arg(f->table()->name()) + .arg(fieldName).arg(fieldName); + return false; + } + } + } + if (!firstField) { + parseInfo.errMsg = i18n("Field not found"); + parseInfo.errDescr = i18n("Table containing \"%1\" field not found").arg(fieldName); + return false; + } + //ok + field = firstField; //store +// querySchema->addField(firstField); + return true; + } + + //table.fieldname or tableAlias.fieldname + tableName = tableName.lower(); + TableSchema *ts = parseInfo.querySchema->table( tableName ); + if (ts) {//table.fieldname + //check if "table" is covered by an alias + const QValueList<int> tPositions = parseInfo.querySchema->tablePositions(tableName); + QValueList<int>::ConstIterator it = tPositions.constBegin(); + QCString tableAlias; + bool covered = true; + for (; it!=tPositions.constEnd() && covered; ++it) { + tableAlias = parseInfo.querySchema->tableAlias(*it); + if (tableAlias.isEmpty() || tableAlias.lower()==tableName.latin1()) + covered = false; //uncovered + KexiDBDbg << " --" << "covered by " << tableAlias << " alias" << endl; + } + if (covered) { + parseInfo.errMsg = i18n("Could not access the table directly using its name"); + parseInfo.errDescr = i18n("Table \"%1\" is covered by aliases. Instead of \"%2\", " + "you can write \"%3\"").arg(tableName) + .arg(tableName+"."+fieldName).arg(tableAlias+"."+fieldName.latin1()); + return false; + } + } + + int tablePosition = -1; + if (!ts) {//try to find tableAlias + tablePosition = parseInfo.querySchema->tablePositionForAlias( tableName.latin1() ); + if (tablePosition>=0) { + ts = parseInfo.querySchema->tables()->at(tablePosition); + if (ts) { +// KexiDBDbg << " --it's a tableAlias.name" << endl; + } + } + } + + if (!ts) { + parseInfo.errMsg = i18n("Table not found"); + parseInfo.errDescr = i18n("Unknown table \"%1\"").arg(tableName); + return false; + } + + QValueList<int> *positionsList = parseInfo.repeatedTablesAndAliases[ tableName ]; + if (!positionsList) { //for sanity + IMPL_ERROR(tableName + "." + fieldName + ", !positionsList "); + return false; + } + + //it's a table.* + if (fieldName=="*") { + if (positionsList->count()>1) { + parseInfo.errMsg = i18n("Ambiguous \"%1.*\" expression").arg(tableName); + parseInfo.errDescr = i18n("More than one \"%1\" table or alias defined").arg(tableName); + return false; + } + tableForQueryAsterisk = ts; +// querySchema->addAsterisk( new QueryAsterisk(querySchema, ts) ); + return true; + } + +// KexiDBDbg << " --it's a table.name" << endl; + Field *realField = ts->field(fieldName); + if (!realField) { + parseInfo.errMsg = i18n("Field not found"); + parseInfo.errDescr = i18n("Table \"%1\" has no \"%2\" field") + .arg(tableName).arg(fieldName); + return false; + } + + // check if table or alias is used twice and both have the same column + // (so the column is ambiguous) + int numberOfTheSameFields = 0; + for (QValueList<int>::iterator it = positionsList->begin(); + it!=positionsList->end();++it) + { + TableSchema *otherTS = parseInfo.querySchema->tables()->at(*it); + if (otherTS->field(fieldName)) + numberOfTheSameFields++; + if (numberOfTheSameFields>1) { + parseInfo.errMsg = i18n("Ambiguous \"%1.%2\" expression") + .arg(tableName).arg(fieldName); + parseInfo.errDescr = i18n("More than one \"%1\" table or alias defined containing \"%2\" field") + .arg(tableName).arg(fieldName); + return false; + } + } + field = realField; //store + tablePositionForField = tablePosition; +// querySchema->addField(realField, tablePosition); + + return true; +} + +//========================================= +static QValueList<QCString> FunctionExpr_builtIns; +static const char* FunctionExpr_builtIns_[] = +{"SUM", "MIN", "MAX", "AVG", "COUNT", "STD", "STDDEV", "VARIANCE", 0 }; + +QValueList<QCString> FunctionExpr::builtInAggregates() +{ + if (FunctionExpr_builtIns.isEmpty()) { + for (const char **p = FunctionExpr_builtIns_; *p; p++) + FunctionExpr_builtIns << *p; + } + return FunctionExpr_builtIns; +} + +FunctionExpr::FunctionExpr( const QString& _name, NArgExpr* args_ ) + : BaseExpr( 0/*undefined*/ ) + , name(_name) + , args(args_) +{ + if (isBuiltInAggregate(name.latin1())) + m_cl = KexiDBExpr_Aggregation; + else + m_cl = KexiDBExpr_Function; + if (args) + args->setParent( this ); +} + +FunctionExpr::FunctionExpr( const FunctionExpr& expr ) + : BaseExpr( 0/*undefined*/ ) + , name(expr.name) + , args(expr.args ? args->copy() : 0) +{ + if (args) + args->setParent( this ); +} + +FunctionExpr::~FunctionExpr() +{ + delete args; +} + +FunctionExpr* FunctionExpr::copy() const +{ + return new FunctionExpr(*this); +} + +QString FunctionExpr::debugString() +{ + QString res; + res.append( QString("FunctionExpr(") + name ); + if (args) + res.append(QString(",") + args->debugString()); + res.append(QString(",type=%1)").arg(Driver::defaultSQLTypeName(type()))); + return res; +} + +QString FunctionExpr::toString(QuerySchemaParameterValueListIterator* params) +{ + return name + "(" + (args ? args->toString(params) : QString::null) + ")"; +} + +void FunctionExpr::getQueryParameters(QuerySchemaParameterList& params) +{ + args->getQueryParameters(params); +} + +Field::Type FunctionExpr::type() +{ + //TODO + return Field::InvalidType; +} + +bool FunctionExpr::validate(ParseInfo& parseInfo) +{ + if (!BaseExpr::validate(parseInfo)) + return false; + + return args ? args->validate(parseInfo) : true; +} + +bool FunctionExpr::isBuiltInAggregate(const QCString& fname) +{ + return builtInAggregates().find(fname.upper())!=FunctionExpr_builtIns.end(); +} |