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.
koffice/kexi/kexidb/expression.cpp

915 lines
23 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 <tqdatetime.h>
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)").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) : "")
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<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);
}
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("<NONE>"))
+ TQString(",type=%1)").arg(Driver::defaultSQLTypeName(type()));
}
TQString 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 TQString("{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)?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("<NONE>"))
+ ",'" + tokenToDebugString() + "',"
+ (m_rarg ? m_rarg->debugString() : TQString("<NONE>"))
+ TQString(",type=%1)").arg(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} ").arg(m_token);
}
TQString 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 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)").arg(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::fromLatin1("[%2]").arg(value.toString())
+ TQString("',type=%1)").arg(Driver::defaultSQLTypeName(type()));
}
TQString QueryParameterExpr::toString(QuerySchemaParameterValueListIterator* params)
{
return params ? params->getPreviousValueAsString(type()) : TQString::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 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)").arg(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.find('.');
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 \"<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 TQValueList<int> tPositions = parseInfo.querySchema->tablePositions(tableName);
TQValueList<int>::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\"").arg(tableName)
.arg(TQString(tableName+"."+fieldName)).arg(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\"").arg(tableName);
return false;
}
TQValueList<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 (TQValueList<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 TQValueList<TQCString> FunctionExpr_builtIns;
static const char* FunctionExpr_builtIns_[] =
{"SUM", "MIN", "MAX", "AVG", "COUNT", "STD", "STDDEV", "VARIANCE", 0 };
TQValueList<TQCString> 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)").arg(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().find(fname.upper())!=FunctionExpr_builtIns.end();
}