KDb

KDbOrderByColumn.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2003-2018 Jarosław Staniek <staniek@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20#include "KDbOrderByColumn.h"
21#include "KDbQuerySchema.h"
22#include "KDbQuerySchema_p.h"
23#include "KDbConnection.h"
24#include "kdb_debug.h"
25
26class Q_DECL_HIDDEN KDbOrderByColumn::Private
27{
28public:
29 Private()
30 : columnIndex(-1)
31 , pos(-1)
32 , field(nullptr)
33 , order(KDbOrderByColumn::SortOrder::Ascending)
34 {
35 }
36 Private(const Private &other) {
37 copy(other);
38 }
39#define KDbOrderByColumnPrivateArgs(o) std::tie(o.querySchema, o.connection, o.columnIndex, o.pos, o.field, o.order)
40 Private(KDbQueryColumnInfo* aColumn, int aPos, KDbField* aField, KDbOrderByColumn::SortOrder aOrder)
41 {
42 const KDbQuerySchema *foundQuerySchema = nullptr;
43 KDbConnection *foundConnection = nullptr;
44 int foundColumnIndex = -1;
45 if (aColumn) {
46 foundQuerySchema =aColumn->querySchema();
47 foundConnection = aColumn->connection();
48 const KDbQueryColumnInfo::Vector fieldsExpanded = foundQuerySchema->fieldsExpanded(
50 foundColumnIndex = fieldsExpanded.indexOf(aColumn);
51 if (foundColumnIndex < 0) {
52 kdbWarning() << "Column not found in query:" << *aColumn;
53 }
54 }
55 KDbOrderByColumnPrivateArgs((*this))
56 = std::tie(foundQuerySchema, foundConnection, foundColumnIndex, aPos, aField, aOrder);
57 }
58 void copy(const Private &other) {
59 KDbOrderByColumnPrivateArgs((*this)) = KDbOrderByColumnPrivateArgs(other);
60 }
61 bool operator==(const Private &other) const {
62 return KDbOrderByColumnPrivateArgs((*this)) == KDbOrderByColumnPrivateArgs(other);
63 }
64
65 //! Query schema that owns the KDbQueryColumnInfo and thus also this KDbOrderByColumn object.
66 //! Cached for performance, can be cached since lifetime of the KDbOrderByColumn object depends
67 //! on the query. @c nullptr if columnIndex is not provided. @since 3.2
68 const KDbQuerySchema *querySchema = nullptr;
69
70 //! Connection used to compute expanded fields. Like querySchema, connection is cached for
71 //! performance and can be cached since lifetime of the KDbOrderByColumn object depends on the
72 //! connection. @c nullptr if columnIndex is not provided. @since 3.2
73 KDbConnection *connection = nullptr;
74
75 //! Index of column to sort, -1 if field is present. @since 3.2
76 int columnIndex;
77
78 //! Value that indicates that column to sort (columnIndex) has been specified by providing its
79 //! position, not name. For example, using "SELECT a, b FROM T ORDER BY 2".
80 //! Value of -1 means that the column to sort has been specified by providing its name (or alias).
81 //! For example "SELECT a, b FROM T ORDER BY b". -1 is the default.
82 int pos;
83
84 //! Used only in case when the second constructor is used.
85 KDbField* field;
86
87 //! Sort order
89};
90
91//----
92
94 : d(new Private)
95{
96}
97
99 : d(new Private(column, pos, nullptr, order))
100{
101}
102
104 : d(new Private(nullptr, -1, field, order))
105{
106}
107
109 : d(new Private(*other.d))
110{
111}
112
113KDbOrderByColumn::~KDbOrderByColumn()
114{
115 delete d;
116}
117
119 KDbQuerySchema *toQuery) const
120{
121 if (d->field) {
122 return new KDbOrderByColumn(d->field, d->order);
123 }
124 if (d->columnIndex >= 0) {
125 KDbQueryColumnInfo* columnInfo;
126 if (fromQuery && toQuery) {
127 columnInfo = toQuery->expandedOrInternalField(conn, d->columnIndex);
128 if (!columnInfo) {
129 kdbWarning() << "Column info not found at index" << d->columnIndex << "in toQuery";
130 return nullptr;
131 }
132 }
133 else {
134 columnInfo = column();
135 }
136 return new KDbOrderByColumn(columnInfo, d->order, d->pos);
137 }
138 return nullptr;
139}
140
142{
143 if (d->columnIndex < 0 || !d->querySchema || !d->connection) {
144 return nullptr;
145 }
146 return d->querySchema->expandedOrInternalField(d->connection, d->columnIndex);
147}
148
150{
151 return d->pos;
152}
153
155{
156 return d->field;
157}
158
160{
161 return d->order;
162}
163
165{
166 if (this != &other) {
167 *d = *other.d;
168 }
169 return *this;
170}
171
173{
174 return *d == *col.d;
175}
176
177QDebug operator<<(QDebug dbg, const KDbOrderByColumn& order)
178{
179 const QLatin1String orderString(
180 order.sortOrder() == KDbOrderByColumn::SortOrder::Ascending ? "ASCENDING" : "DESCENDING");
181 if (order.column()) {
182 if (order.position() > -1) {
183 dbg.nospace() << qPrintable(QString::fromLatin1("COLUMN_AT_POSITION_%1(").arg(order.position() + 1))
184 << *order.column() << ','
185 << qPrintable(orderString) << ')';
186 return dbg.space();
187 }
188 else {
189 dbg.nospace() << "COLUMN(" << *order.column() << ',';
190 dbg.nospace() << qPrintable(orderString) << ')';
191 return dbg.space();
192 }
193 }
194 if (order.field()) {
195 dbg.nospace() << "FIELD(" << *order.field() << ',';
196 dbg.nospace() << qPrintable(orderString) << ')';
197 return dbg.space();
198 }
199 dbg.nospace() << "NONE";
200 return dbg.space();
201}
202
204 KDbConnection *conn,
205 KDbQuerySchema *query,
206 KDb::IdentifierEscapingType escapingType) const
207{
208 const QByteArray orderString(d->order == KDbOrderByColumn::SortOrder::Ascending ? "" : " DESC");
209 KDbEscapedString fieldName, tableName, collationString;
210 KDbQueryColumnInfo *col = column();
211 if (col) {
212 if (d->pos > -1)
213 return KDbEscapedString::number(d->pos + 1) + orderString;
214 else {
215 if (includeTableName && col->field()->table() && col->alias().isEmpty()) {
216 tableName = KDbEscapedString(escapeIdentifier(col->field()->table()->name(), conn, escapingType));
217 tableName += '.';
218 }
219 fieldName = KDbEscapedString(escapeIdentifier(col->aliasOrName(), conn, escapingType));
220 }
221 if (conn && col->field()->isTextType() && escapingType == KDb::DriverEscaping) {
222 collationString = conn->driver()->collationSql();
223 }
224 }
225 else {
226 QString aliasOrName;
227 if (includeTableName && d->field && d->field->table()) {
228 tableName = KDbEscapedString(escapeIdentifier(d->field->table()->name(), conn, escapingType));
229 tableName += '.';
230 } else if (d->field && conn && query) {
231 if (d->field->isExpression()) {
232 const int indexOfField = query->indexOf(*d->field);
233 aliasOrName = query->columnAlias(indexOfField);
234 if (aliasOrName.isEmpty()) {
235 kdbWarning() << "This field does not belong to specified query:" << *d->field
236 << endl << "cannot find alias";
237 aliasOrName = QLatin1String("?unknown_field?");
238 }
239 } else {
240 KDbQueryColumnInfo *ci = query->columnInfo(conn, d->field->name());
241 if (ci) {
242 aliasOrName = ci->aliasOrName();
243 }
244 }
245 }
246 if (aliasOrName.isEmpty()) {
247 // The field is not present on the SELECT list but is still correct,
248 // e.g. SELECT id FROM cars ORDER BY owner
249 aliasOrName = d->field ? d->field->name() : QLatin1String("?missing_field?")/*error*/;
250 }
251 fieldName = KDbEscapedString(escapeIdentifier(aliasOrName, conn, escapingType));
252 if (conn && d->field && d->field->isTextType() && escapingType == KDb::DriverEscaping) {
253 collationString = conn->driver()->collationSql();
254 }
255 }
256 return tableName + fieldName + collationString + orderString;
257}
258
260 KDbConnection *conn,
261 KDb::IdentifierEscapingType escapingType) const
262{
263 return toSqlString(includeTableName, conn, nullptr, escapingType);
264}
265
266//=======================================
267
268class Q_DECL_HIDDEN KDbOrderByColumnList::Private
269{
270public:
271 Private() {
272 }
273 ~Private() {
274 qDeleteAll(data);
275 }
277};
278
280 : d(new Private)
281{
282}
283
285 KDbQuerySchema* fromQuery, KDbQuerySchema* toQuery)
287{
288 for (QList<KDbOrderByColumn *>::ConstIterator it(other.constBegin()); it != other.constEnd();
289 ++it)
290 {
291 KDbOrderByColumn* order = (*it)->copy(conn, fromQuery, toQuery);
292 if (order) {
293 d->data.append(order);
294 }
295 }
296}
297
298KDbOrderByColumnList::~KDbOrderByColumnList()
299{
300 delete d;
301}
302
304{
305 return d->data == other.d->data;
306}
307
309{
310 return d->data.value(index);
311}
312
314{
315 return d->data.value(index);
316}
317
319 const QString& field1, KDbOrderByColumn::SortOrder order1,
320 const QString& field2, KDbOrderByColumn::SortOrder order2,
321 const QString& field3, KDbOrderByColumn::SortOrder order3,
322 const QString& field4, KDbOrderByColumn::SortOrder order4,
323 const QString& field5, KDbOrderByColumn::SortOrder order5)
324{
325 if (!querySchema) {
326 return false;
327 }
328 int numAdded = 0;
329#define ADD_COL(fieldName, order) \
330 if (ok && !fieldName.isEmpty()) { \
331 if (!appendField(conn, querySchema, fieldName, order)) \
332 ok = false; \
333 else \
334 numAdded++; \
335 }
336 bool ok = true;
337 ADD_COL(field1, order1)
338 ADD_COL(field2, order2)
339 ADD_COL(field3, order3)
340 ADD_COL(field4, order4)
341 ADD_COL(field5, order5)
342#undef ADD_COL
343 if (ok) {
344 return true;
345 }
346 for (int i = 0; i < numAdded; i++) {
347 d->data.removeLast();
348 }
349 return false;
350}
351
354{
355 if (columnInfo) {
356 d->data.append(new KDbOrderByColumn(columnInfo, order));
357 }
358}
359
361 KDbOrderByColumn::SortOrder order, int pos)
362{
363 if (!querySchema) {
364 return false;
365 }
366 const KDbQueryColumnInfo::Vector fieldsExpanded(querySchema->fieldsExpanded(conn));
367 if (pos < 0 || pos >= fieldsExpanded.size()) {
368 return false;
369 }
370 KDbQueryColumnInfo* ci = fieldsExpanded[pos];
371 d->data.append(new KDbOrderByColumn(ci, order, pos));
372 return true;
373}
374
376{
377 if (field) {
378 d->data.append(new KDbOrderByColumn(field, order));
379 }
380}
381
383 const QString& fieldName, KDbOrderByColumn::SortOrder order)
384{
385 if (!querySchema) {
386 return false;
387 }
388 KDbQueryColumnInfo *columnInfo = querySchema->columnInfo(conn, fieldName);
389 if (columnInfo) {
390 d->data.append(new KDbOrderByColumn(columnInfo, order));
391 return true;
392 }
393 KDbField *field = querySchema->findTableField(fieldName);
394 if (field) {
395 d->data.append(new KDbOrderByColumn(field, order));
396 return true;
397 }
398 kdbWarning() << "no such field" << fieldName;
399 return false;
400}
401
403{
404 return d->data.isEmpty();
405}
406
408{
409 return d->data.count();
410}
411
416
421
426
431
432QDebug operator<<(QDebug dbg, const KDbOrderByColumnList& list)
433{
434 if (list.isEmpty()) {
435 dbg.nospace() << "NONE";
436 return dbg.space();
437 }
438 bool first = true;
440 if (first)
441 first = false;
442 else
443 dbg.nospace() << '\n';
444 dbg.nospace() << *(*it);
445 }
446 return dbg.space();
447}
448
450 KDbQuerySchema *query,
451 KDb::IdentifierEscapingType escapingType) const
452{
453 KDbEscapedString string;
455 if (!string.isEmpty())
456 string += ", ";
457 string += (*it)->toSqlString(includeTableNames, conn, query, escapingType);
458 }
459 return string;
460}
461
463 KDb::IdentifierEscapingType escapingType) const
464{
465 return toSqlString(includeTableNames, conn, nullptr, escapingType);
466}
467
469{
470 qDeleteAll(d->data);
471 d->data.clear();
472}
Provides database connection, allowing queries and data modification.
KDbDriver * driver() const
virtual KDbEscapedString collationSql() const
Definition KDbDriver.h:232
Specialized string for escaping.
Meta-data for a field.
Definition KDbField.h:72
KDbTableSchema * table()
Definition KDbField.cpp:585
bool isTextType() const
Definition KDbField.h:353
QString name() const
Definition KDbField.cpp:256
bool isExpression() const
KDbOrderByColumnList provides list of sorted columns for a query schema.
QList< KDbOrderByColumn * >::ConstIterator constEnd() const
bool operator==(const KDbOrderByColumnList &other) const
void appendField(KDbField *field, KDbOrderByColumn::SortOrder order=KDbOrderByColumn::SortOrder::Ascending)
const KDbOrderByColumn * value(int index) const
Returns column with given index.
QList< KDbOrderByColumn * >::Iterator end()
bool appendFields(KDbConnection *conn, KDbQuerySchema *querySchema, const QString &field1, KDbOrderByColumn::SortOrder order1=KDbOrderByColumn::SortOrder::Ascending, const QString &field2=QString(), KDbOrderByColumn::SortOrder order2=KDbOrderByColumn::SortOrder::Ascending, const QString &field3=QString(), KDbOrderByColumn::SortOrder order3=KDbOrderByColumn::SortOrder::Ascending, const QString &field4=QString(), KDbOrderByColumn::SortOrder order4=KDbOrderByColumn::SortOrder::Ascending, const QString &field5=QString(), KDbOrderByColumn::SortOrder order5=KDbOrderByColumn::SortOrder::Ascending)
KDbEscapedString toSqlString(bool includeTableNames, KDbConnection *conn, KDbQuerySchema *query, KDb::IdentifierEscapingType escapingType=KDb::DriverEscaping) const
Return an SQL string like "name ASC, 2 DESC" usable for building an SQL statement.
QList< KDbOrderByColumn * >::Iterator begin()
QList< KDbOrderByColumn * >::ConstIterator constBegin() const
void appendColumn(KDbQueryColumnInfo *columnInfo, KDbOrderByColumn::SortOrder order=KDbOrderByColumn::SortOrder::Ascending)
KDbOrderByColumn provides information about a single query column used for sorting.
KDbOrderByColumn::SortOrder sortOrder() const
KDbOrderByColumn()
Creates an empty information about a single query column.
SortOrder
Column sort order.
bool operator==(const KDbOrderByColumn &col) const
KDbField * field() const
A field to sort, used only in case when the second constructor was used.
KDbOrderByColumn * copy(KDbConnection *conn, KDbQuerySchema *fromQuery, KDbQuerySchema *toQuery) const
KDbQueryColumnInfo * column() const
A column to sort.
KDbOrderByColumn & operator=(const KDbOrderByColumn &other)
Assigns other to this object returns a reference to this object.
KDbEscapedString toSqlString(bool includeTableName, KDbConnection *conn, KDbQuerySchema *query, KDb::IdentifierEscapingType escapingType=KDb::DriverEscaping) const
Return an SQL string like "name ASC" or "2 DESC" usable for building an SQL statement.
Helper class that assigns additional information for the column in a query.
const KDbQuerySchema * querySchema() const
Returns query schema for this column.
KDbConnection * connection()
Returns connection for this column.
QString aliasOrName() const
KDbQuerySchema provides information about database query.
KDbQueryColumnInfo::Vector fieldsExpanded(KDbConnection *conn, FieldsExpandedMode mode=FieldsExpandedMode::Default) const
@ WithInternalFields
Like Default but internal fields (for lookup) are appended.
KDbQueryColumnInfo * columnInfo(KDbConnection *conn, const QString &identifier, ExpandMode mode=ExpandMode::Expanded) const
KDbQueryColumnInfo * expandedOrInternalField(KDbConnection *conn, int index) const
KDbField * findTableField(const QString &fieldOrTableAndFieldName) const
IdentifierEscapingType
Escaping type for identifiers.
Definition KDbGlobal.h:144
@ DriverEscaping
Identifiers are escaped by driver.
Definition KDbGlobal.h:145
KIOCORE_EXPORT QStringList list(const QString &fileClass)
const QList< QKeySequence > & copy()
bool operator==(const StyleDelim &l, const StyleDelim &r)
QDebug & nospace()
QDebug & space()
void append(QList< T > &&value)
iterator begin()
void clear()
const_iterator constBegin() const const
const_iterator constEnd() const const
qsizetype count() const const
iterator end()
qsizetype indexOf(const AT &value, qsizetype from) const const
bool isEmpty() const const
void removeLast()
qsizetype size() const const
T value(qsizetype i) const const
QString fromLatin1(QByteArrayView str)
bool isEmpty() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:19:07 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.