|
|
|
/* This file is part of the KDE project
|
|
|
|
Copyright (C) 2004, 2006 Jaroslaw Staniek <js@iidea.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 "parser_p.h"
|
|
|
|
#include "sqlparser.h"
|
|
|
|
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <klocale.h>
|
|
|
|
|
|
|
|
#include <tqregexp.h>
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
using namespace KexiDB;
|
|
|
|
|
|
|
|
Parser *parser = 0;
|
|
|
|
Field *field = 0;
|
|
|
|
//bool requiresTable;
|
|
|
|
TQPtrList<Field> fieldList;
|
|
|
|
int current = 0;
|
|
|
|
TQString ctoken = "";
|
|
|
|
|
|
|
|
//-------------------------------------
|
|
|
|
|
|
|
|
ParserPrivate::ParserPrivate()
|
|
|
|
: reservedKeywords(997, 997, false)
|
|
|
|
, initialized(false)
|
|
|
|
{
|
|
|
|
clear();
|
|
|
|
table = 0;
|
|
|
|
select = 0;
|
|
|
|
db = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ParserPrivate::~ParserPrivate()
|
|
|
|
{
|
|
|
|
delete select;
|
|
|
|
delete table;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ParserPrivate::clear()
|
|
|
|
{
|
|
|
|
operation = Parser::OP_None;
|
|
|
|
error = ParserError();
|
|
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------
|
|
|
|
|
|
|
|
ParseInfo::ParseInfo(KexiDB::QuerySchema *query)
|
|
|
|
: repeatedTablesAndAliases(997, false)
|
|
|
|
, querySchema(query)
|
|
|
|
{
|
|
|
|
repeatedTablesAndAliases.setAutoDelete(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
ParseInfo::~ParseInfo()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------
|
|
|
|
|
|
|
|
extern int yyparse();
|
|
|
|
extern void tokenize(const char *data);
|
|
|
|
|
|
|
|
void yyerror(const char *str)
|
|
|
|
{
|
|
|
|
KexiDBDbg << "error: " << str << endl;
|
|
|
|
KexiDBDbg << "at character " << current << " near tooken " << ctoken << endl;
|
|
|
|
parser->setOperation(Parser::OP_Error);
|
|
|
|
|
|
|
|
const bool otherError = (qstrnicmp(str, "other error", 11)==0);
|
|
|
|
|
|
|
|
if (parser->error().type().isEmpty()
|
|
|
|
&& (str==0 || strlen(str)==0
|
|
|
|
|| qstrnicmp(str, "syntax error", 12)==0 || qstrnicmp(str, "parse error", 11)==0)
|
|
|
|
|| otherError)
|
|
|
|
{
|
|
|
|
KexiDBDbg << parser->statement() << endl;
|
|
|
|
TQString ptrline = "";
|
|
|
|
for(int i=0; i < current; i++)
|
|
|
|
ptrline += " ";
|
|
|
|
|
|
|
|
ptrline += "^";
|
|
|
|
|
|
|
|
KexiDBDbg << ptrline << endl;
|
|
|
|
|
|
|
|
//lexer may add error messages
|
|
|
|
TQString lexerErr = parser->error().error();
|
|
|
|
|
|
|
|
TQString errtypestr(str);
|
|
|
|
if (lexerErr.isEmpty()) {
|
|
|
|
#if 0
|
|
|
|
if (errtypestr.startsWith("parse error, unexpected ")) {
|
|
|
|
//something like "parse error, unexpected IDENTIFIER, expecting ',' or ')'"
|
|
|
|
TQString e = errtypestr.mid(24);
|
|
|
|
KexiDBDbg << e <<endl;
|
|
|
|
TQString token = "IDENTIFIER";
|
|
|
|
if (e.startsWith(token)) {
|
|
|
|
TQRegExp re("'.'");
|
|
|
|
int pos=0;
|
|
|
|
pos = re.search(e, pos);
|
|
|
|
TQStringList captured=re.capturedTexts();
|
|
|
|
if (captured.count()>=2) {
|
|
|
|
// KexiDBDbg << "**" << captured.at(1) << endl;
|
|
|
|
// KexiDBDbg << "**" << captured.at(2) << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// IDENTIFIER, expecting '")) {
|
|
|
|
e = errtypestr.mid(47);
|
|
|
|
KexiDBDbg << e <<endl;
|
|
|
|
// ,' or ')'
|
|
|
|
// lexerErr i18n("identifier was expected");
|
|
|
|
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
if (errtypestr.startsWith("parse error, expecting `IDENTIFIER'"))
|
|
|
|
lexerErr = i18n("identifier was expected");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!otherError) {
|
|
|
|
if (!lexerErr.isEmpty())
|
|
|
|
lexerErr.prepend(": ");
|
|
|
|
|
|
|
|
if (parser->isReservedKeyword(ctoken.latin1()))
|
|
|
|
parser->setError( ParserError(i18n("Syntax Error"),
|
|
|
|
i18n("\"%1\" is a reserved keyword").arg(ctoken)+lexerErr, ctoken, current) );
|
|
|
|
else
|
|
|
|
parser->setError( ParserError(i18n("Syntax Error"),
|
|
|
|
i18n("Syntax Error near \"%1\"").arg(ctoken)+lexerErr, ctoken, current) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void setError(const TQString& errName, const TQString& errDesc)
|
|
|
|
{
|
|
|
|
parser->setError( ParserError(errName, errDesc, ctoken, current) );
|
|
|
|
yyerror(errName.latin1());
|
|
|
|
}
|
|
|
|
|
|
|
|
void setError(const TQString& errDesc)
|
|
|
|
{
|
|
|
|
setError("other error", errDesc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* this is better than assert() */
|
|
|
|
#define IMPL_ERROR(errmsg) setError("Implementation error", errmsg)
|
|
|
|
|
|
|
|
bool parseData(Parser *p, const char *data)
|
|
|
|
{
|
|
|
|
/* todo: make this REENTRANT */
|
|
|
|
parser = p;
|
|
|
|
parser->clear();
|
|
|
|
field = 0;
|
|
|
|
fieldList.clear();
|
|
|
|
// requiresTable = false;
|
|
|
|
|
|
|
|
if (!data) {
|
|
|
|
ParserError err(i18n("Error"), i18n("No query specified"), ctoken, current);
|
|
|
|
parser->setError(err);
|
|
|
|
yyerror("");
|
|
|
|
parser = 0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
tokenize(data);
|
|
|
|
if (!parser->error().type().isEmpty()) {
|
|
|
|
parser = 0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
yyparse();
|
|
|
|
|
|
|
|
bool ok = true;
|
|
|
|
if(parser->operation() == Parser::OP_Select)
|
|
|
|
{
|
|
|
|
KexiDBDbg << "parseData(): ok" << endl;
|
|
|
|
// KexiDBDbg << "parseData(): " << tableDict.count() << " loaded tables" << endl;
|
|
|
|
/* TableSchema *ts;
|
|
|
|
for(TQDictIterator<TableSchema> it(tableDict); TableSchema *s = tableList.first(); s; s = tableList.next())
|
|
|
|
{
|
|
|
|
KexiDBDbg << " " << s->name() << endl;
|
|
|
|
}*/
|
|
|
|
/*removed
|
|
|
|
Field::ListIterator it = parser->select()->fieldsIterator();
|
|
|
|
for(Field *item; (item = it.current()); ++it)
|
|
|
|
{
|
|
|
|
if(tableList.findRef(item->table()) == -1)
|
|
|
|
{
|
|
|
|
ParserError err(i18n("Field List Error"), i18n("Unknown table '%1' in field list").arg(item->table()->name()), ctoken, current);
|
|
|
|
parser->setError(err);
|
|
|
|
|
|
|
|
yyerror("fieldlisterror");
|
|
|
|
ok = false;
|
|
|
|
}
|
|
|
|
}*/
|
|
|
|
//take the dummy table out of the query
|
|
|
|
// parser->select()->removeTable(dummy);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ok = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// tableDict.clear();
|
|
|
|
parser = 0;
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Adds \a column to \a querySchema. \a column can be in a form of
|
|
|
|
table.field, tableAlias.field or field
|
|
|
|
*/
|
|
|
|
bool addColumn( ParseInfo& parseInfo, BaseExpr* columnExpr )
|
|
|
|
{
|
|
|
|
if (!columnExpr->validate(parseInfo)) {
|
|
|
|
setError(parseInfo.errMsg, parseInfo.errDescr);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
VariableExpr *v_e = columnExpr->toVariable();
|
|
|
|
if (columnExpr->exprClass() == KexiDBExpr_Variable && v_e) {
|
|
|
|
//it's a variable:
|
|
|
|
if (v_e->name=="*") {//all tables asterisk
|
|
|
|
if (parseInfo.querySchema->tables()->isEmpty()) {
|
|
|
|
setError(i18n("\"*\" could not be used if no tables are specified"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
parseInfo.querySchema->addAsterisk( new QueryAsterisk(parseInfo.querySchema) );
|
|
|
|
}
|
|
|
|
else if (v_e->tableForQueryAsterisk) {//one-table asterisk
|
|
|
|
parseInfo.querySchema->addAsterisk(
|
|
|
|
new QueryAsterisk(parseInfo.querySchema, v_e->tableForQueryAsterisk) );
|
|
|
|
}
|
|
|
|
else if (v_e->field) {//"table.field" or "field" (bound to a table or not)
|
|
|
|
parseInfo.querySchema->addField(v_e->field, v_e->tablePositionForField);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
IMPL_ERROR("addColumn(): unknown case!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//it's complex expression
|
|
|
|
parseInfo.querySchema->addExpression(columnExpr);
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
KexiDBDbg << "found variable name: " << varName << endl;
|
|
|
|
int dotPos = varName.find('.');
|
|
|
|
TQString tableName, fieldName;
|
|
|
|
//TODO: shall we also support db name?
|
|
|
|
if (dotPos>0) {
|
|
|
|
tableName = varName.left(dotPos);
|
|
|
|
fieldName = varName.mid(dotPos+1);
|
|
|
|
}
|
|
|
|
if (tableName.isEmpty()) {//fieldname only
|
|
|
|
fieldName = varName;
|
|
|
|
if (fieldName=="*") {
|
|
|
|
parseInfo.querySchema->addAsterisk( new QueryAsterisk(parseInfo.querySchema) );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
//find first table that has this field
|
|
|
|
Field *firstField = 0;
|
|
|
|
for (TableSchema::ListIterator it(*parseInfo.querySchema->tables()); it.current(); ++it) {
|
|
|
|
Field *f = it.current()->field(fieldName);
|
|
|
|
if (f) {
|
|
|
|
if (!firstField) {
|
|
|
|
firstField = f;
|
|
|
|
} else if (f->table()!=firstField->table()) {
|
|
|
|
//ambiguous field name
|
|
|
|
setError(i18n("Ambiguous field name"),
|
|
|
|
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) {
|
|
|
|
setError(i18n("Field not found"),
|
|
|
|
i18n("Table containing \"%1\" field not found").arg(fieldName));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
//ok
|
|
|
|
parseInfo.querySchema->addField(firstField);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {//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) {
|
|
|
|
setError(i18n("Could not access the table directly using its name"),
|
|
|
|
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) {
|
|
|
|
TQValueList<int> *positionsList = repeatedTablesAndAliases[ tableName ];
|
|
|
|
if (!positionsList) {
|
|
|
|
IMPL_ERROR(tableName + "." + fieldName + ", !positionsList ");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fieldName=="*") {
|
|
|
|
if (positionsList->count()>1) {
|
|
|
|
setError(i18n("Ambiguous \"%1.*\" expression").arg(tableName),
|
|
|
|
i18n("More than one \"%1\" table or alias defined").arg(tableName));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
parseInfo.querySchema->addAsterisk( new QueryAsterisk(parseInfo.querySchema, ts) );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// KexiDBDbg << " --it's a table.name" << endl;
|
|
|
|
Field *realField = ts->field(fieldName);
|
|
|
|
if (realField) {
|
|
|
|
// 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) {
|
|
|
|
setError(i18n("Ambiguous \"%1.%2\" expression").arg(tableName).arg(fieldName),
|
|
|
|
i18n("More than one \"%1\" table or alias defined containing \"%2\" field")
|
|
|
|
.arg(tableName).arg(fieldName));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
parseInfo.querySchema->addField(realField, tablePosition);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
setError(i18n("Field not found"), i18n("Table \"%1\" has no \"%2\" field")
|
|
|
|
.arg(tableName).arg(fieldName));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
tableNotFoundError(tableName);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! clean up no longer needed temporary objects
|
|
|
|
#define CLEANUP \
|
|
|
|
delete colViews; \
|
|
|
|
delete tablesList; \
|
|
|
|
delete options
|
|
|
|
|
|
|
|
QuerySchema* buildSelectQuery(
|
|
|
|
QuerySchema* querySchema, NArgExpr* colViews, NArgExpr* tablesList,
|
|
|
|
SelectOptionsInternal* options )
|
|
|
|
{
|
|
|
|
ParseInfo parseInfo(querySchema);
|
|
|
|
|
|
|
|
//-------tables list
|
|
|
|
// assert( tablesList ); //&& tablesList->exprClass() == KexiDBExpr_TableList );
|
|
|
|
|
|
|
|
uint columnNum = 0;
|
|
|
|
/*TODO: use this later if there are columns that use database fields,
|
|
|
|
e.g. "SELECT 1 from table1 t, table2 t") is ok however. */
|
|
|
|
//used to collect information about first repeated table name or alias:
|
|
|
|
// TQDict<char> tableNamesAndTableAliases(997, false);
|
|
|
|
// TQString repeatedTableNameOrTableAlias;
|
|
|
|
if (tablesList) {
|
|
|
|
for (int i=0; i<tablesList->args(); i++, columnNum++) {
|
|
|
|
BaseExpr *e = tablesList->arg(i);
|
|
|
|
VariableExpr* t_e = 0;
|
|
|
|
TQCString aliasString;
|
|
|
|
if (e->exprClass() == KexiDBExpr_SpecialBinary) {
|
|
|
|
BinaryExpr* t_with_alias = e->toBinary();
|
|
|
|
assert(t_with_alias);
|
|
|
|
assert(t_with_alias->left()->exprClass() == KexiDBExpr_Variable);
|
|
|
|
assert(t_with_alias->right()->exprClass() == KexiDBExpr_Variable
|
|
|
|
&& (t_with_alias->token()==AS || t_with_alias->token()==0));
|
|
|
|
t_e = t_with_alias->left()->toVariable();
|
|
|
|
aliasString = t_with_alias->right()->toVariable()->name.latin1();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
t_e = e->toVariable();
|
|
|
|
}
|
|
|
|
assert(t_e);
|
|
|
|
TQCString tname = t_e->name.latin1();
|
|
|
|
TableSchema *s = parser->db()->tableSchema(TQString(tname));
|
|
|
|
if(!s) {
|
|
|
|
setError(//i18n("Field List Error"),
|
|
|
|
i18n("Table \"%1\" does not exist").arg(TQString(tname)));
|
|
|
|
// yyerror("fieldlisterror");
|
|
|
|
CLEANUP;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
TQCString tableOrAliasName;
|
|
|
|
if (!aliasString.isEmpty()) {
|
|
|
|
tableOrAliasName = aliasString;
|
|
|
|
// KexiDBDbg << "- add alias for table: " << aliasString << endl;
|
|
|
|
} else {
|
|
|
|
tableOrAliasName = tname;
|
|
|
|
}
|
|
|
|
// 1. collect information about first repeated table name or alias
|
|
|
|
// (potential ambiguity)
|
|
|
|
TQValueList<int> *list = parseInfo.repeatedTablesAndAliases[tableOrAliasName];
|
|
|
|
if (list) {
|
|
|
|
//another table/alias with the same name
|
|
|
|
list->append( i );
|
|
|
|
// KexiDBDbg << "- another table/alias with name: " << tableOrAliasName << endl;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
list = new TQValueList<int>();
|
|
|
|
list->append( i );
|
|
|
|
parseInfo.repeatedTablesAndAliases.insert( tableOrAliasName, list );
|
|
|
|
// KexiDBDbg << "- first table/alias with name: " << tableOrAliasName << endl;
|
|
|
|
}
|
|
|
|
/* if (repeatedTableNameOrTableAlias.isEmpty()) {
|
|
|
|
if (tableNamesAndTableAliases[tname])
|
|
|
|
repeatedTableNameOrTableAlias=tname;
|
|
|
|
else
|
|
|
|
tableNamesAndTableAliases.insert(tname, (const char*)1);
|
|
|
|
}
|
|
|
|
if (!aliasString.isEmpty()) {
|
|
|
|
KexiDBDbg << "- add alias for table: " << aliasString << endl;
|
|
|
|
// querySchema->setTableAlias(columnNum, aliasString);
|
|
|
|
//2. collect information about first repeated table name or alias
|
|
|
|
// (potential ambiguity)
|
|
|
|
if (repeatedTableNameOrTableAlias.isEmpty()) {
|
|
|
|
if (tableNamesAndTableAliases[aliasString])
|
|
|
|
repeatedTableNameOrTableAlias=aliasString;
|
|
|
|
else
|
|
|
|
tableNamesAndTableAliases.insert(aliasString, (const char*)1);
|
|
|
|
}
|
|
|
|
}*/
|
|
|
|
// KexiDBDbg << "addTable: " << tname << endl;
|
|
|
|
querySchema->addTable( s, aliasString );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set parent table if there's only one */
|
|
|
|
// if (parser->select()->tables()->count()==1)
|
|
|
|
if (querySchema->tables()->count()==1)
|
|
|
|
querySchema->setMasterTable(querySchema->tables()->first());
|
|
|
|
|
|
|
|
//-------add fields
|
|
|
|
if (colViews) {
|
|
|
|
BaseExpr *e;
|
|
|
|
columnNum = 0;
|
|
|
|
const uint colCount = colViews->list.count();
|
|
|
|
// for (BaseExpr::ListIterator it(colViews->list);(e = it.current()); columnNum++)
|
|
|
|
colViews->list.first();
|
|
|
|
for (; columnNum<colCount; columnNum++) {
|
|
|
|
e = colViews->list.current();
|
|
|
|
bool moveNext = true; //used to avoid ++it when an item is taken from the list
|
|
|
|
BaseExpr *columnExpr = e;
|
|
|
|
VariableExpr* aliasVariable = 0;
|
|
|
|
if (e->exprClass() == KexiDBExpr_SpecialBinary && e->toBinary()
|
|
|
|
&& (e->token()==AS || e->token()==0))
|
|
|
|
{
|
|
|
|
//KexiDBExpr_SpecialBinary: with alias
|
|
|
|
columnExpr = e->toBinary()->left();
|
|
|
|
// isFieldWithAlias = true;
|
|
|
|
aliasVariable = e->toBinary()->right()->toVariable();
|
|
|
|
if (!aliasVariable) {
|
|
|
|
setError(i18n("Invalid alias definition for column \"%1\"")
|
|
|
|
.arg(columnExpr->toString())); //ok?
|
|
|
|
CLEANUP;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const int c = columnExpr->exprClass();
|
|
|
|
const bool isExpressionField =
|
|
|
|
c == KexiDBExpr_Const
|
|
|
|
|| c == KexiDBExpr_Unary
|
|
|
|
|| c == KexiDBExpr_Arithm
|
|
|
|
|| c == KexiDBExpr_Logical
|
|
|
|
|| c == KexiDBExpr_Relational
|
|
|
|
|| c == KexiDBExpr_Const
|
|
|
|
|| c == KexiDBExpr_Function
|
|
|
|
|| c == KexiDBExpr_Aggregation;
|
|
|
|
|
|
|
|
if (c == KexiDBExpr_Variable) {
|
|
|
|
//just a variable, do nothing, addColumn() will handle this
|
|
|
|
}
|
|
|
|
else if (isExpressionField) {
|
|
|
|
//expression object will be reused, take, will be owned, do not destroy
|
|
|
|
// KexiDBDbg << colViews->list.count() << " " << it.current()->debugString() << endl;
|
|
|
|
colViews->list.take(); //take() doesn't work
|
|
|
|
moveNext = false;
|
|
|
|
}
|
|
|
|
else if (aliasVariable) {
|
|
|
|
//take first (left) argument of the special binary expr, will be owned, do not destroy
|
|
|
|
e->toBinary()->m_larg = 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
setError(i18n("Invalid \"%1\" column definition").arg(e->toString())); //ok?
|
|
|
|
CLEANUP;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!addColumn( parseInfo, columnExpr )) {
|
|
|
|
CLEANUP;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aliasVariable) {
|
|
|
|
// KexiDBDbg << "ALIAS \"" << aliasVariable->name << "\" set for column "
|
|
|
|
// << columnNum << endl;
|
|
|
|
querySchema->setColumnAlias(columnNum, aliasVariable->name.latin1());
|
|
|
|
}
|
|
|
|
/* if (e->exprClass() == KexiDBExpr_SpecialBinary && dynamic_cast<BinaryExpr*>(e)
|
|
|
|
&& (e->type()==AS || e->type()==0))
|
|
|
|
{
|
|
|
|
//also add alias
|
|
|
|
VariableExpr* aliasVariable =
|
|
|
|
dynamic_cast<VariableExpr*>(dynamic_cast<BinaryExpr*>(e)->right());
|
|
|
|
if (!aliasVariable) {
|
|
|
|
setError(i18n("Invalid column alias definition")); //ok?
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
kdDebug() << "ALIAS \"" << aliasVariable->name << "\" set for column "
|
|
|
|
<< columnNum << endl;
|
|
|
|
querySchema->setColumnAlias(columnNum, aliasVariable->name.latin1());
|
|
|
|
}*/
|
|
|
|
|
|
|
|
if (moveNext) {
|
|
|
|
colViews->list.next();
|
|
|
|
// ++it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//----- SELECT options
|
|
|
|
if (options) {
|
|
|
|
//----- WHERE expr.
|
|
|
|
if (options->whereExpr) {
|
|
|
|
if (!options->whereExpr->validate(parseInfo)) {
|
|
|
|
setError(parseInfo.errMsg, parseInfo.errDescr);
|
|
|
|
CLEANUP;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
querySchema->setWhereExpression(options->whereExpr);
|
|
|
|
}
|
|
|
|
//----- ORDER BY
|
|
|
|
if (options->orderByColumns) {
|
|
|
|
OrderByColumnList &orderByColumnList = querySchema->orderByColumnList();
|
|
|
|
OrderByColumnInternal::ListConstIterator it = options->orderByColumns->constEnd();
|
|
|
|
uint count = options->orderByColumns->count();
|
|
|
|
--it;
|
|
|
|
for (;count>0; --it, --count)
|
|
|
|
/*opposite direction due to parser specifics*/
|
|
|
|
{
|
|
|
|
//first, try to find a column name or alias (outside of asterisks)
|
|
|
|
QueryColumnInfo *columnInfo = querySchema->columnInfo( (*it).aliasOrName, false/*outside of asterisks*/ );
|
|
|
|
if (columnInfo) {
|
|
|
|
orderByColumnList.appendColumn( *columnInfo, (*it).ascending );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
//failed, try to find a field name within all the tables
|
|
|
|
if ((*it).columnNumber != -1) {
|
|
|
|
if (!orderByColumnList.appendColumn( *querySchema,
|
|
|
|
(*it).ascending, (*it).columnNumber-1 ))
|
|
|
|
{
|
|
|
|
setError(i18n("Could not define sorting - no column at position %1")
|
|
|
|
.arg((*it).columnNumber));
|
|
|
|
CLEANUP;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Field * f = querySchema->findTableField((*it).aliasOrName);
|
|
|
|
if (!f) {
|
|
|
|
setError(i18n("Could not define sorting - "
|
|
|
|
"column name or alias \"%1\" does not exist").arg((*it).aliasOrName));
|
|
|
|
CLEANUP;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
orderByColumnList.appendField( *f, (*it).ascending );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// KexiDBDbg << "Select ColViews=" << (colViews ? colViews->debugString() : TQString())
|
|
|
|
// << " Tables=" << (tablesList ? tablesList->debugString() : TQString()) << endl;
|
|
|
|
|
|
|
|
CLEANUP;
|
|
|
|
return querySchema;
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef CLEANUP
|
|
|
|
|