[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-commits
Subject: [kdb] /: Fix handling ORDER BY part of queries
From: Jaroslaw Staniek <null () kde ! org>
Date: 2018-05-16 8:16:28
Message-ID: E1fIrbo-00036c-C8 () code ! kde ! org
[Download RAW message or body]
Git commit 3414fbddffa433b23d43376b44fef5421b734250 by Jaroslaw Staniek.
Committed on 16/05/2018 at 08:15.
Pushed by staniek into branch 'master'.
Fix handling ORDER BY part of queries
Summary:
- new APIs
- maintain relations between connections, queries and column infos
- fix cloning queries with ORDER BY
- update and add autotests
FIXED-IN:3.2
BUG:392753
CCBUG:393705
Test Plan:
Required: KEXI & KDb 3.2 (current master)
Test 1: Open report and query from https://bugs.kde.org/show_bug.cgi?id=392753#c0 in \
design and data views.
Expected: works, no crashes
Test 2: Try these on a standard Persons and Cars test:
select id from cars order by owner;
select id from cars order by owner, model, id;
select id, model from cars order by 2, 1;
Expected: works, no crash
Reviewers: piggz
Reviewed By: piggz
Subscribers: Kexi-Devel-list
Tags: #kdb
Differential Revision: https://phabricator.kde.org/D12873
M +1 -1 CMakeLists.txt
M +3 -0 autotests/CMakeLists.txt
A +212 -0 autotests/OrderByColumnTest.cpp [License: LGPL (v2+)]
A +46 -0 autotests/OrderByColumnTest.h [License: LGPL (v2+)]
M +2 -2 src/KDbNativeStatementBuilder.cpp
M +87 -28 src/KDbOrderByColumn.cpp
M +40 -13 src/KDbOrderByColumn.h
M +17 -28 src/KDbQueryColumnInfo.cpp
M +25 -1 src/KDbQueryColumnInfo.h
M +18 -4 src/KDbQuerySchema.cpp
M +29 -1 src/KDbQuerySchema_p.h
https://commits.kde.org/kdb/3414fbddffa433b23d43376b44fef5421b734250
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c5f0c4b0..157377b0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,7 +3,7 @@ find_package(ECM 1.8.0 REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules ${ECM_MODULE_PATH} \
${ECM_KDE_MODULE_DIR}) include(SetKDbCMakePolicies NO_POLICY_SCOPE)
-project(KDb VERSION 3.1.90) # Update this
+project(KDb VERSION 3.1.91) # Update this
include(KDbAddTests)
include(KDbAddExamples)
diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt
index ee4d65a6..bd344989 100644
--- a/autotests/CMakeLists.txt
+++ b/autotests/CMakeLists.txt
@@ -43,6 +43,7 @@ ecm_add_tests(
DriverTest.cpp
ExpressionsTest.cpp
MissingTableTest.cpp
+ OrderByColumnTest.cpp
QuerySchemaTest.cpp
KDbTest.cpp
@@ -52,6 +53,8 @@ ecm_add_tests(
target_compile_definitions(MissingTableTest PRIVATE \
-DFILES_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data" )
+target_compile_definitions(OrderByColumnTest PRIVATE KDB_DEPRECATED=)
+
if(NOT WIN32) #TODO enable for Windows when headers_test.sh is ported e.g. to python
add_subdirectory(headers)
endif()
diff --git a/autotests/OrderByColumnTest.cpp b/autotests/OrderByColumnTest.cpp
new file mode 100644
index 00000000..d0b7e053
--- /dev/null
+++ b/autotests/OrderByColumnTest.cpp
@@ -0,0 +1,212 @@
+/* This file is part of the KDE project
+ Copyright (C) 2018 Jarosław Staniek <staniek@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "OrderByColumnTest.h"
+
+#include <KDbExpression>
+#include <KDbOrderByColumn>
+#include <KDbQueryAsterisk>
+#include <KDbQuerySchema>
+#include <KDbNativeStatementBuilder>
+
+#include <QTest>
+
+QTEST_GUILESS_MAIN(OrderByColumnTest)
+
+void OrderByColumnTest::initTestCase()
+{
+}
+
+void OrderByColumnTest::testSelect1Query()
+{
+ QVERIFY(utils.testCreateDbWithTables("OrderByColumnTest"));
+ KDbQuerySchema query;
+ KDbField *oneField = new KDbField;
+ oneField->setExpression(KDbConstExpression(KDbToken::CHARACTER_STRING_LITERAL, \
"foo")); + query.addField(oneField);
+ KDbOrderByColumnList* orderBy = query.orderByColumnList();
+ QVERIFY(orderBy);
+ QVERIFY(orderBy->isEmpty());
+ QCOMPARE(orderBy->count(), 0);
+ orderBy->appendField(oneField);
+ KDbConnection *conn = utils.connection.data();
+
+ // automatic alias "expr1"
+ KDbEscapedString sql;
+ QVERIFY(utils.kdbBuilder()->generateSelectStatement(&sql, &query));
+ QCOMPARE(sql, "SELECT 'foo' AS expr1 ORDER BY expr1");
+ QVERIFY(!orderBy->isEmpty());
+ QCOMPARE(orderBy->count(), 1);
+ const int indexOfField = query.indexOf(*oneField);
+ QCOMPARE(indexOfField, 0);
+ const QString alias(query.columnAlias(indexOfField));
+ QVERIFY(!alias.isEmpty());
+ KDbOrderByColumn *orderByColumn = orderBy->value(indexOfField);
+ QVERIFY(orderByColumn);
+ QVERIFY(!orderByColumn->column());
+ QCOMPARE(orderByColumn->field(), oneField);
+ QVERIFY(!orderBy->value(orderBy->count() + 10));
+ KDbEscapedString orderBySqlOldApi = orderBy->toSqlString(true, conn, \
KDb::KDbEscaping); + QCOMPARE(orderBySqlOldApi, ""); // alias is not used
+ KDbEscapedString orderBySql = orderBy->toSqlString(true, conn, &query, \
KDb::KDbEscaping); + QCOMPARE(orderBySql, alias); // alias is used to point to the \
column "'foo'" +
+ // change alias to something other than valid ID
+ QVERIFY(query.setColumnAlias(indexOfField, "0"));
+ QVERIFY(utils.kdbBuilder()->generateSelectStatement(&sql, &query));
+ QCOMPARE(sql, "SELECT 'foo' AS \"0\" ORDER BY \"0\"");
+ orderBySqlOldApi = orderBy->toSqlString(true, conn, KDb::KDbEscaping);
+ QCOMPARE(orderBySqlOldApi, ""); // alias is not used
+ orderBySql = orderBy->toSqlString(true, conn, &query, KDb::KDbEscaping);
+ QCOMPARE(orderBySql, "\"0\""); // alias is used to point to the column "'foo'"
+}
+
+void OrderByColumnTest::testOrderByIndex()
+{
+ QVERIFY(utils.testCreateDbWithTables("OrderByColumnTest"));
+ KDbQuerySchema query;
+ KDbTableSchema *carsTable = utils.connection->tableSchema("cars");
+ QVERIFY(carsTable);
+ query.addTable(carsTable);
+ query.addAsterisk(new KDbQueryAsterisk(&query));
+ KDbOrderByColumnList* orderBy = query.orderByColumnList();
+ KDbConnection *conn = utils.connection.data();
+
+ // "SELECT * FROM cars ORDER BY model ASC, owner DESC"
+ QVERIFY(query.orderByColumnList()->isEmpty());
+ QVERIFY(orderBy->appendColumn(conn, &query,
+ KDbOrderByColumn::SortOrder::Ascending, 2));
+ QVERIFY(orderBy->appendColumn(conn, &query,
+ KDbOrderByColumn::SortOrder::Descending, 1));
+ KDbEscapedString sql;
+ QVERIFY(utils.kdbBuilder()->generateSelectStatement(&sql, &query));
+ QCOMPARE(sql, "SELECT cars.* FROM cars ORDER BY 3, 2 DESC");
+
+ QVERIFY2(!orderBy->appendColumn(conn, &query,
+ KDbOrderByColumn::SortOrder::Ascending, 3),
+ "appendField for null");
+}
+
+void OrderByColumnTest::testOrderByColumnName()
+{
+ QVERIFY(utils.testCreateDbWithTables("OrderByColumnTest"));
+ KDbQuerySchema query;
+ KDbTableSchema *carsTable = utils.connection->tableSchema("cars");
+ QVERIFY(carsTable);
+ query.addTable(carsTable);
+ query.addAsterisk(new KDbQueryAsterisk(&query));
+
+ // "SELECT * FROM cars ORDER BY model, owner"
+ QVERIFY(query.orderByColumnList());
+ QVERIFY(query.orderByColumnList()->isEmpty());
+ QCOMPARE(query.orderByColumnList()->count(), 0);
+
+ KDbOrderByColumnList* orderBy = query.orderByColumnList();
+ QVERIFY(orderBy);
+ QVERIFY(orderBy->isEmpty());
+ KDbField *modelField = carsTable->field("model");
+ QVERIFY(modelField);
+ KDbField *ownerField = carsTable->field("owner");
+ QVERIFY(ownerField);
+ orderBy->appendField(modelField);
+ orderBy->appendField(ownerField);
+ KDbConnection *conn = utils.connection.data();
+ KDbEscapedString orderBySql = orderBy->toSqlString(true, conn, &query, \
KDb::KDbEscaping); + QCOMPARE(orderBySql, "cars.model, cars.owner");
+
+ KDbEscapedString sql;
+ QVERIFY(utils.kdbBuilder()->generateSelectStatement(&sql, &query));
+ QCOMPARE(sql, "SELECT cars.* FROM cars ORDER BY model, owner");
+ QVERIFY(utils.driverBuilder()->generateSelectStatement(&sql, &query));
+ QCOMPARE(sql, "SELECT [cars].* FROM [cars] ORDER BY [model] COLLATE '', \
[owner]"); +
+ // "SELECT * FROM cars ORDER BY model ASC, owner DESC"
+ orderBy->clear();
+ QVERIFY(query.orderByColumnList()->isEmpty());
+ orderBy->appendField(modelField, KDbOrderByColumn::SortOrder::Ascending);
+ orderBy->appendField(ownerField, KDbOrderByColumn::SortOrder::Descending);
+ QVERIFY(utils.kdbBuilder()->generateSelectStatement(&sql, &query));
+ const char validSelect1[] = "SELECT cars.* FROM cars ORDER BY model, owner \
DESC"; + QCOMPARE(sql, validSelect1);
+ QVERIFY(utils.driverBuilder()->generateSelectStatement(&sql, &query));
+ const char validDriverSelect1[] = "SELECT [cars].* FROM [cars] ORDER BY [model] \
COLLATE '', [owner] DESC"; + QCOMPARE(sql, validDriverSelect1);
+
+ // The same query, adding null field
+ orderBy->clear();
+ QVERIFY(query.orderByColumnList()->isEmpty());
+ orderBy->appendField(nullptr);
+ QVERIFY2(query.orderByColumnList()->isEmpty(), "Adding null fields should not \
affect OREDR BY"); + orderBy->appendField(modelField, \
KDbOrderByColumn::SortOrder::Ascending); + orderBy->appendField(ownerField, \
KDbOrderByColumn::SortOrder::Descending); + orderBy->appendField(nullptr);
+ QVERIFY(utils.kdbBuilder()->generateSelectStatement(&sql, &query));
+ QCOMPARE(sql, validSelect1);
+ QVERIFY(utils.driverBuilder()->generateSelectStatement(&sql, &query));
+ QCOMPARE(sql, validDriverSelect1);
+
+ // The same query, overload
+ orderBy->clear();
+ QVERIFY(query.orderByColumnList()->isEmpty());
+ QVERIFY(orderBy->appendFields(conn, &query,
+ "model", KDbOrderByColumn::SortOrder::Ascending,
+ "owner", \
KDbOrderByColumn::SortOrder::Descending)); + \
QVERIFY(utils.kdbBuilder()->generateSelectStatement(&sql, &query)); + \
QCOMPARE(sql, validSelect1); + \
QVERIFY(utils.driverBuilder()->generateSelectStatement(&sql, &query)); + \
QCOMPARE(sql, validDriverSelect1); +
+ // The same query, overload
+ orderBy->clear();
+ QVERIFY(query.orderByColumnList()->isEmpty());
+ QVERIFY(orderBy->appendField(conn, &query, "model", \
KDbOrderByColumn::SortOrder::Ascending)); + QVERIFY(orderBy->appendField(conn, \
&query, "owner", KDbOrderByColumn::SortOrder::Descending)); + \
QVERIFY(utils.kdbBuilder()->generateSelectStatement(&sql, &query)); + \
QCOMPARE(sql, validSelect1); + \
QVERIFY(utils.driverBuilder()->generateSelectStatement(&sql, &query)); + \
QCOMPARE(sql, validDriverSelect1); +
+ QCOMPARE(orderBy->count(), 2);
+ QVERIFY2(!orderBy->appendField(conn, &query, ""), "appendField for null");
+ QCOMPARE(orderBy->count(), 2);
+
+ // The same query, overload
+ orderBy->clear();
+ QCOMPARE(orderBy->count(), 0);
+ QVERIFY(query.orderByColumnList()->isEmpty());
+ KDbQueryColumnInfo::Vector columns = query.fieldsExpanded(conn);
+ KDbQueryColumnInfo *ownerColumnInfo = columns.value(1);
+ QVERIFY(ownerColumnInfo);
+ KDbQueryColumnInfo *modelColumnInfo = columns.value(2);
+ QVERIFY(modelColumnInfo);
+ orderBy->appendColumn(modelColumnInfo, KDbOrderByColumn::SortOrder::Ascending);
+ orderBy->appendColumn(ownerColumnInfo, KDbOrderByColumn::SortOrder::Descending);
+ QCOMPARE(orderBy->count(), 2);
+ QVERIFY(utils.kdbBuilder()->generateSelectStatement(&sql, &query));
+ QCOMPARE(sql, validSelect1);
+ QVERIFY(utils.driverBuilder()->generateSelectStatement(&sql, &query));
+ QCOMPARE(sql, validDriverSelect1);
+}
+
+//! @todo Test KDbQuerySchema::setOrderByColumnList
+//! @todo Test more KDbOrderByColumnList and KDbOrderByColumn
+
+void OrderByColumnTest::cleanupTestCase()
+{
+}
diff --git a/autotests/OrderByColumnTest.h b/autotests/OrderByColumnTest.h
new file mode 100644
index 00000000..2b226b16
--- /dev/null
+++ b/autotests/OrderByColumnTest.h
@@ -0,0 +1,46 @@
+/* This file is part of the KDE project
+ Copyright (C) 2018 Jarosław Staniek <staniek@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KDBORDERBYCOLUMNTEST_H
+#define KDBORDERBYCOLUMNTEST_H
+
+#include "KDbTestUtils.h"
+
+class OrderByColumnTest : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void initTestCase();
+
+ //! Test ORDER BY data for "SELECT 'foo'" query
+ void testSelect1Query();
+
+ //! Test ORDER BY data for "SELECT * FROM cars ORDER BY 2"
+ void testOrderByIndex();
+
+ //! Test ORDER BY data for "SELECT * FROM cars ORDER BY model"
+ void testOrderByColumnName();
+
+ void cleanupTestCase();
+
+private:
+ KDbTestUtils utils;
+};
+
+#endif
diff --git a/src/KDbNativeStatementBuilder.cpp b/src/KDbNativeStatementBuilder.cpp
index e04b23d0..e1d82961 100644
--- a/src/KDbNativeStatementBuilder.cpp
+++ b/src/KDbNativeStatementBuilder.cpp
@@ -389,7 +389,7 @@ static bool selectStatementInternal(KDbEscapedString *target,
// ORDER BY
KDbEscapedString orderByString(querySchema->orderByColumnList()->toSqlString(
- !singleTable /*includeTableName*/, connection, dialect));
+ !singleTable /*includeTableName*/, connection, querySchema, dialect));
const QVector<int> pkeyFieldsOrder(querySchema->pkeyFieldsOrder(connection));
if (dialect == KDb::DriverEscaping && orderByString.isEmpty() && \
!pkeyFieldsOrder.isEmpty()) {
// Native only: add automatic ORDER BY if there is no explicitly defined one
@@ -408,7 +408,7 @@ static bool selectStatementInternal(KDbEscapedString *target,
automaticPKOrderBy.appendColumn(ci);
}
orderByString = automaticPKOrderBy.toSqlString(!singleTable \
/*includeTableName*/,
- connection, dialect);
+ connection, querySchema, \
dialect); }
if (!orderByString.isEmpty())
sql += (" ORDER BY " + orderByString);
diff --git a/src/KDbOrderByColumn.cpp b/src/KDbOrderByColumn.cpp
index 6986cd95..4155db7a 100644
--- a/src/KDbOrderByColumn.cpp
+++ b/src/KDbOrderByColumn.cpp
@@ -1,5 +1,5 @@
/* This file is part of the KDE project
- Copyright (C) 2003-2016 Jarosław Staniek <staniek@kde.org>
+ Copyright (C) 2003-2018 Jarosław Staniek <staniek@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -27,7 +27,7 @@ class Q_DECL_HIDDEN KDbOrderByColumn::Private
{
public:
Private()
- : column(nullptr)
+ : columnIndex(-1)
, pos(-1)
, field(nullptr)
, order(KDbOrderByColumn::SortOrder::Ascending)
@@ -36,9 +36,24 @@ public:
Private(const Private &other) {
copy(other);
}
-#define KDbOrderByColumnPrivateArgs(o) std::tie(o.column, o.pos, o.field, o.order)
- Private(KDbQueryColumnInfo* aColumn, int aPos, KDbField* aField, \
KDbOrderByColumn::SortOrder aOrder) {
- KDbOrderByColumnPrivateArgs((*this)) = std::tie(aColumn, aPos, aField, \
aOrder); +#define KDbOrderByColumnPrivateArgs(o) std::tie(o.querySchema, \
o.connection, o.columnIndex, o.pos, o.field, o.order) + \
Private(KDbQueryColumnInfo* aColumn, int aPos, KDbField* aField, \
KDbOrderByColumn::SortOrder aOrder) + {
+ const KDbQuerySchema *foundQuerySchema = nullptr;
+ KDbConnection *foundConnection = nullptr;
+ int foundColumnIndex = -1;
+ if (aColumn) {
+ foundQuerySchema =aColumn->querySchema();
+ foundConnection = aColumn->connection();
+ const KDbQueryColumnInfo::Vector fieldsExpanded = \
foundQuerySchema->fieldsExpanded( + foundConnection, \
KDbQuerySchema::FieldsExpandedMode::WithInternalFields); + \
foundColumnIndex = fieldsExpanded.indexOf(aColumn); + if (foundColumnIndex \
< 0) { + kdbWarning() << "Column not found in query:" << *aColumn;
+ }
+ }
+ KDbOrderByColumnPrivateArgs((*this))
+ = std::tie(foundQuerySchema, foundConnection, foundColumnIndex, aPos, \
aField, aOrder); }
void copy(const Private &other) {
KDbOrderByColumnPrivateArgs((*this)) = KDbOrderByColumnPrivateArgs(other);
@@ -47,8 +62,18 @@ public:
return KDbOrderByColumnPrivateArgs((*this)) == \
KDbOrderByColumnPrivateArgs(other); }
- //! Column to sort, @c nullptr if field is non-0.
- KDbQueryColumnInfo* column;
+ //! Query schema that owns the KDbQueryColumnInfo and thus also this \
KDbOrderByColumn object. + //! Cached for performance, can be cached since \
lifetime of the KDbOrderByColumn object depends + //! on the query. @c nullptr if \
columnIndex is not provided. @since 3.2 + const KDbQuerySchema *querySchema = \
nullptr; +
+ //! Connection used to compute expanded fields. Like querySchema, connection is \
cached for + //! performance and can be cached since lifetime of the \
KDbOrderByColumn object depends on the + //! connection. @c nullptr if columnIndex \
is not provided. @since 3.2 + KDbConnection *connection = nullptr;
+
+ //! Index of column to sort, -1 if field is present. @since 3.2
+ int columnIndex;
//! Value that indicates that column to sort (columnIndex) has been specified by \
providing its
//! position, not name. For example, using "SELECT a, b FROM T ORDER BY 2".
@@ -96,22 +121,17 @@ KDbOrderByColumn *KDbOrderByColumn::copy(KDbConnection *conn, \
KDbQuerySchema *fr if (d->field) {
return new KDbOrderByColumn(d->field, d->order);
}
- if (d->column) {
+ if (d->columnIndex >= 0) {
KDbQueryColumnInfo* columnInfo;
if (fromQuery && toQuery) {
- int columnIndex = fromQuery->columnsOrder(conn).value(d->column);
- if (columnIndex < 0) {
- kdbWarning() << "Index not found for column" << *d->column;
- return nullptr;
- }
- columnInfo = toQuery->expandedOrInternalField(conn, columnIndex);
+ columnInfo = toQuery->expandedOrInternalField(conn, d->columnIndex);
if (!columnInfo) {
- kdbWarning() << "Column info not found at index" << columnIndex << \
"in toQuery"; + kdbWarning() << "Column info not found at index" << \
d->columnIndex << "in toQuery"; return nullptr;
}
}
else {
- columnInfo = d->column;
+ columnInfo = column();
}
return new KDbOrderByColumn(columnInfo, d->order, d->pos);
}
@@ -120,7 +140,10 @@ KDbOrderByColumn *KDbOrderByColumn::copy(KDbConnection *conn, \
KDbQuerySchema *fr
KDbQueryColumnInfo* KDbOrderByColumn::column() const
{
- return d->column;
+ if (d->columnIndex < 0 || !d->querySchema || !d->connection) {
+ return nullptr;
+ }
+ return d->querySchema->expandedOrInternalField(d->connection, d->columnIndex);
}
int KDbOrderByColumn::position() const
@@ -179,38 +202,67 @@ QDebug operator<<(QDebug dbg, const KDbOrderByColumn& order)
KDbEscapedString KDbOrderByColumn::toSqlString(bool includeTableName,
KDbConnection *conn,
+ KDbQuerySchema *query,
KDb::IdentifierEscapingType \
escapingType) const {
const QByteArray orderString(d->order == KDbOrderByColumn::SortOrder::Ascending \
? "" : " DESC"); KDbEscapedString fieldName, tableName, collationString;
- if (d->column) {
+ KDbQueryColumnInfo *col = column();
+ if (col) {
if (d->pos > -1)
return KDbEscapedString::number(d->pos + 1) + orderString;
else {
- if (includeTableName && d->column->alias().isEmpty()) {
- tableName = \
KDbEscapedString(escapeIdentifier(d->column->field()->table()->name(), conn, \
escapingType)); + if (includeTableName && col->field()->table() && \
col->alias().isEmpty()) { + tableName = \
KDbEscapedString(escapeIdentifier(col->field()->table()->name(), conn, \
escapingType)); tableName += '.';
}
- fieldName = KDbEscapedString(escapeIdentifier(d->column->aliasOrName(), \
conn, escapingType)); + fieldName = \
KDbEscapedString(escapeIdentifier(col->aliasOrName(), conn, escapingType)); }
- if (d->column->field()->isTextType() && escapingType == KDb::DriverEscaping) \
{ + if (conn && col->field()->isTextType() && escapingType == \
KDb::DriverEscaping) { collationString = conn->driver()->collationSql();
}
}
else {
- if (d->field && includeTableName) {
+ QString aliasOrName;
+ if (includeTableName && d->field && d->field->table()) {
tableName = KDbEscapedString(escapeIdentifier(d->field->table()->name(), \
conn, escapingType)); tableName += '.';
+ } else if (d->field && conn && query) {
+ if (d->field->isExpression()) {
+ const int indexOfField = query->indexOf(*d->field);
+ aliasOrName = query->columnAlias(indexOfField);
+ if (aliasOrName.isEmpty()) {
+ kdbWarning() << "This field does not belong to specified query:" \
<< *d->field + << endl << "cannot find alias";
+ aliasOrName = QLatin1String("?unknown_field?");
+ }
+ } else {
+ KDbQueryColumnInfo *ci = query->columnInfo(conn, d->field->name());
+ if (ci) {
+ aliasOrName = ci->aliasOrName();
+ }
+ }
+ }
+ if (aliasOrName.isEmpty()) {
+ // The field is not present on the SELECT list but is still correct,
+ // e.g. SELECT id FROM cars ORDER BY owner
+ aliasOrName = d->field ? d->field->name() : \
QLatin1String("?missing_field?")/*error*/; }
- fieldName = KDbEscapedString(escapeIdentifier(
- d->field ? d->field->name() : QLatin1String("??")/*error*/, conn, \
escapingType));
- if (d->field && d->field->isTextType() && escapingType == \
KDb::DriverEscaping) { + fieldName = \
KDbEscapedString(escapeIdentifier(aliasOrName, conn, escapingType)); + if \
(conn && d->field && d->field->isTextType() && escapingType == KDb::DriverEscaping) { \
collationString = conn->driver()->collationSql(); }
}
return tableName + fieldName + collationString + orderString;
}
+KDbEscapedString KDbOrderByColumn::toSqlString(bool includeTableName,
+ KDbConnection *conn,
+ KDb::IdentifierEscapingType \
escapingType) const +{
+ return toSqlString(includeTableName, conn, nullptr, escapingType);
+}
+
//=======================================
class Q_DECL_HIDDEN KDbOrderByColumnList::Private
@@ -395,17 +447,24 @@ QDebug operator<<(QDebug dbg, const KDbOrderByColumnList& list)
}
KDbEscapedString KDbOrderByColumnList::toSqlString(bool includeTableNames, \
KDbConnection *conn,
- KDb::IdentifierEscapingType escapingType) \
const + KDbQuerySchema *query,
+ KDb::IdentifierEscapingType \
escapingType) const {
KDbEscapedString string;
for (QList<KDbOrderByColumn*>::ConstIterator it(constBegin()); it != constEnd(); \
++it) { if (!string.isEmpty())
string += ", ";
- string += (*it)->toSqlString(includeTableNames, conn, escapingType);
+ string += (*it)->toSqlString(includeTableNames, conn, query, escapingType);
}
return string;
}
+KDbEscapedString KDbOrderByColumnList::toSqlString(bool includeTableNames, \
KDbConnection *conn, + \
KDb::IdentifierEscapingType escapingType) const +{
+ return toSqlString(includeTableNames, conn, nullptr, escapingType);
+}
+
void KDbOrderByColumnList::clear()
{
qDeleteAll(d->data);
diff --git a/src/KDbOrderByColumn.h b/src/KDbOrderByColumn.h
index 47408e0c..96c6cdb6 100644
--- a/src/KDbOrderByColumn.h
+++ b/src/KDbOrderByColumn.h
@@ -1,5 +1,5 @@
/* This file is part of the KDE project
- Copyright (C) 2003-2016 Jarosław Staniek <staniek@kde.org>
+ Copyright (C) 2003-2018 Jarosław Staniek <staniek@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -89,13 +89,26 @@ public:
//! @since 3.1
inline bool operator!=(const KDbOrderByColumn &other) const { return \
!operator==(other); }
- /*! @return a string like "name ASC" usable for building an SQL statement.
- If @a includeTableNames is true (the default) field is output in a form
- of "tablename.fieldname" (but only if fieldname is not a name of alias).
+ /** Return an SQL string like "name ASC" or "2 DESC" usable for building an SQL \
statement + *
+ * If @a includeTableNames is @c true fields that are related to a table are
+ * printed as "tablename.fieldname".
+ *
+ * @a escapingType can be used to alter default escaping type.
+ * If @a conn is not provided for DriverEscaping, no escaping is performed.
+ * If @a query is provided, it can be used to obtain alias information.
+ *
+ * @since 3.2
+ */
+ KDbEscapedString toSqlString(bool includeTableName,
+ KDbConnection *conn, KDbQuerySchema *query,
+ KDb::IdentifierEscapingType escapingType = \
KDb::DriverEscaping) const;
- @a escapingType can be used to alter default escaping type.
- If @a conn is not provided for DriverEscaping, no escaping is performed. */
- KDbEscapedString toSqlString(bool includeTableName = true,
+ /*! @overload
+
+ @deprecated since 3.2, use overload that also takes query schema
+ */
+ KDB_DEPRECATED KDbEscapedString toSqlString(bool includeTableName = true,
KDbConnection *conn = nullptr,
KDb::IdentifierEscapingType escapingType = \
KDb::DriverEscaping) const;
@@ -201,15 +214,29 @@ public:
*/
QList<KDbOrderByColumn*>::ConstIterator constEnd() const;
- /*! @return an SQL string like "name ASC, 2 DESC" usable for building an SQL \
statement.
- If @a includeTableNames is true (the default) fields are output in a form
- of "tablename.fieldname".
+ /** Return an SQL string like "name ASC, 2 DESC" usable for building an SQL \
statement + *
+ * If @a includeTableNames is @c true (the default) fields that are related to a \
table are + * printed as "tablename.fieldname".
+ *
+ * @a escapingType can be used to alter default escaping type.
+ * If @a conn is not provided for DriverEscaping, no escaping is performed.
+ * If @a query is provided, it can be used to obtain alias information.
+ *
+ * @since 3.2
+ */
+ KDbEscapedString toSqlString(bool includeTableNames,
+ KDbConnection *conn, KDbQuerySchema *query,
+ KDb::IdentifierEscapingType escapingType = \
KDb::DriverEscaping) const; +
+ /*! @overload
- @a escapingType can be used to alter default escaping type.
- If @a conn is not provided for DriverEscaping, no escaping is performed. */
- KDbEscapedString toSqlString(bool includeTableNames = true,
+ @deprecated since 3.2, use overload that also takes query schema
+ */
+ KDB_DEPRECATED KDbEscapedString toSqlString(bool includeTableNames = true,
KDbConnection *conn = nullptr,
KDb::IdentifierEscapingType escapingType = \
KDb::DriverEscaping) const; +
private:
class Private;
Private * const d;
diff --git a/src/KDbQueryColumnInfo.cpp b/src/KDbQueryColumnInfo.cpp
index 123ea45b..e00afa4f 100644
--- a/src/KDbQueryColumnInfo.cpp
+++ b/src/KDbQueryColumnInfo.cpp
@@ -1,5 +1,5 @@
/* This file is part of the KDE project
- Copyright (C) 2003-2016 Jarosław Staniek <staniek@kde.org>
+ Copyright (C) 2003-2018 Jarosław Staniek <staniek@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -18,38 +18,12 @@
*/
#include "KDbQueryColumnInfo.h"
+#include "KDbQuerySchema_p.h"
#include "KDbTableSchema.h"
#include "KDbField.h"
#include "KDbField_p.h"
#include "kdb_debug.h"
-//! @internal
-class Q_DECL_HIDDEN KDbQueryColumnInfo::Private
-{
-public:
- Private(KDbField *f, const QString& a, bool v, KDbQueryColumnInfo *foreign)
- : field(f)
- , alias(a)
- , visible(v)
- , indexForVisibleLookupValue(-1)
- , foreignColumn(foreign)
- {
- }
-
- KDbField *field;
- QString alias;
-
- //! @c true if this column is visible to the user (and its data is fetched by \
the engine)
- bool visible;
-
- /*! Index of column with visible lookup value within the 'fields expanded' \
vector.
- @see KDbQueryColumnInfo::indexForVisibleLookupValue() */
- int indexForVisibleLookupValue;
-
- //! Non-nullptr if this column is a visible column for @a foreignColumn
- KDbQueryColumnInfo *foreignColumn;
-};
-
KDbQueryColumnInfo::KDbQueryColumnInfo(KDbField *f, const QString& alias, bool \
visible, KDbQueryColumnInfo *foreignColumn)
: d(new Private(f, alias, visible, foreignColumn))
@@ -126,6 +100,21 @@ const KDbQueryColumnInfo *KDbQueryColumnInfo::foreignColumn() \
const return d->foreignColumn;
}
+const KDbQuerySchema* KDbQueryColumnInfo::querySchema() const
+{
+ return d->querySchema;
+}
+
+KDbConnection* KDbQueryColumnInfo::connection()
+{
+ return d->connection;
+}
+
+const KDbConnection* KDbQueryColumnInfo::connection() const
+{
+ return d->connection;
+}
+
QDebug operator<<(QDebug dbg, const KDbQueryColumnInfo& info)
{
QString fieldName;
diff --git a/src/KDbQueryColumnInfo.h b/src/KDbQueryColumnInfo.h
index c84b71c3..3a0fcfd1 100644
--- a/src/KDbQueryColumnInfo.h
+++ b/src/KDbQueryColumnInfo.h
@@ -1,5 +1,5 @@
/* This file is part of the KDE project
- Copyright (C) 2003-2016 Jarosław Staniek <staniek@kde.org>
+ Copyright (C) 2003-2018 Jarosław Staniek <staniek@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -26,7 +26,9 @@
#include <QVector>
#include <QString>
+class KDbConnection;
class KDbField;
+class KDbQuerySchema;
//! @short Helper class that assigns additional information for the column in a \
query /*! The following information is assigned:
@@ -89,7 +91,29 @@ public:
//! @overload KDbQueryColumnInfo *foreignColumn();
const KDbQueryColumnInfo *foreignColumn() const;
+ /**
+ * Returns query schema for this column
+ *
+ * @since 3.2
+ */
+ const KDbQuerySchema* querySchema() const;
+
+ /**
+ * Returns connection for this column
+ *
+ * @since 3.2
+ */
+ KDbConnection* connection();
+
+ /**
+ * @overload
+ *
+ * @since 3.2
+ */
+ const KDbConnection* connection() const;
+
private:
+ friend class KDbQuerySchema;
class Private;
Private * const d;
Q_DISABLE_COPY(KDbQueryColumnInfo)
diff --git a/src/KDbQuerySchema.cpp b/src/KDbQuerySchema.cpp
index 2a262cb7..e03b3652 100644
--- a/src/KDbQuerySchema.cpp
+++ b/src/KDbQuerySchema.cpp
@@ -749,6 +749,8 @@ KDbQueryColumnInfo::Vector \
KDbQuerySchema::fieldsExpandedInternal( if (!d->fakeRecordIdField) {
d->fakeRecordIdField = new KDbField(QLatin1String("rowID"), \
KDbField::BigInteger);
d->fakeRecordIdCol = new \
KDbQueryColumnInfo(d->fakeRecordIdField, QString(), true); + \
d->fakeRecordIdCol->d->querySchema = this; + \
d->fakeRecordIdCol->d->connection = conn; }
tmpFieldsExpandedWithInternal[fieldsExpandedVectorSize + \
internalFieldCount] = d->fakeRecordIdCol; }
@@ -821,6 +823,8 @@ KDbQuerySchemaFieldsExpanded \
*KDbQuerySchema::computeFieldsExpanded(KDbConnectio foreach(KDbField *ast_f, \
*ast_fields) {
KDbQueryColumnInfo *ci = new KDbQueryColumnInfo(ast_f, \
QString()/*no field for asterisk!*/, isColumnVisible(fieldPosition));
+ ci->d->querySchema = this;
+ ci->d->connection = conn;
list.append(ci);
kdbDebug() << "caching (unexpanded) columns order:" << *ci << \
"at position" << fieldPosition; cache->columnsOrder.insert(ci, fieldPosition);
@@ -835,6 +839,8 @@ KDbQuerySchemaFieldsExpanded \
*KDbQuerySchema::computeFieldsExpanded(KDbConnectio // list.append(tab_f);
KDbQueryColumnInfo *ci = new KDbQueryColumnInfo(tab_f, \
QString()/*no field for asterisk!*/, isColumnVisible(fieldPosition));
+ ci->d->querySchema = this;
+ ci->d->connection = conn;
list.append(ci);
kdbDebug() << "caching (unexpanded) columns order:" << *ci \
<< "at position" << fieldPosition; cache->columnsOrder.insert(ci, fieldPosition);
@@ -844,6 +850,8 @@ KDbQuerySchemaFieldsExpanded \
*KDbQuerySchema::computeFieldsExpanded(KDbConnectio } else {
//a single field
KDbQueryColumnInfo *ci = new KDbQueryColumnInfo(f, \
columnAlias(fieldPosition), isColumnVisible(fieldPosition)); + \
ci->d->querySchema = this; + ci->d->connection = conn;
list.append(ci);
columnInfosOutsideAsterisks.insert(ci, true);
kdbDebug() << "caching (unexpanded) column's order:" << *ci << "at \
position" << fieldPosition; @@ -883,8 +891,11 @@ KDbQuerySchemaFieldsExpanded \
*KDbQuerySchema::computeFieldsExpanded(KDbConnectio
cache->ownedVisibleFields.append(visibleColumn); // \
remember to delete later }
- lookup_list.append(
- new KDbQueryColumnInfo(visibleColumn, QString(), \
true/*visible*/, ci/*foreign*/)); + KDbQueryColumnInfo *lookupCi = \
new KDbQueryColumnInfo( + visibleColumn, QString(), true \
/*visible*/, ci /*foreign*/); + lookupCi->d->querySchema = this;
+ lookupCi->d->connection = conn;
+ lookup_list.append(lookupCi);
/*
//add visibleField to the list of SELECTed fields if \
it is not yes present there
if (!findTableField( \
visibleField->table()->name()+"."+visibleField->name() )) { @@ -938,8 +949,11 @@ \
KDbQuerySchemaFieldsExpanded \
*KDbQuerySchema::computeFieldsExpanded(KDbConnectio
cache->ownedVisibleFields.append(visibleColumn); // remember \
to delete later }
- lookup_list.append(
- new KDbQueryColumnInfo(visibleColumn, QString(), \
true/*visible*/, ci/*foreign*/)); + KDbQueryColumnInfo *lookupCi = new \
KDbQueryColumnInfo( + visibleColumn, QString(), true /*visible*/, \
ci /*foreign*/); + lookupCi->d->querySchema = this;
+ lookupCi->d->connection = conn;
+ lookup_list.append(lookupCi);
/*
//add visibleField to the list of SELECTed fields if it is \
not yes present there
if (!findTableField( \
visibleField->table()->name()+"."+visibleField->name() )) {
diff --git a/src/KDbQuerySchema_p.h b/src/KDbQuerySchema_p.h
index 30a5430d..90e15d71 100644
--- a/src/KDbQuerySchema_p.h
+++ b/src/KDbQuerySchema_p.h
@@ -22,6 +22,7 @@
#include "KDbDriver.h"
#include "KDbExpression.h"
+#include "KDbQueryColumnInfo.h"
#include "KDbQuerySchema.h"
#include <QBitArray>
@@ -29,7 +30,34 @@
class KDbConnection;
-//! @internal
+class Q_DECL_HIDDEN KDbQueryColumnInfo::Private
+{
+public:
+ Private(KDbField *f, const QString& a, bool v, KDbQueryColumnInfo *foreign)
+ : field(f)
+ , alias(a)
+ , visible(v)
+ , indexForVisibleLookupValue(-1)
+ , foreignColumn(foreign)
+ {
+ }
+
+ KDbConnection *connection = nullptr; //!< Used to relate KDbQueryColumnInfo with \
query. @since 3.2 + const KDbQuerySchema *querySchema = nullptr; //!< Used to \
relate KDbQueryColumnInfo with query. @since 3.2 + KDbField *field;
+ QString alias;
+
+ //! @c true if this column is visible to the user (and its data is fetched by \
the engine) + bool visible;
+
+ /*! Index of column with visible lookup value within the 'fields expanded' \
vector. + @see KDbQueryColumnInfo::indexForVisibleLookupValue() */
+ int indexForVisibleLookupValue;
+
+ //! Non-nullptr if this column is a visible column for @a foreignColumn
+ KDbQueryColumnInfo *foreignColumn;
+};
+
class KDbQuerySchemaPrivate
{
Q_DECLARE_TR_FUNCTIONS(KDbQuerySchema)
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic