kexi

parser_p.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include "parser_p.h"
00021 #include "sqlparser.h"
00022 
00023 #include <kdebug.h>
00024 #include <klocale.h>
00025 
00026 #include <qregexp.h>
00027 
00028 #include <assert.h>
00029 
00030 using namespace KexiDB;
00031 
00032 Parser *parser;
00033 Field *field;
00034 //bool requiresTable;
00035 QPtrList<Field> fieldList;
00036 int current = 0;
00037 QString ctoken = "";
00038 
00039 //-------------------------------------
00040 
00041 ParserPrivate::ParserPrivate()
00042  : reservedKeywords(997, 997, false)
00043  , initialized(false)
00044 {
00045     clear();
00046     table = 0;
00047     select = 0;
00048     db = 0;
00049 }
00050 
00051 ParserPrivate::~ParserPrivate()
00052 {
00053     delete select;
00054     delete table;
00055 }
00056 
00057 void ParserPrivate::clear()
00058 {
00059     operation = Parser::OP_None;
00060     error = ParserError();
00061 }
00062 
00063 //-------------------------------------
00064 
00065 ParseInfo::ParseInfo(KexiDB::QuerySchema *query)
00066  : repeatedTablesAndAliases(997, false)
00067  , querySchema(query)
00068 {
00069     repeatedTablesAndAliases.setAutoDelete(true);
00070 }
00071 
00072 ParseInfo::~ParseInfo()
00073 {
00074 }
00075 
00076 //-------------------------------------
00077 
00078 extern int yyparse();
00079 extern void tokenize(const char *data);
00080 
00081 void yyerror(const char *str)
00082 {
00083     KexiDBDbg << "error: " << str << endl;
00084     KexiDBDbg << "at character " << current << " near tooken " << ctoken << endl;
00085     parser->setOperation(Parser::OP_Error);
00086 
00087     const bool otherError = (qstrnicmp(str, "other error", 11)==0);
00088     
00089     if (parser->error().type().isEmpty() 
00090         && (str==0 || strlen(str)==0 
00091         || qstrnicmp(str, "syntax error", 12)==0 || qstrnicmp(str, "parse error", 11)==0)
00092         || otherError)
00093     {
00094         KexiDBDbg << parser->statement() << endl;
00095         QString ptrline = "";
00096         for(int i=0; i < current; i++)
00097             ptrline += " ";
00098 
00099         ptrline += "^";
00100 
00101         KexiDBDbg << ptrline << endl;
00102 
00103         //lexer may add error messages
00104         QString lexerErr = parser->error().error();
00105 
00106         QString errtypestr(str);
00107         if (lexerErr.isEmpty()) {
00108 #if 0
00109             if (errtypestr.startsWith("parse error, unexpected ")) {
00110                 //something like "parse error, unexpected IDENTIFIER, expecting ',' or ')'"
00111                 QString e = errtypestr.mid(24);
00112                 KexiDBDbg << e <<endl;
00113                 QString token = "IDENTIFIER";
00114                 if (e.startsWith(token)) {
00115                     QRegExp re("'.'");
00116                     int pos=0;
00117                     pos = re.search(e, pos);
00118                     QStringList captured=re.capturedTexts();
00119                     if (captured.count()>=2) {
00120 //                      KexiDBDbg << "**" << captured[1] << endl;
00121 //                      KexiDBDbg << "**" << captured[2] << endl;
00122                     }
00123                 }
00124                     
00125                     
00126                     
00127 //           IDENTIFIER, expecting '")) {
00128                 e = errtypestr.mid(47);
00129                 KexiDBDbg << e <<endl;
00130 //              ,' or ')'
00131 //      lexerErr i18n("identifier was expected");
00132                 
00133             } else 
00134 #endif
00135             if (errtypestr.startsWith("parse error, expecting `IDENTIFIER'"))
00136                 lexerErr = i18n("identifier was expected");
00137         }
00138         
00139         if (!otherError) {
00140             if (!lexerErr.isEmpty())
00141                 lexerErr.prepend(": ");
00142 
00143             if (parser->isReservedKeyword(ctoken.latin1()))
00144                 parser->setError( ParserError(i18n("Syntax Error"), 
00145                     i18n("\"%1\" is a reserved keyword").arg(ctoken)+lexerErr, ctoken, current) );
00146             else
00147                 parser->setError( ParserError(i18n("Syntax Error"), 
00148                     i18n("Syntax Error near \"%1\"").arg(ctoken)+lexerErr, ctoken, current) );
00149         }
00150     }
00151 }
00152 
00153 void setError(const QString& errName, const QString& errDesc)
00154 {
00155     parser->setError( ParserError(errName, errDesc, ctoken, current) );
00156     yyerror(errName.latin1());
00157 }
00158 
00159 void setError(const QString& errDesc)
00160 {
00161     setError("other error", errDesc);
00162 }
00163 
00164 /* this is better than assert() */
00165 #define IMPL_ERROR(errmsg) setError("Implementation error", errmsg)
00166 
00167 bool parseData(Parser *p, const char *data)
00168 {
00169 /* todo: make this REENTRANT */
00170     parser = p;
00171     parser->clear();
00172     field = 0;
00173     fieldList.clear();
00174 //  requiresTable = false;
00175 
00176     if (!data) {
00177         ParserError err(i18n("Error"), i18n("No query specified"), ctoken, current);
00178         parser->setError(err);
00179         yyerror("");
00180         parser = 0;
00181         return false;
00182     }
00183 
00184     tokenize(data);
00185     if (!parser->error().type().isEmpty()) {
00186         parser = 0;
00187         return false;
00188     }
00189     yyparse();
00190 
00191     bool ok = true;
00192     if(parser->operation() == Parser::OP_Select)
00193     {
00194         KexiDBDbg << "parseData(): ok" << endl;
00195 //          KexiDBDbg << "parseData(): " << tableDict.count() << " loaded tables" << endl;
00196 /*          TableSchema *ts;
00197             for(QDictIterator<TableSchema> it(tableDict); TableSchema *s = tableList.first(); s; s = tableList.next())
00198             {
00199                 KexiDBDbg << "  " << s->name() << endl;
00200             }*/
00201 /*removed
00202             Field::ListIterator it = parser->select()->fieldsIterator();
00203             for(Field *item; (item = it.current()); ++it)
00204             {
00205                 if(tableList.findRef(item->table()) == -1)
00206                 {
00207                     ParserError err(i18n("Field List Error"), i18n("Unknown table '%1' in field list").arg(item->table()->name()), ctoken, current);
00208                     parser->setError(err);
00209 
00210                     yyerror("fieldlisterror");
00211                     ok = false;
00212                 }
00213             }*/
00214             //take the dummy table out of the query
00215 //          parser->select()->removeTable(dummy);
00216     }
00217     else {
00218         ok = false;
00219     }
00220 
00221 //      tableDict.clear();
00222     parser = 0;
00223     return ok;
00224 }
00225 
00226     
00227 /* Adds \a column to \a querySchema. \a column can be in a form of
00228  table.field, tableAlias.field or field
00229 */
00230 bool addColumn( ParseInfo& parseInfo, BaseExpr* columnExpr )
00231 {
00232     if (!columnExpr->validate(parseInfo)) {
00233         setError(parseInfo.errMsg, parseInfo.errDescr);
00234         return false;
00235     }
00236 
00237     VariableExpr *v_e = columnExpr->toVariable();
00238     if (columnExpr->exprClass() == KexiDBExpr_Variable && v_e) {
00239         //it's a variable:
00240         if (v_e->name=="*") {//all tables asterisk
00241             if (parseInfo.querySchema->tables()->isEmpty()) {
00242                 setError(i18n("\"*\" could not be used if no tables are specified"));
00243                 return false;
00244             }
00245             parseInfo.querySchema->addAsterisk( new QueryAsterisk(parseInfo.querySchema) );
00246         }
00247         else if (v_e->tableForQueryAsterisk) {//one-table asterisk
00248             parseInfo.querySchema->addAsterisk( 
00249                 new QueryAsterisk(parseInfo.querySchema, v_e->tableForQueryAsterisk) );
00250         }
00251         else if (v_e->field) {//"table.field" or "field" (bound to a table or not)
00252             parseInfo.querySchema->addField(v_e->field, v_e->tablePositionForField);
00253         }
00254         else {
00255             IMPL_ERROR("addColumn(): unknown case!");
00256             return false;
00257         }
00258         return true;
00259     }
00260 
00261     //it's complex expression
00262     Field *field = new Field(parseInfo.querySchema, columnExpr);
00263     parseInfo.querySchema->addField(field);
00264 
00265 #if 0
00266     KexiDBDbg << "found variable name: " << varName << endl;
00267     int dotPos = varName.find('.');
00268     QString tableName, fieldName;
00269 //TODO: shall we also support db name?
00270     if (dotPos>0) {
00271         tableName = varName.left(dotPos);
00272         fieldName = varName.mid(dotPos+1);
00273     }
00274     if (tableName.isEmpty()) {//fieldname only
00275         fieldName = varName;
00276         if (fieldName=="*") {
00277             parseInfo.querySchema->addAsterisk( new QueryAsterisk(parseInfo.querySchema) );
00278         }
00279         else {
00280             //find first table that has this field
00281             Field *firstField = 0;
00282             for (TableSchema::ListIterator it(*parseInfo.querySchema->tables()); it.current(); ++it) {
00283                 Field *f = it.current()->field(fieldName);
00284                 if (f) {
00285                     if (!firstField) {
00286                         firstField = f;
00287                     } else if (f->table()!=firstField->table()) {
00288                         //ambiguous field name
00289                         setError(i18n("Ambiguous field name"), 
00290                             i18n("Both table \"%1\" and \"%2\" have defined \"%3\" field. "
00291                                 "Use \"<tableName>.%4\" notation to specify table name.")
00292                                 .arg(firstField->table()->name()).arg(f->table()->name())
00293                                 .arg(fieldName).arg(fieldName));
00294                         return false;
00295                     }
00296                 }
00297             }
00298             if (!firstField) {
00299                     setError(i18n("Field not found"), 
00300                         i18n("Table containing \"%1\" field not found").arg(fieldName));
00301                     return false;
00302             }
00303             //ok
00304             parseInfo.querySchema->addField(firstField);
00305         }
00306     }
00307     else {//table.fieldname or tableAlias.fieldname
00308         tableName = tableName.lower();
00309         TableSchema *ts = parseInfo.querySchema->table( tableName );
00310         if (ts) {//table.fieldname
00311             //check if "table" is covered by an alias
00312             const QValueList<int> tPositions = parseInfo.querySchema->tablePositions(tableName);
00313             QValueList<int>::ConstIterator it = tPositions.constBegin();
00314             QCString tableAlias;
00315             bool covered = true;
00316             for (; it!=tPositions.constEnd() && covered; ++it) {
00317                 tableAlias = parseInfo.querySchema->tableAlias(*it);
00318                 if (tableAlias.isEmpty() || tableAlias.lower()==tableName.latin1())
00319                     covered = false; //uncovered
00320                 KexiDBDbg << " --" << "covered by " << tableAlias << " alias" << endl;
00321             }
00322             if (covered) {
00323                 setError(i18n("Could not access the table directly using its name"), 
00324                     i18n("Table \"%1\" is covered by aliases. Instead of \"%2\", "
00325                     "you can write \"%3\"").arg(tableName)
00326                     .arg(tableName+"."+fieldName).arg(tableAlias+"."+fieldName.latin1()));
00327                 return false;
00328             }
00329         }
00330         
00331         int tablePosition = -1;
00332         if (!ts) {//try to find tableAlias
00333             tablePosition = parseInfo.querySchema->tablePositionForAlias( tableName.latin1() );
00334             if (tablePosition>=0) {
00335                 ts = parseInfo.querySchema->tables()->at(tablePosition);
00336                 if (ts) {
00337 //                  KexiDBDbg << " --it's a tableAlias.name" << endl;
00338                 }
00339             }
00340         }
00341 
00342 
00343         if (ts) {
00344             QValueList<int> *positionsList = repeatedTablesAndAliases[ tableName ];
00345             if (!positionsList) {
00346                 IMPL_ERROR(tableName + "." + fieldName + ", !positionsList ");
00347                 return false;
00348             }
00349 
00350             if (fieldName=="*") {
00351                 if (positionsList->count()>1) {
00352                     setError(i18n("Ambiguous \"%1.*\" expression").arg(tableName),
00353                         i18n("More than one \"%1\" table or alias defined").arg(tableName));
00354                     return false;
00355                 }
00356                 parseInfo.querySchema->addAsterisk( new QueryAsterisk(parseInfo.querySchema, ts) );
00357             }
00358             else {
00359 //              KexiDBDbg << " --it's a table.name" << endl;
00360                 Field *realField = ts->field(fieldName);
00361                 if (realField) {
00362                     // check if table or alias is used twice and both have the same column
00363                     // (so the column is ambiguous)
00364                     int numberOfTheSameFields = 0;
00365                     for (QValueList<int>::iterator it = positionsList->begin();
00366                         it!=positionsList->end();++it)
00367                     {
00368                         TableSchema *otherTS = parseInfo.querySchema->tables()->at(*it);
00369                         if (otherTS->field(fieldName))
00370                             numberOfTheSameFields++;
00371                         if (numberOfTheSameFields>1) {
00372                             setError(i18n("Ambiguous \"%1.%2\" expression").arg(tableName).arg(fieldName),
00373                                 i18n("More than one \"%1\" table or alias defined containing \"%2\" field")
00374                                 .arg(tableName).arg(fieldName));
00375                             return false;
00376                         }
00377                     }
00378 
00379                     parseInfo.querySchema->addField(realField, tablePosition);
00380                 }
00381                 else {
00382                     setError(i18n("Field not found"), i18n("Table \"%1\" has no \"%2\" field")
00383                         .arg(tableName).arg(fieldName));
00384                     return false;
00385                 }
00386             }
00387         }
00388         else {
00389             tableNotFoundError(tableName);
00390             return false;
00391         }
00392     }
00393 #endif
00394     return true;
00395 }
00396 
00397 //clean up no longer needed objects
00398 #define CLEANUP \
00399     delete colViews; \
00400     delete tablesList
00401 
00402 QuerySchema* parseSelect( 
00403     QuerySchema* querySchema, NArgExpr* colViews, NArgExpr* tablesList, BaseExpr* whereExpr )
00404 {
00405     ParseInfo parseInfo(querySchema);
00406     
00407     //-------tables list
00408 //  assert( tablesList ); //&& tablesList->exprClass() == KexiDBExpr_TableList );
00409 
00410     uint columnNum = 0;
00411 /*TODO: use this later if there are columns that use database fields, 
00412         e.g. "SELECT 1 from table1 t, table2 t") is ok however. */
00413     //used to collect information about first repeated table name or alias:
00414 //  QDict<char> tableNamesAndTableAliases(997, false);
00415 //  QString repeatedTableNameOrTableAlias;
00416     if (tablesList) {
00417         for (int i=0; i<tablesList->args(); i++, columnNum++) {
00418             BaseExpr *e = tablesList->arg(i);
00419             VariableExpr* t_e = 0;
00420             QCString aliasString;
00421             if (e->exprClass() == KexiDBExpr_SpecialBinary) {
00422                 BinaryExpr* t_with_alias = e->toBinary();
00423                 assert(t_with_alias);
00424                 assert(t_with_alias->left()->exprClass() == KexiDBExpr_Variable);
00425                 assert(t_with_alias->right()->exprClass() == KexiDBExpr_Variable
00426                     && (t_with_alias->token()==AS || t_with_alias->token()==0));
00427                 t_e = t_with_alias->left()->toVariable();
00428                 aliasString = t_with_alias->right()->toVariable()->name.latin1();
00429             }
00430             else {
00431                 t_e = e->toVariable();
00432             }
00433             assert(t_e);
00434             QCString tname = t_e->name.latin1();
00435             TableSchema *s = parser->db()->tableSchema(tname);
00436             if(!s) {
00437                 setError(//i18n("Field List Error"), 
00438                     i18n("Table \"%1\" does not exist").arg(tname));
00439     //          yyerror("fieldlisterror");
00440                 CLEANUP;
00441                 return 0;
00442             }
00443             QCString tableOrAliasName;
00444             if (!aliasString.isEmpty()) {
00445                 tableOrAliasName = aliasString;
00446 //              KexiDBDbg << "- add alias for table: " << aliasString << endl;
00447             } else {
00448                 tableOrAliasName = tname;
00449             }
00450             // 1. collect information about first repeated table name or alias
00451             //    (potential ambiguity)
00452             QValueList<int> *list = parseInfo.repeatedTablesAndAliases[tableOrAliasName];
00453             if (list) {
00454                 //another table/alias with the same name
00455                 list->append( i );
00456 //              KexiDBDbg << "- another table/alias with name: " << tableOrAliasName << endl;
00457             }
00458             else {
00459                 list = new QValueList<int>();
00460                 list->append( i );
00461                 parseInfo.repeatedTablesAndAliases.insert( tableOrAliasName, list );
00462 //              KexiDBDbg << "- first table/alias with name: " << tableOrAliasName << endl;
00463             }
00464     /*      if (repeatedTableNameOrTableAlias.isEmpty()) {
00465                 if (tableNamesAndTableAliases[tname])
00466                     repeatedTableNameOrTableAlias=tname;
00467                 else
00468                     tableNamesAndTableAliases.insert(tname, (const char*)1);
00469             }
00470             if (!aliasString.isEmpty()) {
00471                 KexiDBDbg << "- add alias for table: " << aliasString << endl;
00472     //          querySchema->setTableAlias(columnNum, aliasString);
00473                 //2. collect information about first repeated table name or alias
00474                 //   (potential ambiguity)
00475                 if (repeatedTableNameOrTableAlias.isEmpty()) {
00476                     if (tableNamesAndTableAliases[aliasString])
00477                         repeatedTableNameOrTableAlias=aliasString;
00478                     else
00479                         tableNamesAndTableAliases.insert(aliasString, (const char*)1);
00480                 }
00481             }*/
00482 //          KexiDBDbg << "addTable: " << tname << endl;
00483             querySchema->addTable( s, aliasString );
00484         }
00485     }
00486 
00487     /* set parent table if there's only one */
00488 //  if (parser->select()->tables()->count()==1)
00489     if (querySchema->tables()->count()==1)
00490         querySchema->setMasterTable(querySchema->tables()->first());
00491 
00492     //-------add fields
00493     if (colViews) {
00494         BaseExpr *e;
00495         columnNum = 0;
00496         const uint colCount = colViews->list.count();
00497 //      for (BaseExpr::ListIterator it(colViews->list);(e = it.current()); columnNum++)
00498         colViews->list.first();
00499         for (; columnNum<colCount; columnNum++) {
00500             e = colViews->list.current();
00501             bool moveNext = true; //used to avoid ++it when an item is taken from the list
00502             BaseExpr *columnExpr = e;
00503             VariableExpr* aliasVariable = 0;
00504             if (e->exprClass() == KexiDBExpr_SpecialBinary && e->toBinary()
00505                 && (e->token()==AS || e->token()==0))
00506             {
00507                 //KexiDBExpr_SpecialBinary: with alias
00508                 columnExpr = e->toBinary()->left();
00509     //          isFieldWithAlias = true;
00510                 aliasVariable = e->toBinary()->right()->toVariable();
00511                 if (!aliasVariable) {
00512                     setError(i18n("Invalid alias definition for column \"%1\"")
00513                         .arg(columnExpr->toString())); //ok?
00514                     CLEANUP;
00515                     return 0;
00516                 }
00517             }
00518     
00519             const int c = columnExpr->exprClass();
00520             const bool isExpressionField = 
00521                     c == KexiDBExpr_Const
00522                 || c == KexiDBExpr_Unary
00523                 || c == KexiDBExpr_Arithm
00524                 || c == KexiDBExpr_Logical
00525                 || c == KexiDBExpr_Relational
00526                 || c == KexiDBExpr_Const
00527                 || c == KexiDBExpr_Function
00528                 || c == KexiDBExpr_Aggregation;
00529     
00530             if (c == KexiDBExpr_Variable) {
00531                 //just a variable, do nothing, addColumn() will handle this
00532             }
00533             else if (isExpressionField) {
00534                 //expression object will be reused, take, will be owned, do not destroy
00535 //      KexiDBDbg << colViews->list.count() << " " << it.current()->debugString() << endl;
00536                 colViews->list.take(); //take() doesn't work
00537                 moveNext = false;
00538             }
00539             else if (aliasVariable) {
00540                 //take first (left) argument of the special binary expr, will be owned, do not destroy
00541                 e->toBinary()->m_larg = 0;
00542             }
00543             else {
00544                 setError(i18n("Invalid \"%1\" column definition").arg(e->toString())); //ok?
00545                 CLEANUP;
00546                 return 0;
00547             }
00548     
00549             if (!addColumn( parseInfo, columnExpr )) {
00550                 CLEANUP;
00551                 return 0;
00552             }
00553             
00554             if (aliasVariable) {
00555 //              KexiDBDbg << "ALIAS \"" << aliasVariable->name << "\" set for column " 
00556 //                  << columnNum << endl;
00557                 querySchema->setColumnAlias(columnNum, aliasVariable->name.latin1());
00558             }
00559     /*      if (e->exprClass() == KexiDBExpr_SpecialBinary && dynamic_cast<BinaryExpr*>(e)
00560                 && (e->type()==AS || e->type()==0))
00561             {
00562                 //also add alias
00563                 VariableExpr* aliasVariable =
00564                     dynamic_cast<VariableExpr*>(dynamic_cast<BinaryExpr*>(e)->right());
00565                 if (!aliasVariable) {
00566                     setError(i18n("Invalid column alias definition")); //ok?
00567                     return 0;
00568                 }
00569                 kdDebug() << "ALIAS \"" << aliasVariable->name << "\" set for column " 
00570                     << columnNum << endl;
00571                 querySchema->setColumnAlias(columnNum, aliasVariable->name.latin1());
00572             }*/
00573     
00574             if (moveNext) {
00575                 colViews->list.next();
00576 //              ++it;
00577             }
00578         }
00579     }
00580     //----- WHERE expr.
00581     if (whereExpr) {
00582         if (!whereExpr->validate(parseInfo)) {
00583             setError(parseInfo.errMsg, parseInfo.errDescr);
00584             CLEANUP;
00585             return false;
00586         }
00587         querySchema->setWhereExpression(whereExpr);
00588     }
00589 
00590 //  KexiDBDbg << "Select ColViews=" << (colViews ? colViews->debugString() : QString::null)
00591 //      << " Tables=" << (tablesList ? tablesList->debugString() : QString::null) << endl;
00592     
00593     CLEANUP;
00594     return querySchema;
00595 }
00596 
00597 #undef CLEANUP
00598 
KDE Home | KDE Accessibility Home | Description of Access Keys