/* This file is part of the KDE project Copyright (C) 2003-2007 Jaroslaw Staniek 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 #include #include #include KEXI_DB_EXPORT TQString 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; } TQString BaseExpr::debugString() { return TQString("BaseExpr(%1,type=%1)").tqarg(m_token).tqarg(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) : "") TQString BaseExpr::tokenToDebugString(int token) { if (token < 254) { if (isprint(token)) return TQString(TQChar(uchar(token))); else return TQString::number(token); } return TQString(safe_tname(token)); } TQString BaseExpr::tokenToString() { if (m_token < 255 && isprint(m_token)) return tokenToDebugString(); return TQString(); } NArgExpr* BaseExpr::toNArg() { return dynamic_cast(this); } UnaryExpr* BaseExpr::toUnary() { return dynamic_cast(this); } BinaryExpr* BaseExpr::toBinary() { return dynamic_cast(this); } ConstExpr* BaseExpr::toConst() { return dynamic_cast(this); } VariableExpr* BaseExpr::toVariable() { return dynamic_cast(this); } FunctionExpr* BaseExpr::toFunction() { return dynamic_cast(this); } QueryParameterExpr* BaseExpr::toQueryParameter() { return dynamic_cast(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); } TQString NArgExpr::debugString() { TQString s = TQString("NArgExpr(") + "class=" + exprClassName(m_cl); for ( BaseExpr::ListIterator it(list); it.current(); ++it ) { s+=", "; s+=it.current()->debugString(); } s+=")"; return s; } TQString NArgExpr::toString( QuerySchemaParameterValueListIterator* params ) { TQString 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); } TQString UnaryExpr::debugString() { return "UnaryExpr('" + tokenToDebugString() + "', " + (m_arg ? m_arg->debugString() : TQString("")) + TQString(",type=%1)").tqarg(Driver::defaultSQLTypeName(type())); } TQString UnaryExpr::toString(QuerySchemaParameterValueListIterator* params) { if (m_token=='(') //parentheses (special case) return "(" + (m_arg ? m_arg->toString(params) : "") + ")"; if (m_token < 255 && isprint(m_token)) return tokenToDebugString() + (m_arg ? m_arg->toString(params) : ""); if (m_token==NOT) return "NOT " + (m_arg ? m_arg->toString(params) : ""); if (m_token==SQL_IS_NULL) return (m_arg ? m_arg->toString(params) : "") + " IS NULL"; if (m_token==SQL_IS_NOT_NULL) return (m_arg ? m_arg->toString(params) : "") + " IS NOT NULL"; return TQString("{INVALID_OPERATOR#%1} ").tqarg(m_token) + (m_arg ? m_arg->toString(params) : ""); } 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 " (negacja) int dla "# " (dlugosc stringu) int dla "+/- " */ 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)?TQString("not"):TQChar(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; } TQString BinaryExpr::debugString() { return TQString("BinaryExpr(") + "class=" + exprClassName(m_cl) + "," + (m_larg ? m_larg->debugString() : TQString("")) + ",'" + tokenToDebugString() + "'," + (m_rarg ? m_rarg->debugString() : TQString("")) + TQString(",type=%1)").tqarg(Driver::defaultSQLTypeName(type())); } TQString 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 TQString("{INVALID_BINARY_OPERATOR#%1} ").tqarg(m_token); } TQString BinaryExpr::toString(QuerySchemaParameterValueListIterator* params) { #define INFIX(a) \ (m_larg ? m_larg->toString(params) : "") + " " + a + " " + (m_rarg ? m_rarg->toString(params) : "") 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 TQVariant& 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() == TQVariant::Int || value.type() == TQVariant::UInt) { TQ_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; } TQString ConstExpr::debugString() { return TQString("ConstExpr('") + tokenToDebugString() +"'," + toString() + TQString(",type=%1)").tqarg(Driver::defaultSQLTypeName(type())); } TQString 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 TQString::number(value.toPoint().x())+"."+TQString::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 TQString& message) : ConstExpr( TQUERY_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; } TQString QueryParameterExpr::debugString() { return TQString("QueryParameterExpr('") + TQString::tqfromLatin1("[%2]").tqarg(value.toString()) + TQString("',type=%1)").tqarg(Driver::defaultSQLTypeName(type())); } TQString QueryParameterExpr::toString(QuerySchemaParameterValueListIterator* params) { return params ? params->getPreviousValueAsString(type()) : TQString::tqfromLatin1("[%2]").tqarg(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 TQString& _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); } TQString VariableExpr::debugString() { return TQString("VariableExpr(") + name + TQString(",type=%1)").tqarg(field ? Driver::defaultSQLTypeName(type()) : TQString("FIELD NOT DEFINED YET")); } TQString 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.tqfind('.'); TQString 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 \".%4\" notation to specify table name.") .tqarg(firstField->table()->name()).tqarg(f->table()->name()) .tqarg(fieldName).tqarg(fieldName); return false; } } } if (!firstField) { parseInfo.errMsg = i18n("Field not found"); parseInfo.errDescr = i18n("Table containing \"%1\" field not found").tqarg(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 TQValueList tPositions = parseInfo.querySchema->tablePositions(tableName); TQValueList::ConstIterator it = tPositions.constBegin(); TQCString 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\"").tqarg(tableName) .tqarg(TQString(tableName+"."+fieldName)).tqarg(TQString(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\"").tqarg(tableName); return false; } TQValueList *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").tqarg(tableName); parseInfo.errDescr = i18n("More than one \"%1\" table or alias defined").tqarg(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") .tqarg(tableName).tqarg(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 (TQValueList::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") .tqarg(tableName).tqarg(fieldName); parseInfo.errDescr = i18n("More than one \"%1\" table or alias defined containing \"%2\" field") .tqarg(tableName).tqarg(fieldName); return false; } } field = realField; //store tablePositionForField = tablePosition; // querySchema->addField(realField, tablePosition); return true; } //========================================= static TQValueList FunctionExpr_builtIns; static const char* FunctionExpr_builtIns_[] = {"SUM", "MIN", "MAX", "AVG", "COUNT", "STD", "STDDEV", "VARIANCE", 0 }; TQValueList FunctionExpr::builtInAggregates() { if (FunctionExpr_builtIns.isEmpty()) { for (const char **p = FunctionExpr_builtIns_; *p; p++) FunctionExpr_builtIns << *p; } return FunctionExpr_builtIns; } FunctionExpr::FunctionExpr( const TQString& _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); } TQString FunctionExpr::debugString() { TQString res; res.append( TQString("FunctionExpr(") + name ); if (args) res.append(TQString(",") + args->debugString()); res.append(TQString(",type=%1)").tqarg(Driver::defaultSQLTypeName(type()))); return res; } TQString FunctionExpr::toString(QuerySchemaParameterValueListIterator* params) { return name + "(" + (args ? args->toString(params) : TQString()) + ")"; } 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 TQCString& fname) { return builtInAggregates().tqfind(fname.upper())!=FunctionExpr_builtIns.end(); }