File indexing completed on 2024-12-01 13:00:14
0001 /* This file is part of the KDE project 0002 Copyright (C) 2004-2017 Jarosław Staniek <staniek@kde.org> 0003 0004 This library is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU Library General Public 0006 License as published by the Free Software Foundation; either 0007 version 2 of the License, or (at your option) any later version. 0008 0009 This library is distributed in the hope that it will be useful, 0010 but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 Library General Public License for more details. 0013 0014 You should have received a copy of the GNU Library General Public License 0015 along with this library; see the file COPYING.LIB. If not, write to 0016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0017 * Boston, MA 02110-1301, USA. 0018 */ 0019 0020 #include "KDbParser_p.h" 0021 #include "KDb.h" 0022 #include "KDbConnection.h" 0023 #include "KDbTableSchema.h" 0024 #include "KDbQueryAsterisk.h" 0025 #include "KDbQuerySchema.h" 0026 #include "KDbQuerySchema_p.h" 0027 #include "KDbOrderByColumn.h" 0028 #include "kdb_debug.h" 0029 #include "generated/sqlparser.h" 0030 0031 #include <QMutableListIterator> 0032 0033 KDbParser *globalParser = nullptr; 0034 KDbField *globalField = nullptr; 0035 QList<KDbField*> fieldList; 0036 int globalCurrentPos = 0; 0037 QByteArray globalToken; 0038 0039 extern int yylex_destroy(void); 0040 0041 //------------------------------------- 0042 0043 KDbParserPrivate::KDbParserPrivate() 0044 : table(nullptr), query(nullptr), connection(nullptr), initialized(false) 0045 { 0046 reset(); 0047 } 0048 0049 KDbParserPrivate::~KDbParserPrivate() 0050 { 0051 reset(); 0052 } 0053 0054 void KDbParserPrivate::reset() 0055 { 0056 statementType = KDbParser::NoType; 0057 sql.clear(); 0058 error = KDbParserError(); 0059 delete table; 0060 table = nullptr; 0061 delete query; 0062 query = nullptr; 0063 } 0064 0065 void KDbParserPrivate::setStatementType(KDbParser::StatementType type) 0066 { 0067 statementType = type; 0068 } 0069 0070 void KDbParserPrivate::setError(const KDbParserError &err) 0071 { 0072 error = err; 0073 } 0074 0075 void KDbParserPrivate::setTableSchema(KDbTableSchema *table) 0076 { 0077 delete this->table; 0078 this->table = table; 0079 } 0080 0081 void KDbParserPrivate::setQuerySchema(KDbQuerySchema *query) 0082 { 0083 if (this->query != query) { 0084 delete this->query; 0085 } 0086 this->query = query; 0087 } 0088 0089 KDbQuerySchema* KDbParserPrivate::createQuery() 0090 { 0091 return query ? query : new KDbQuerySchema; 0092 } 0093 0094 //------------------------------------- 0095 0096 KDbParseInfo::KDbParseInfo(KDbQuerySchema *query) 0097 : d(new Private) 0098 { 0099 d->querySchema = query; 0100 } 0101 0102 KDbParseInfo::~KDbParseInfo() 0103 { 0104 delete d; 0105 } 0106 0107 QList<int> KDbParseInfo::tablesAndAliasesForName(const QString &tableOrAliasName) const 0108 { 0109 QList<int> result; 0110 const QList<int> *list = d->repeatedTablesAndAliases.value(tableOrAliasName); 0111 if (list) { 0112 result = *list; 0113 } 0114 if (result.isEmpty()) { 0115 int position = d->querySchema->tablePositionForAlias(tableOrAliasName); 0116 if (position == -1) { 0117 position = d->querySchema->tablePosition(tableOrAliasName); 0118 if (position != -1) { 0119 result.append(position); 0120 } 0121 } else { 0122 result.append(position); 0123 } 0124 } 0125 return result; 0126 } 0127 0128 KDbQuerySchema* KDbParseInfo::querySchema() const 0129 { 0130 return d->querySchema; 0131 } 0132 0133 QString KDbParseInfo::errorMessage() const 0134 { 0135 return d->errorMessage; 0136 } 0137 0138 QString KDbParseInfo::errorDescription() const 0139 { 0140 return d->errorDescription; 0141 } 0142 0143 void KDbParseInfo::setErrorMessage(const QString &message) 0144 { 0145 d->errorMessage = message; 0146 } 0147 0148 void KDbParseInfo::setErrorDescription(const QString &description) 0149 { 0150 d->errorDescription = description; 0151 } 0152 0153 //------------------------------------- 0154 0155 KDbParseInfoInternal::KDbParseInfoInternal(KDbQuerySchema *query) 0156 : KDbParseInfo(query) 0157 { 0158 } 0159 0160 KDbParseInfoInternal::~KDbParseInfoInternal() 0161 { 0162 } 0163 0164 void KDbParseInfoInternal::appendPositionForTableOrAliasName(const QString &tableOrAliasName, int pos) 0165 { 0166 QList<int> *list = d->repeatedTablesAndAliases.value(tableOrAliasName); 0167 if (!list) { 0168 list = new QList<int>(); 0169 d->repeatedTablesAndAliases.insert(tableOrAliasName, list); 0170 } 0171 list->append(pos); 0172 } 0173 0174 //------------------------------------- 0175 0176 extern int yyparse(); 0177 extern void tokenize(const char *data); 0178 0179 void yyerror(const char *str) 0180 { 0181 kdbDebug() << "error: " << str; 0182 kdbDebug() << "at character " << globalCurrentPos << " near tooken " << globalToken; 0183 KDbParserPrivate::get(globalParser)->setStatementType(KDbParser::NoType); 0184 0185 const bool otherError = (qstrnicmp(str, "other error", 11) == 0); 0186 const bool syntaxError = qstrnicmp(str, "syntax error", 12) == 0; 0187 if ((globalParser->error().type().isEmpty() 0188 && (str == nullptr || strlen(str) == 0 || syntaxError)) 0189 || otherError) 0190 { 0191 kdbDebug() << globalParser->statement(); 0192 QString ptrline(globalCurrentPos, QLatin1Char(' ')); 0193 0194 ptrline += QLatin1String("^"); 0195 0196 kdbDebug() << ptrline; 0197 0198 #if 0 0199 //lexer may add error messages 0200 QString lexerErr = globalParser->error().message(); 0201 0202 QString errtypestr = QLatin1String(str); 0203 if (lexerErr.isEmpty()) { 0204 if (errtypestr.startsWith(QString::fromLatin1("parse error, expecting `IDENTIFIER'"))) { 0205 lexerErr = KDbParser::tr("identifier was expected"); 0206 } 0207 } 0208 #endif 0209 0210 //! @todo exact invalid expression can be selected in the editor, based on KDbParseInfo data 0211 if (!otherError) { 0212 const bool isKDbSqlKeyword = KDb::isKDbSqlKeyword(globalToken); 0213 if (isKDbSqlKeyword || syntaxError) { 0214 if (isKDbSqlKeyword) { 0215 KDbParserPrivate::get(globalParser)->setError(KDbParserError(KDbParser::tr("Syntax Error"), 0216 KDbParser::tr("\"%1\" is a reserved keyword.").arg(QLatin1String(globalToken)), 0217 globalToken, globalCurrentPos)); 0218 } else { 0219 KDbParserPrivate::get(globalParser)->setError(KDbParserError(KDbParser::tr("Syntax Error"), 0220 KDbParser::tr("Syntax error."), 0221 globalToken, globalCurrentPos)); 0222 } 0223 } else { 0224 KDbParserPrivate::get(globalParser)->setError(KDbParserError(KDbParser::tr("Error"), 0225 KDbParser::tr("Error near \"%1\".").arg(QLatin1String(globalToken)), 0226 globalToken, globalCurrentPos)); 0227 } 0228 } 0229 } 0230 } 0231 0232 void setError(const QString& errName, const QString& errDesc) 0233 { 0234 KDbParserPrivate::get(globalParser)->setError(KDbParserError(errName, errDesc, globalToken, globalCurrentPos)); 0235 yyerror(qPrintable(errName)); 0236 } 0237 0238 void setError(const QString& errDesc) 0239 { 0240 setError(KDbParser::tr("Other error"), errDesc); 0241 } 0242 0243 /* this is better than assert() */ 0244 #define IMPL_ERROR(errmsg) setError(KDbParser::tr("Implementation error"), QLatin1String(errmsg)) 0245 0246 //! @internal Parses @a data for parser @a p 0247 //! @todo Make it REENTRANT 0248 bool parseData() 0249 { 0250 fieldList.clear(); 0251 0252 const KDbEscapedString sql(globalParser->statement()); 0253 if (sql.isEmpty()) { 0254 KDbParserError err(KDbParser::tr("Error"), 0255 KDbParser::tr("No query statement specified."), 0256 globalToken, globalCurrentPos); 0257 KDbParserPrivate::get(globalParser)->setError(err); 0258 yyerror(""); 0259 return false; 0260 } 0261 0262 const char *data = sql.constData(); 0263 tokenize(data); 0264 if (!globalParser->error().type().isEmpty()) { 0265 return false; 0266 } 0267 0268 bool ok = yyparse() == 0; 0269 if (ok && globalCurrentPos < sql.length()) { 0270 kdbDebug() << "Parse error: tokens left" 0271 << "globalCurrentPos:" << globalCurrentPos << "sql.length():" << sql.length() 0272 << "globalToken:" << QString::fromUtf8(globalToken); 0273 KDbParserError err(KDbParser::tr("Error"), 0274 KDbParser::tr("Unexpected character."), 0275 globalToken, globalCurrentPos); 0276 KDbParserPrivate::get(globalParser)->setError(err); 0277 yyerror(""); 0278 ok = false; 0279 } 0280 if (ok && globalParser->statementType() == KDbParser::Select) { 0281 kdbDebug() << "parseData(): ok"; 0282 // kdbDebug() << "parseData(): " << tableDict.count() << " loaded tables"; 0283 /* KDbTableSchema *ts; 0284 for(QDictIterator<KDbTableSchema> it(tableDict); KDbTableSchema *s = tableList.first(); s; s = tableList.next()) 0285 { 0286 kdbDebug() << " " << s->name(); 0287 }*/ 0288 } else { 0289 ok = false; 0290 } 0291 yylex_destroy(); 0292 return ok; 0293 } 0294 0295 0296 /*! Adds @a columnExpr to @a parseInfo 0297 The column can be in a form table.field, tableAlias.field or field. 0298 @return true on success. On error message in globalParser object is updated. 0299 */ 0300 bool addColumn(KDbParseInfo *parseInfo, const KDbExpression &columnExpr) 0301 { 0302 if (!KDbExpression(columnExpr).validate(parseInfo)) { // (KDbExpression(columnExpr) used to avoid constness problem) 0303 setError(parseInfo->errorMessage(), parseInfo->errorDescription()); 0304 return false; 0305 } 0306 0307 const KDbVariableExpression v_e(columnExpr.toVariable()); 0308 if (columnExpr.expressionClass() == KDb::VariableExpression && !v_e.isNull()) { 0309 //it's a variable: 0310 if (v_e.name() == QLatin1String("*")) {//all tables asterisk 0311 if (parseInfo->querySchema()->tables()->isEmpty()) { 0312 setError(KDbParser::tr("\"*\" could not be used if no tables are specified.")); 0313 return false; 0314 } 0315 KDbQueryAsterisk *a = new KDbQueryAsterisk(parseInfo->querySchema()); 0316 if (!parseInfo->querySchema()->addAsterisk(a)) { 0317 delete a; 0318 setError(KDbParser::tr("\"*\" could not be added.")); 0319 return false; 0320 } 0321 } else if (v_e.tableForQueryAsterisk()) {//one-table asterisk 0322 KDbQueryAsterisk *a = new KDbQueryAsterisk(parseInfo->querySchema(), *v_e.tableForQueryAsterisk()); 0323 if (!parseInfo->querySchema()->addAsterisk(a)) { 0324 delete a; 0325 setError(KDbParser::tr("\"<table>.*\" could not be added.")); 0326 return false; 0327 } 0328 } else if (v_e.field()) {//"table.field" or "field" (bound to a table or not) 0329 if (!parseInfo->querySchema()->addField(v_e.field(), v_e.tablePositionForField())) { 0330 setError(KDbParser::tr("Could not add binding to a field.")); 0331 return false; 0332 } 0333 } else { 0334 IMPL_ERROR("addColumn(): unknown case!"); 0335 return false; 0336 } 0337 return true; 0338 } 0339 0340 //it's complex expression 0341 return parseInfo->querySchema()->addExpression(columnExpr); 0342 } 0343 0344 KDbQuerySchema* buildSelectQuery( 0345 KDbQuerySchema* querySchema, KDbNArgExpression* _colViews, 0346 KDbNArgExpression* _tablesList, SelectOptionsInternal* options) 0347 { 0348 KDbParseInfoInternal parseInfo(querySchema); 0349 0350 // remove from heap (using heap was requered because parser uses union) 0351 KDbNArgExpression colViews; 0352 if (_colViews) { 0353 colViews = *_colViews; 0354 delete _colViews; 0355 } 0356 KDbNArgExpression tablesList; 0357 if (_tablesList) { 0358 tablesList = *_tablesList; 0359 delete _tablesList; 0360 } 0361 QScopedPointer<SelectOptionsInternal> optionsPtr(options); 0362 QScopedPointer<KDbQuerySchema> querySchemaPtr(querySchema); // destroy query on any error 0363 0364 //-------tables list 0365 int columnNum = 0; 0366 /*! @todo use this later if there are columns that use database fields, 0367 e.g. "SELECT 1 from table1 t, table2 t") is ok however. */ 0368 //used to collect information about first repeated table name or alias: 0369 if (!tablesList.isEmpty()) { 0370 for (int i = 0; i < tablesList.argCount(); i++, columnNum++) { 0371 KDbExpression e(tablesList.arg(i)); 0372 KDbVariableExpression t_e; 0373 QString aliasString; 0374 if (e.expressionClass() == KDb::SpecialBinaryExpression) { 0375 KDbBinaryExpression t_with_alias = e.toBinary(); 0376 Q_ASSERT(e.isBinary()); 0377 Q_ASSERT(t_with_alias.left().expressionClass() == KDb::VariableExpression); 0378 Q_ASSERT(t_with_alias.right().expressionClass() == KDb::VariableExpression 0379 && (t_with_alias.token() == KDbToken::AS || t_with_alias.token() == KDbToken::AS_EMPTY)); 0380 t_e = t_with_alias.left().toVariable(); 0381 aliasString = t_with_alias.right().toVariable().name(); 0382 } else { 0383 t_e = e.toVariable(); 0384 } 0385 Q_ASSERT(t_e.isVariable()); 0386 QString tname = t_e.name(); 0387 KDbTableSchema *s = globalParser->connection()->tableSchema(tname); 0388 if (!s) { 0389 setError(KDbParser::tr("Table \"%1\" does not exist.").arg(tname)); 0390 return nullptr; 0391 } 0392 QString tableOrAliasName = KDb::iifNotEmpty(aliasString, tname); 0393 if (!aliasString.isEmpty()) { 0394 // kdbDebug() << "- add alias for table: " << aliasString; 0395 const int tablePosition = querySchema->tablePositionForAlias(aliasString); 0396 if (tablePosition != -1 && tablePosition != i) { 0397 KDbTableSchema* tableForAlias = querySchema->tables()->at(tablePosition); 0398 setError(KDbParser::tr("Could not set alias \"%1\" for table \"%2\". " 0399 "This alias is already set for table \"%3\".") 0400 .arg(aliasString, tname, tableForAlias->name())); 0401 break; 0402 } 0403 } 0404 // 1. collect information about first repeated table name or alias 0405 // (potential ambiguity) 0406 parseInfo.appendPositionForTableOrAliasName(tableOrAliasName, i); 0407 // kdbDebug() << "addTable: " << tname; 0408 querySchema->addTable(s, aliasString); 0409 } 0410 } 0411 0412 /* set parent table if there's only one */ 0413 if (querySchema->tables()->count() == 1) 0414 querySchema->setMasterTable(querySchema->tables()->first()); 0415 0416 //-------add fields 0417 if (!colViews.isEmpty()) { 0418 columnNum = 0; 0419 bool containsAsteriskColumn = false; // used to check duplicated asterisks (disallowed) 0420 for (int i = 0; i < colViews.argCount(); i++, columnNum++) { 0421 const KDbExpression e(colViews.arg(i)); 0422 KDbExpression columnExpr(e); 0423 KDbVariableExpression aliasVariable; 0424 if (e.expressionClass() == KDb::SpecialBinaryExpression && e.isBinary() 0425 && (e.token() == KDbToken::AS || e.token() == KDbToken::AS_EMPTY)) { 0426 //KDb::SpecialBinaryExpression: with alias 0427 columnExpr = e.toBinary().left(); 0428 aliasVariable = e.toBinary().right().toVariable(); 0429 if (aliasVariable.isNull()) { 0430 setError(KDbParser::tr("Invalid alias definition for column \"%1\".") 0431 .arg(columnExpr.toString(nullptr).toString())); //ok? 0432 break; 0433 } 0434 } 0435 0436 const KDb::ExpressionClass c = columnExpr.expressionClass(); 0437 const bool isExpressionField = 0438 c == KDb::ConstExpression 0439 || c == KDb::UnaryExpression 0440 || c == KDb::ArithmeticExpression 0441 || c == KDb::LogicalExpression 0442 || c == KDb::RelationalExpression 0443 || c == KDb::FunctionExpression 0444 || c == KDb::AggregationExpression 0445 || c == KDb::QueryParameterExpression; 0446 0447 if (c == KDb::VariableExpression) { 0448 if (columnExpr.toVariable().name() == QLatin1String("*")) { 0449 if (containsAsteriskColumn) { 0450 setError(KDbParser::tr("More than one asterisk \"*\" is not allowed.")); 0451 return nullptr; 0452 } 0453 else { 0454 containsAsteriskColumn = true; 0455 } 0456 } 0457 // addColumn() will handle this 0458 } 0459 else if (isExpressionField) { 0460 //expression object will be reused, take, will be owned, do not destroy 0461 // kdbDebug() << colViews->list.count() << " " << it.current()->debugString(); 0462 //! @todo IMPORTANT: it.remove(); 0463 } else if (aliasVariable.isNull()) { 0464 setError(KDbParser::tr("Invalid \"%1\" column definition.") 0465 .arg(e.toString(nullptr).toString())); //ok? 0466 break; 0467 } 0468 else { 0469 //take first (left) argument of the special binary expr, will be owned, do not destroy 0470 e.toBinary().setLeft(KDbExpression()); 0471 } 0472 0473 if (!addColumn(&parseInfo, columnExpr)) { 0474 break; 0475 } 0476 0477 if (!aliasVariable.isNull()) { 0478 // kdbDebug() << "ALIAS \"" << aliasVariable->name << "\" set for column " 0479 // << columnNum; 0480 const int currentColumn = querySchema->columnPositionForAlias(aliasVariable.name()); 0481 if (currentColumn != -1) { 0482 setError(KDbParser::tr("Could not set alias \"%1\" for column #%2. This alias is already set for column #%3.") 0483 .arg(aliasVariable.name()).arg(columnNum + 1).arg(currentColumn + 1)); 0484 break; 0485 } 0486 if (!querySchema->setColumnAlias(columnNum, aliasVariable.name())) { 0487 setError(KDbParser::tr("Could not set alias \"%1\" for column #%2.") 0488 .arg(aliasVariable.name()).arg(columnNum + 1)); 0489 break; 0490 } 0491 } 0492 } // for 0493 if (!globalParser->error().message().isEmpty()) { // we could not return earlier (inside the loop) 0494 // because we want run CLEANUP what could crash QMutableListIterator. 0495 return nullptr; 0496 } 0497 } 0498 //----- SELECT options 0499 if (options) { 0500 //----- WHERE expr. 0501 if (!options->whereExpr.isNull()) { 0502 if (!options->whereExpr.validate(&parseInfo)) { 0503 setError(parseInfo.errorMessage(), parseInfo.errorDescription()); 0504 return nullptr; 0505 } 0506 KDbQuerySchemaPrivate::setWhereExpressionInternal(querySchema, options->whereExpr); 0507 } 0508 //----- ORDER BY 0509 if (options->orderByColumns) { 0510 KDbOrderByColumnList *orderByColumnList = querySchema->orderByColumnList(); 0511 int count = options->orderByColumns->count(); 0512 QList<OrderByColumnInternal>::ConstIterator it(options->orderByColumns->constEnd()); 0513 --it; 0514 for (;count > 0; --it, --count) 0515 /*opposite direction due to parser specifics*/ 0516 { 0517 // first, try to find a column name or alias (outside of asterisks) 0518 KDbQueryColumnInfo *columnInfo = querySchema->columnInfo( 0519 globalParser->connection(), (*it).aliasOrName, 0520 KDbQuerySchema::ExpandMode::Unexpanded /*outside of asterisks*/); 0521 if (columnInfo) { 0522 orderByColumnList->appendColumn(columnInfo, (*it).order); 0523 } else { 0524 //failed, try to find a field name within all the tables 0525 if ((*it).columnNumber != -1) { 0526 if (!orderByColumnList->appendColumn(globalParser->connection(), 0527 querySchema, (*it).order, 0528 (*it).columnNumber - 1)) 0529 { 0530 setError(KDbParser::tr("Could not define sorting. Column at " 0531 "position %1 does not exist.") 0532 .arg((*it).columnNumber)); 0533 return nullptr; 0534 } 0535 } else { 0536 KDbField * f = querySchema->findTableField((*it).aliasOrName); 0537 if (!f) { 0538 setError(KDbParser::tr("Could not define sorting. " 0539 "Column name or alias \"%1\" does not exist.") 0540 .arg((*it).aliasOrName)); 0541 return nullptr; 0542 } 0543 orderByColumnList->appendField(f, (*it).order); 0544 } 0545 } 0546 } 0547 } 0548 } 0549 // kdbDebug() << "Select ColViews=" << (colViews ? colViews->debugString() : QString()) 0550 // << " Tables=" << (tablesList ? tablesList->debugString() : QString()s); 0551 return querySchemaPtr.take(); 0552 }