Herunterladen als
root/src/queryinterpreter.cpp @ 846ba44a
38838b5b | Christian Ehringfeld | /*
|
|
* Copyright (C) 2015 Christian Ehringfeld <c.ehringfeld@t-online.de>
|
|||
*
|
|||
* This program is free software; you can redistribute it and/or modify it
|
|||
* under the terms of the GNU Lesser General Public License as published by
|
|||
* the Free Software Foundation.
|
|||
*
|
|||
* This program 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 General Public License
|
|||
* for more details.
|
|||
*
|
|||
* You should have received a copy of the GNU Lesser General Public License
|
|||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|||
*/
|
|||
2ee5022f | Christian Ehringfeld | #include "queryinterpreter.h"
|
|
3160499c | Christian Ehringfeld | #include "join.h"
|
|
#include "query.h"
|
|||
dbd41a3a | Christian Ehringfeld | #include "attributeresolver.h"
|
|
3160499c | Christian Ehringfeld | #include "orderby.h"
|
|
#include "expression.h"
|
|||
#include "schema.h"
|
|||
8fea42af | Christian Ehringfeld | #include "entityinstancefactory.h"
|
|
2ee5022f | Christian Ehringfeld | using namespace CuteEntityManager;
|
|
dbd41a3a | Christian Ehringfeld | QueryInterpreter::QueryInterpreter(QSharedPointer<AttributeResolver> ar) {
|
|
this->ar = ar;
|
|||
2ee5022f | Christian Ehringfeld | }
|
|
dbd41a3a | Christian Ehringfeld | QSqlQuery QueryInterpreter::build(Query &q, const QMetaObject *obj) {
|
|
8fea42af | Christian Ehringfeld | if(obj) {
|
|
this->resolveRelations(q, obj);
|
|||
}
|
|||
3160499c | Christian Ehringfeld | QList<QString> clauses = QList<QString>();
|
|
1b167b6c | Christian Ehringfeld | clauses.append(this->buildSelect(q, q.getSelect(), q.getDistinct(),
|
|
3160499c | Christian Ehringfeld | q.getSelectOption()));
|
|
clauses.append(this->buildFrom(q.getFrom()));
|
|||
clauses.append(this->buildJoin(q.getJoins()));
|
|||
1b167b6c | Christian Ehringfeld | clauses.append(this->buildWhere(q, q.getWhere()));
|
|
3160499c | Christian Ehringfeld | clauses.append(this->buildGroupBy(q.getGroupBy()));
|
|
1b167b6c | Christian Ehringfeld | clauses.append(this->buildHaving(q, q.getHaving()));
|
|
3160499c | Christian Ehringfeld | QString sql = "";
|
|
bool first = true;
|
|||
for (int i = 0; i < clauses.size(); ++i) {
|
|||
QString clause = clauses.at(i);
|
|||
if (!clause.isEmpty()) {
|
|||
if (first) {
|
|||
first = false;
|
|||
} else {
|
|||
dbd41a3a | Christian Ehringfeld | sql += this->ar->getQb()->getSeparator();
|
|
3160499c | Christian Ehringfeld | }
|
|
sql += clause;
|
|||
}
|
|||
}
|
|||
sql = this->buildOrderByAndLimit(sql, q.getOrderBy(), q.getLimit(),
|
|||
q.getOffset());
|
|||
dbd41a3a | Christian Ehringfeld | QSqlQuery sqlQuery = this->ar->getQb()->getQuery();
|
|
3160499c | Christian Ehringfeld | sqlQuery.prepare(sql);
|
|
dbd41a3a | Christian Ehringfeld | this->ar->getQb()->bindValues(q.getParams(), sqlQuery, false);
|
|
3160499c | Christian Ehringfeld | return sqlQuery;
|
|
}
|
|||
1b167b6c | Christian Ehringfeld | QString QueryInterpreter::buildSelect(Query &q,
|
|
const QList<Expression> &columns,
|
|||
3160499c | Christian Ehringfeld | const bool &distinct, const QString &selectOption) const {
|
|
dbd41a3a | Christian Ehringfeld | QString sqlSelect = distinct ? ("SELECT " + this->ar->getQb()->distinct() + " ") :
|
|
87739ae0 | Christian Ehringfeld | "SELECT ";
|
|
3160499c | Christian Ehringfeld | if (!selectOption.isEmpty()) {
|
|
38335e94 | Christian Ehringfeld | sqlSelect += selectOption + " ";
|
|
3160499c | Christian Ehringfeld | }
|
|
if (columns.isEmpty()) {
|
|||
87739ae0 | Christian Ehringfeld | return sqlSelect + "*";
|
|
3160499c | Christian Ehringfeld | }
|
|
bool first = true;
|
|||
for (int i = 0; i < columns.size(); ++i) {
|
|||
if (first) {
|
|||
first = false;
|
|||
} else {
|
|||
sqlSelect += ", ";
|
|||
}
|
|||
Expression e = columns.at(i);
|
|||
QString nExp = e.getExpression();
|
|||
2316d17f | Christian Ehringfeld | auto params = e.getParams();
|
|
fd67a7a7 | Christian Ehringfeld | this->convertParams(q, params, nExp);
|
|
3160499c | Christian Ehringfeld | if (e.getOnlyColumn()) {
|
|
dbd41a3a | Christian Ehringfeld | sqlSelect += this->ar->getQb()->getSchema()->quoteColumnName(e.getExpression());
|
|
3160499c | Christian Ehringfeld | } else if (!nExp.contains("(")) {
|
|
QRegularExpression re =
|
|||
QRegularExpression(
|
|||
QRegularExpression::escape("/^(.*?)(?i:\\s+as\\s+|\\s+)([\\w\\-_\\.]+)$/"));
|
|||
cb5bb587 | Christian Ehringfeld | #if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)
|
|
ae872f6f | Christian Ehringfeld | re.optimize();
|
|
cb5bb587 | Christian Ehringfeld | #endif
|
|
3160499c | Christian Ehringfeld | QRegularExpressionMatchIterator iterator = re.globalMatch(nExp, 0,
|
|
QRegularExpression::PartialPreferFirstMatch);
|
|||
if (iterator.hasNext()) {
|
|||
for (int var = 0; var < 2; ++var) {
|
|||
QRegularExpressionMatch match = iterator.next();
|
|||
if (var == 1) {
|
|||
dbd41a3a | Christian Ehringfeld | sqlSelect += this->ar->getQb()->getSchema()->quoteColumnName(
|
|
match.captured()) + " AS " + this->ar->getQb()->getSchema()->quoteColumnName("a" +
|
|||
3160499c | Christian Ehringfeld | QString::number(i));
|
|
}
|
|||
}
|
|||
} else {
|
|||
dbd41a3a | Christian Ehringfeld | nExp = this->ar->getQb()->getSchema()->quoteColumnName(nExp);
|
|
3160499c | Christian Ehringfeld | }
|
|
} else {
|
|||
dbd41a3a | Christian Ehringfeld | sqlSelect += nExp + " AS " + this->ar->getQb()->getSchema()->quoteColumnName("a" +
|
|
3160499c | Christian Ehringfeld | QString::number(i));
|
|
}
|
|||
}
|
|||
return sqlSelect;
|
|||
}
|
|||
QString QueryInterpreter::buildFrom(const QStringList &from) const {
|
|||
if (from.isEmpty()) {
|
|||
return "";
|
|||
}
|
|||
dbd41a3a | Christian Ehringfeld | auto tables = this->ar->getQb()->quoteTableNames(from);
|
|
3160499c | Christian Ehringfeld | QString clause = "FROM ";
|
|
bool first = true;
|
|||
for (int var = 0; var < tables.size(); ++var) {
|
|||
if (first) {
|
|||
first = false;
|
|||
} else {
|
|||
clause += ", ";
|
|||
}
|
|||
f89ad1ad | Christian Ehringfeld | clause += tables.at(var);
|
|
3160499c | Christian Ehringfeld | }
|
|
return clause;
|
|||
}
|
|||
QString QueryInterpreter::buildJoin(const QList<Join> &joins) const {
|
|||
if (joins.isEmpty()) {
|
|||
return "";
|
|||
}
|
|||
QString sqlJoin = "";
|
|||
for (int i = 0; i < joins.size(); ++i) {
|
|||
Join j = joins.at(i);
|
|||
dbd41a3a | Christian Ehringfeld | sqlJoin += j.getType() + this->ar->getQb()->getSeparator() +
|
|
this->ar->getQb()->getSchema()->quoteTableName(j.getForeignTable());
|
|||
e3fc2748 | Christian Ehringfeld | sqlJoin += (j.getAlias().isEmpty() ? "" : " AS " +
|
|
this->ar->getQb()->getSchema()->quoteTableName(j.getAlias()));
|
|||
506067a2 | Christian Ehringfeld | if (!j.getExpression().getExpression().isEmpty()) {
|
|
6cfbc25e | Christian Ehringfeld | QString expression = j.getExpression().getExpression();
|
|
int count = expression.count("=");
|
|||
if (count < 1) {
|
|||
dbd41a3a | Christian Ehringfeld | expression = this->ar->getQb()->getSchema()->quoteTableName(expression);
|
|
6cfbc25e | Christian Ehringfeld | } else if (count == 1) {
|
|
QStringList list = expression.split("=");
|
|||
dbd41a3a | Christian Ehringfeld | expression = this->ar->getQb()->getSchema()->quoteColumnName(list.at(
|
|
6cfbc25e | Christian Ehringfeld | 0).trimmed()) + " = ";
|
|
dbd41a3a | Christian Ehringfeld | expression += this->ar->getQb()->getSchema()->quoteColumnName(list.at(1).trimmed());
|
|
6cfbc25e | Christian Ehringfeld | }
|
|
sqlJoin += " ON " + expression;
|
|||
3160499c | Christian Ehringfeld | }
|
|
}
|
|||
return sqlJoin;
|
|||
}
|
|||
e3fc2748 | Christian Ehringfeld | QString QueryInterpreter::buildSQLJoin(const QString &table1, const QString &col1,
|
|
const QString &table2, const QString &col2) const {
|
|||
return table1 + "." + col1 + " = " + table2 + "." + col2;
|
|||
}
|
|||
1b167b6c | Christian Ehringfeld | QString QueryInterpreter::buildWhere(Query &q,
|
|
const QList<Expression> &conditions)
|
|||
3160499c | Christian Ehringfeld | const {
|
|
1b167b6c | Christian Ehringfeld | QString where = this->buildCondition(q, conditions);
|
|
3160499c | Christian Ehringfeld | return where.isEmpty() ? "" : ("WHERE " + where);
|
|
}
|
|||
2ee5022f | Christian Ehringfeld | ||
3160499c | Christian Ehringfeld | QString QueryInterpreter::buildGroupBy(const QStringList &groupBy) const {
|
|
dbd41a3a | Christian Ehringfeld | return groupBy.isEmpty() ? "" : "GROUP BY " + this->ar->getQb()->buildColumns(
|
|
3160499c | Christian Ehringfeld | groupBy);
|
|
2ee5022f | Christian Ehringfeld | }
|
|
3160499c | Christian Ehringfeld | ||
1b167b6c | Christian Ehringfeld | QString QueryInterpreter::buildHaving(Query &q,
|
|
19cf5548 | Christian Ehringfeld | const QList<Expression> &conditions) const {
|
|
1b167b6c | Christian Ehringfeld | QString having = this->buildCondition(q, conditions);
|
|
3160499c | Christian Ehringfeld | return having.isEmpty() ? "" : ("HAVING " + having);
|
|
}
|
|||
QString QueryInterpreter::buildOrderByAndLimit(QString sql,
|
|||
const QList<OrderBy> &orderBy, const quint64 &limit,
|
|||
const quint64 &offset) const {
|
|||
QString sqlOrderBy = this->buildOrderBy(orderBy);
|
|||
if (!sqlOrderBy.isEmpty()) {
|
|||
dbd41a3a | Christian Ehringfeld | sql += this->ar->getQb()->getSeparator() + sqlOrderBy;
|
|
3160499c | Christian Ehringfeld | }
|
|
dbd41a3a | Christian Ehringfeld | QString sqlLimit = this->ar->getQb()->limit(limit, offset, false);
|
|
3160499c | Christian Ehringfeld | if (!sqlLimit.isEmpty()) {
|
|
dbd41a3a | Christian Ehringfeld | sql += this->ar->getQb()->getSeparator() + sqlLimit;
|
|
3160499c | Christian Ehringfeld | }
|
|
return sql;
|
|||
}
|
|||
QString QueryInterpreter::buildOrderBy(const QList<OrderBy> &columns) const {
|
|||
if (columns.isEmpty()) {
|
|||
return "";
|
|||
}
|
|||
bool first = true;
|
|||
QString sqlOrder = "ORDER BY ";
|
|||
for (int i = 0; i < columns.size(); ++i) {
|
|||
if (first) {
|
|||
first = false;
|
|||
} else {
|
|||
sqlOrder += ", ";
|
|||
}
|
|||
OrderBy order = columns.at(i);
|
|||
if (order.getColumn().isEmpty()) {
|
|||
sqlOrder += order.getExpressedDirection().getExpression();
|
|||
} else {
|
|||
dbd41a3a | Christian Ehringfeld | sqlOrder += this->ar->getQb()->getSchema()->quoteColumnName(order.getColumn());
|
|
3160499c | Christian Ehringfeld | switch (order.getDirection()) {
|
|
case Direction::SORT_ASC:
|
|||
sqlOrder += " ASC";
|
|||
break;
|
|||
case Direction::SORT_DESC:
|
|||
sqlOrder += " DESC";
|
|||
break;
|
|||
default:
|
|||
break;
|
|||
}
|
|||
}
|
|||
}
|
|||
return sqlOrder;
|
|||
}
|
|||
1b167b6c | Christian Ehringfeld | QString QueryInterpreter::buildCondition(Query &q,
|
|
19cf5548 | Christian Ehringfeld | const QList<Expression> &conditions) const {
|
|
3160499c | Christian Ehringfeld | if (conditions.isEmpty()) {
|
|
return "";
|
|||
}
|
|||
506067a2 | Christian Ehringfeld | QString sqlCondition = "";
|
|
bool first = true;
|
|||
for (int i = 0; i < conditions.size(); ++i) {
|
|||
Expression exp = conditions.at(i);
|
|||
QString expression = exp.getExpression();
|
|||
if (!expression.isEmpty()) {
|
|||
if (first) {
|
|||
first = false;
|
|||
} else if (expression.at(0) != ' ') {
|
|||
dbd41a3a | Christian Ehringfeld | sqlCondition += this->ar->getQb()->getSeparator();
|
|
506067a2 | Christian Ehringfeld | }
|
|
}
|
|||
2316d17f | Christian Ehringfeld | auto params = exp.getParams();
|
|
fd67a7a7 | Christian Ehringfeld | this->convertParams(q, params, expression);
|
|
506067a2 | Christian Ehringfeld | sqlCondition += expression;
|
|
}
|
|||
return sqlCondition;
|
|||
3160499c | Christian Ehringfeld | }
|
|
2316d17f | Christian Ehringfeld | ||
8fea42af | Christian Ehringfeld | QVariant QueryInterpreter::convertParamValue(const QVariant val) const {
|
|
auto typeName = QString(val.typeName());
|
|||
QVariant r = val;
|
|||
if(typeName.contains("QSharedPointer")) {
|
|||
if(typeName.contains("QList")) {
|
|||
auto entities = EntityInstanceFactory::castQVariantList(r);
|
|||
QList<QVariant> ids;
|
|||
for (int i = 0; i < entities.size(); ++i) {
|
|||
if(entities.at(i)) {
|
|||
ids.append(entities.at(i)->getProperty(entities.at(i)->getPrimaryKey()));
|
|||
}
|
|||
}
|
|||
r.setValue<QList<QVariant>>(ids);
|
|||
} else {
|
|||
auto entity = EntityInstanceFactory::castQVariant(r);
|
|||
if(entity && entity->getId() != -1) {
|
|||
r = entity->getProperty(entity->getPrimaryKey());
|
|||
}
|
|||
}
|
|||
}
|
|||
return r;
|
|||
}
|
|||
void QueryInterpreter::resolveRelations(Query &q, const QMetaObject *obj) {
|
|||
e3fc2748 | Christian Ehringfeld | auto joins = q.getJoins();
|
|
auto instance = EntityInstanceFactory::createInstance(obj->className());
|
|||
auto newJoins = QList<Join>();
|
|||
for (int i = 0; i < joins.size(); ++i) {
|
|||
auto join = joins.at(i);
|
|||
if(!join.getAttribute().isEmpty()) {
|
|||
auto attr = this->ar->resolveAttribute(obj->className(), join.getAttribute());
|
|||
if(attr) {
|
|||
auto foreignInstance = EntityInstanceFactory::createInstance(
|
|||
attr->getRelatedClass()->className());
|
|||
join.setForeignTable(attr->getRelatedTable());
|
|||
if(!attr->getConjunctedTable().isEmpty()) {
|
|||
Join j = Join();
|
|||
j.setForeignTable(attr->getConjunctedTable());
|
|||
j.setExpression(Expression(this->buildSQLJoin(attr->getConjunctedTable(),
|
|||
attr->getColumnName(), attr->getTableName(),
|
|||
instance->getPrimaryKey())));
|
|||
join.setForeignTable(attr->getRelatedTable());
|
|||
join.setAlias(attr->getName());
|
|||
join.setExpression(Expression(this->buildSQLJoin(attr->getConjunctedTable(),
|
|||
attr->getRelatedColumnName(), attr->getName(),
|
|||
foreignInstance->getPrimaryKey())));
|
|||
newJoins.prepend(j);
|
|||
} else {
|
|||
join.setAlias(attr->getName());
|
|||
join.setForeignTable(attr->getRelatedTable());
|
|||
join.setExpression(Expression(this->buildSQLJoin(attr->getTableName(),
|
|||
attr->getColumnName(), attr->getName(), foreignInstance->getPrimaryKey())));
|
|||
}
|
|||
joins.replace(i, join);
|
|||
}
|
|||
}
|
|||
fd67a7a7 | Christian Ehringfeld | }
|
|
e3fc2748 | Christian Ehringfeld | foreach (Join j, newJoins) {
|
|
joins.prepend(j);
|
|||
}
|
|||
q.setJoins(joins);
|
|||
8fea42af | Christian Ehringfeld | }
|
|
fd67a7a7 | Christian Ehringfeld | void QueryInterpreter::convertParams(Query &q, const QHash<QString, QVariant> ¶ms,
|
|
QString &condition) const {
|
|||
for (auto i = params.begin(); i != params.end(); ++i) {
|
|||
QString key = this->generateParam(q);
|
|||
condition.replace(this->ar->getQb()->placeHolder(i.key()),
|
|||
this->ar->getQb()->placeHolder(key));
|
|||
q.appendParam(key, this->convertParamValue(i.value()));
|
|||
2316d17f | Christian Ehringfeld | }
|
|
}
|
|||
fd67a7a7 | Christian Ehringfeld | ||
3b82c8c0 | Christian Ehringfeld | QString QueryInterpreter::generateParam(Query &q) const {
|
|
return "eP" + QString::number(q.getParams().size() + 1);
|
|||
}
|