Projekt

Allgemein

Profil

Herunterladen als
Herunterladen (12,5 KB) Statistiken
| Zweig: | Revision:
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> &params,
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);
}