diff --git a/src/KDbNativeStatementBuilder.cpp b/src/KDbNativeStatementBuilder.cpp --- a/src/KDbNativeStatementBuilder.cpp +++ b/src/KDbNativeStatementBuilder.cpp @@ -342,7 +342,7 @@ s_where += s_where_sub; } //EXPLICITLY SPECIFIED WHERE EXPRESSION - if (driver && !querySchema->whereExpression().isNull()) { + if (!querySchema->whereExpression().isNull()) { KDbQuerySchemaParameterValueListIterator paramValuesIt(parameters); KDbQuerySchemaParameterValueListIterator *paramValuesItPtr = parameters.isEmpty() ? 0 : ¶mValuesIt; if (wasWhere) { diff --git a/src/KDbQuerySchema.h b/src/KDbQuerySchema.h --- a/src/KDbQuerySchema.h +++ b/src/KDbQuerySchema.h @@ -1,5 +1,5 @@ /* This file is part of the KDE project - Copyright (C) 2003-2016 Jarosław Staniek + Copyright (C) 2003-2017 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -717,18 +717,36 @@ driver escaping. */ KDbEscapedString autoIncrementSQLFieldsList(KDbConnection *conn) const; - /*! Sets a WHERE expression @a exp. - Previously set WHERE expression will be removed. - You can pass null expression (KDbExpression()) to remove existing WHERE expresssion. */ - void setWhereExpression(const KDbExpression& expr); + /** + * @brief Sets a WHERE expression @a exp. + * + * Previously set WHERE expression will be removed. A null expression + * (KDbExpression()) can be passed to remove existing WHERE expresssion. + * @return @c false if @a expr is not a valid WHERE expression. validate() is called + * to check this. On failure the WHERE expression for this query is cleared. In this + * case a string pointed by @a errorMessage (if provided) is set to a general error + * message and a string pointed by @a errorDescription (if provided) is set to a + * detailed description of the error. + */ + bool setWhereExpression(const KDbExpression &expr, QString *errorMessage = nullptr, + QString *errorDescription = nullptr); /*! @return WHERE expression or 0 if this query has no WHERE expression */ KDbExpression whereExpression() const; - /*! Adds a part to WHERE expression. - Simplifies creating of WHERE expression, if used instead - of setWhereExpression(KDbExpression *expr). */ - void addToWhereExpression(KDbField *field, const QVariant& value, KDbToken relation = '='); + /** + * @brief Appends a part to WHERE expression. + * + * Simplifies creating of WHERE expression if used instead of setWhereExpression(). + * @return @c false if the newly constructed WHERE expression is not valid. + * validate() is called to check this. On failure the WHERE expression for this query + * is left unchanged. In this case a string pointed by @a errorMessage (if provided) + * is set to a general error message and a string pointed by @a errorDescription + * (if provided) is set to a detailed description of the error. + */ + bool addToWhereExpression(KDbField *field, const QVariant &value, + KDbToken relation = '=', QString *errorMessage = nullptr, + QString *errorDescription = nullptr); /*! Sets a list of columns for ORDER BY section of the query. Each name on the list must be a field or alias present within the query @@ -760,14 +778,16 @@ * by the KDbParser. * Example :Let the query be "SELECT FROM WHERE ". * First each field from (@see fields()) is validated using - * KDbField::expression().validate(). Then the (@see whereExpression()) + * KDbField::expression().validate(). Then the (@see + * whereExpression()) * is validated using KDbExpression::validate(). * - * On error, a string pointed by @a errorMessage is set to a general error message - * and a string pointed by @a errorDescription is set to a detailed description - * of the error. */ + * On error a string pointed by @a errorMessage (if provided) is set to a general + * error message and a string pointed by @a errorDescription (if provided) is set to a + * detailed description of the error. + */ //! @todo add tests - bool validate(QString *errorMessage, QString *errorDescription); + bool validate(QString *errorMessage = nullptr, QString *errorDescription = nullptr); class Private; @@ -784,6 +804,10 @@ KDbQueryColumnInfo::Vector fieldsExpandedInternal(FieldsExpandedOptions options, bool onlyVisible) const; + /** Internal method used by a query parser. + */ + void setWhereExpressionInternal(const KDbExpression &expr); + Private * const d; }; diff --git a/src/KDbQuerySchema.cpp b/src/KDbQuerySchema.cpp --- a/src/KDbQuerySchema.cpp +++ b/src/KDbQuerySchema.cpp @@ -1,5 +1,5 @@ /* This file is part of the KDE project - Copyright (C) 2003-2016 Jarosław Staniek + Copyright (C) 2003-2017 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -1529,13 +1529,44 @@ return d->autoIncrementSQLFieldsList; } -void KDbQuerySchema::setWhereExpression(const KDbExpression& expr) +static void setResult(const KDbParseInfoInternal &parseInfo, + QString *errorMessage, QString *errorDescription) { - d->whereExpr = expr.clone(); + if (errorMessage) { + *errorMessage = parseInfo.errorMessage(); + } + if (errorDescription) { + *errorDescription = parseInfo.errorDescription(); + } +} + +bool KDbQuerySchema::setWhereExpression(const KDbExpression &expr, QString *errorMessage, + QString *errorDescription) +{ + KDbExpression newWhereExpr = expr.clone(); + KDbParseInfoInternal parseInfo(this); + QString tempErrorMessage; + QString tempErrorDescription; + QString *errorMessagePointer = errorMessage ? errorMessage : &tempErrorMessage; + QString *errorDescriptionPointer + = errorDescription ? errorDescription : &tempErrorDescription; + if (!newWhereExpr.validate(&parseInfo)) { + setResult(parseInfo, errorMessagePointer, errorDescription); + kdbWarning() << "message=" << *errorMessagePointer + << "description=" << *errorDescriptionPointer; + kdbWarning() << newWhereExpr; + d->whereExpr = KDbExpression(); + return false; + } + errorMessagePointer->clear(); + errorDescriptionPointer->clear(); + Private::setWhereExpressionInternal(this, newWhereExpr); + return true; } -void KDbQuerySchema::addToWhereExpression(KDbField *field, - const QVariant& value, KDbToken relation) +bool KDbQuerySchema::addToWhereExpression(KDbField *field, const QVariant &value, + KDbToken relation, QString *errorMessage, + QString *errorDescription) { KDbToken token; if (value.isNull()) { @@ -1557,16 +1588,19 @@ relation, KDbVariableExpression((field->table() ? (field->table()->name() + QLatin1Char('.')) : QString()) + field->name()) ); - if (d->whereExpr.isNull()) { - d->whereExpr = newExpr; - } - else { - d->whereExpr = KDbBinaryExpression( + const KDbExpression origWhereExpr = d->whereExpr; + if (!d->whereExpr.isNull()) { + newExpr = KDbBinaryExpression( d->whereExpr, KDbToken::AND, newExpr ); } + const bool result = setWhereExpression(newExpr, errorMessage, errorDescription); + if (!result) { // revert, setWhereExpression() cleared it + d->whereExpr = origWhereExpr; + } + return result; } /* @@ -1616,17 +1650,6 @@ return params; } -static void setResult(const KDbParseInfoInternal &parseInfo, - QString *errorMessage, QString *errorDescription) -{ - if (errorMessage) { - *errorMessage = parseInfo.errorMessage(); - } - if (errorDescription) { - *errorDescription = parseInfo.errorDescription(); - } -} - bool KDbQuerySchema::validate(QString *errorMessage, QString *errorDescription) { KDbParseInfoInternal parseInfo(this); diff --git a/src/KDbQuerySchema_p.h b/src/KDbQuerySchema_p.h --- a/src/KDbQuerySchema_p.h +++ b/src/KDbQuerySchema_p.h @@ -80,6 +80,12 @@ return columnPositionsForAliases.value(alias.toLower(), -1); } + //! Accessor for buildSelectQuery() + static void setWhereExpressionInternal(KDbQuerySchema *query, const KDbExpression &expr) + { + query->d->whereExpr = expr; + } + KDbQuerySchema *query; /*! Master table of the query. (may be NULL) diff --git a/src/parser/KDbParser_p.h b/src/parser/KDbParser_p.h --- a/src/parser/KDbParser_p.h +++ b/src/parser/KDbParser_p.h @@ -88,6 +88,8 @@ ~KDbParseInfo(); //! @return positions of tables/aliases having the same name @a tableOrAliasName. + //! First tries to use information provided by appendPositionForTableOrAliasName(), + //! then information from the query schema. QList tablesAndAliasesForName(const QString &tableOrAliasName) const; //! @return query schema for this parsing diff --git a/src/parser/KDbParser_p.cpp b/src/parser/KDbParser_p.cpp --- a/src/parser/KDbParser_p.cpp +++ b/src/parser/KDbParser_p.cpp @@ -1,5 +1,5 @@ /* This file is part of the KDE project - Copyright (C) 2004-2016 Jarosław Staniek + Copyright (C) 2004-2017 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -22,6 +22,7 @@ #include "KDbConnection.h" #include "KDbTableSchema.h" #include "KDbQuerySchema.h" +#include "KDbQuerySchema_p.h" #include "kdb_debug.h" #include "generated/sqlparser.h" @@ -98,8 +99,23 @@ QList KDbParseInfo::tablesAndAliasesForName(const QString &tableOrAliasName) const { + QList result; const QList *list = d->repeatedTablesAndAliases.value(tableOrAliasName); - return list ? *list : QList(); + if (list) { + result = *list; + } + if (result.isEmpty()) { + int position = d->querySchema->tablePositionForAlias(tableOrAliasName); + if (position == -1) { + position = d->querySchema->tablePosition(tableOrAliasName); + if (position != -1) { + result.append(position); + } + } else { + result.append(position); + } + } + return result; } KDbQuerySchema* KDbParseInfo::querySchema() const @@ -465,7 +481,7 @@ setError(parseInfo.errorMessage(), parseInfo.errorDescription()); return 0; } - querySchema->setWhereExpression(options->whereExpr); + KDbQuerySchema::Private::setWhereExpressionInternal(querySchema, options->whereExpr); } //----- ORDER BY if (options->orderByColumns) {