summaryrefslogtreecommitdiffstats
path: root/kexi/kexidb/expression.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kexi/kexidb/expression.cpp')
-rw-r--r--kexi/kexidb/expression.cpp914
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();
+}