You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
915 lines
22 KiB
915 lines
22 KiB
/* This file is part of the KDE project
|
|
Copyright (C) 2003-2007 Jaroslaw Staniek <js@iidea.pl>
|
|
|
|
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();
|
|
}
|