File indexing completed on 2024-12-01 07:24:58

0001 /* This file is part of the KDE project
0002    Copyright (C) 2003-2018 Jarosław Staniek <staniek@kde.org>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LIB.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "KDbOrderByColumn.h"
0021 #include "KDbQuerySchema.h"
0022 #include "KDbQuerySchema_p.h"
0023 #include "KDbConnection.h"
0024 #include "kdb_debug.h"
0025 
0026 class Q_DECL_HIDDEN KDbOrderByColumn::Private
0027 {
0028 public:
0029     Private()
0030         : columnIndex(-1)
0031         , pos(-1)
0032         , field(nullptr)
0033         , order(KDbOrderByColumn::SortOrder::Ascending)
0034     {
0035     }
0036     Private(const Private &other) {
0037         copy(other);
0038     }
0039 #define KDbOrderByColumnPrivateArgs(o) std::tie(o.querySchema, o.connection, o.columnIndex, o.pos, o.field, o.order)
0040     Private(KDbQueryColumnInfo* aColumn, int aPos, KDbField* aField, KDbOrderByColumn::SortOrder aOrder)
0041     {
0042         const KDbQuerySchema *foundQuerySchema = nullptr;
0043         KDbConnection *foundConnection = nullptr;
0044         int foundColumnIndex = -1;
0045         if (aColumn) {
0046             foundQuerySchema =aColumn->querySchema();
0047             foundConnection = aColumn->connection();
0048             const KDbQueryColumnInfo::Vector fieldsExpanded = foundQuerySchema->fieldsExpanded(
0049                     foundConnection, KDbQuerySchema::FieldsExpandedMode::WithInternalFields);
0050             foundColumnIndex = fieldsExpanded.indexOf(aColumn);
0051             if (foundColumnIndex < 0) {
0052                 kdbWarning() << "Column not found in query:" << *aColumn;
0053             }
0054         }
0055         KDbOrderByColumnPrivateArgs((*this))
0056             = std::tie(foundQuerySchema, foundConnection, foundColumnIndex, aPos, aField, aOrder);
0057     }
0058     void copy(const Private &other) {
0059         KDbOrderByColumnPrivateArgs((*this)) = KDbOrderByColumnPrivateArgs(other);
0060     }
0061     bool operator==(const Private &other) const {
0062         return KDbOrderByColumnPrivateArgs((*this)) == KDbOrderByColumnPrivateArgs(other);
0063     }
0064 
0065     //! Query schema that owns the KDbQueryColumnInfo and thus also this KDbOrderByColumn object.
0066     //! Cached for performance, can be cached since lifetime of the KDbOrderByColumn object depends
0067     //! on the query. @c nullptr if columnIndex is not provided. @since 3.2
0068     const KDbQuerySchema *querySchema = nullptr;
0069 
0070     //! Connection used to compute expanded fields. Like querySchema, connection is cached for
0071     //! performance and can be cached since lifetime of the KDbOrderByColumn object depends on the
0072     //! connection. @c nullptr if columnIndex is not provided. @since 3.2
0073     KDbConnection *connection = nullptr;
0074 
0075     //! Index of column to sort, -1 if field is present. @since 3.2
0076     int columnIndex;
0077 
0078     //! Value that indicates that column to sort (columnIndex) has been specified by providing its
0079     //! position, not name. For example, using "SELECT a, b FROM T ORDER BY 2".
0080     //! Value of -1 means that the column to sort has been specified by providing its name (or alias).
0081     //! For example "SELECT a, b FROM T ORDER BY b". -1 is the default.
0082     int pos;
0083 
0084     //! Used only in case when the second constructor is used.
0085     KDbField* field;
0086 
0087     //! Sort order
0088     KDbOrderByColumn::SortOrder order;
0089 };
0090 
0091 //----
0092 
0093 KDbOrderByColumn::KDbOrderByColumn()
0094     : d(new Private)
0095 {
0096 }
0097 
0098 KDbOrderByColumn::KDbOrderByColumn(KDbQueryColumnInfo* column, SortOrder order, int pos)
0099     : d(new Private(column, pos, nullptr, order))
0100 {
0101 }
0102 
0103 KDbOrderByColumn::KDbOrderByColumn(KDbField* field, SortOrder order)
0104     : d(new Private(nullptr, -1, field, order))
0105 {
0106 }
0107 
0108 KDbOrderByColumn::KDbOrderByColumn(const KDbOrderByColumn &other)
0109     : d(new Private(*other.d))
0110 {
0111 }
0112 
0113 KDbOrderByColumn::~KDbOrderByColumn()
0114 {
0115     delete d;
0116 }
0117 
0118 KDbOrderByColumn *KDbOrderByColumn::copy(KDbConnection *conn, KDbQuerySchema *fromQuery,
0119                                          KDbQuerySchema *toQuery) const
0120 {
0121     if (d->field) {
0122         return new KDbOrderByColumn(d->field, d->order);
0123     }
0124     if (d->columnIndex >= 0) {
0125         KDbQueryColumnInfo* columnInfo;
0126         if (fromQuery && toQuery) {
0127             columnInfo = toQuery->expandedOrInternalField(conn, d->columnIndex);
0128             if (!columnInfo) {
0129                 kdbWarning() << "Column info not found at index" << d->columnIndex << "in toQuery";
0130                 return nullptr;
0131             }
0132         }
0133         else {
0134             columnInfo = column();
0135         }
0136         return new KDbOrderByColumn(columnInfo, d->order, d->pos);
0137     }
0138     return nullptr;
0139 }
0140 
0141 KDbQueryColumnInfo* KDbOrderByColumn::column() const
0142 {
0143     if (d->columnIndex < 0 || !d->querySchema || !d->connection) {
0144         return nullptr;
0145     }
0146     return d->querySchema->expandedOrInternalField(d->connection, d->columnIndex);
0147 }
0148 
0149 int KDbOrderByColumn::position() const
0150 {
0151     return d->pos;
0152 }
0153 
0154 KDbField* KDbOrderByColumn::field() const
0155 {
0156     return d->field;
0157 }
0158 
0159 KDbOrderByColumn::SortOrder KDbOrderByColumn::sortOrder() const
0160 {
0161     return d->order;
0162 }
0163 
0164 KDbOrderByColumn& KDbOrderByColumn::operator=(const KDbOrderByColumn &other)
0165 {
0166     if (this != &other) {
0167         *d = *other.d;
0168     }
0169     return *this;
0170 }
0171 
0172 bool KDbOrderByColumn::operator==(const KDbOrderByColumn& col) const
0173 {
0174     return *d == *col.d;
0175 }
0176 
0177 QDebug operator<<(QDebug dbg, const KDbOrderByColumn& order)
0178 {
0179     const QLatin1String orderString(
0180         order.sortOrder() == KDbOrderByColumn::SortOrder::Ascending ? "ASCENDING" : "DESCENDING");
0181     if (order.column()) {
0182         if (order.position() > -1) {
0183             dbg.nospace() << qPrintable(QString::fromLatin1("COLUMN_AT_POSITION_%1(").arg(order.position() + 1))
0184                           << *order.column() << ','
0185                           << qPrintable(orderString) << ')';
0186             return dbg.space();
0187         }
0188         else {
0189             dbg.nospace() << "COLUMN(" << *order.column() << ',';
0190             dbg.nospace() << qPrintable(orderString) << ')';
0191             return dbg.space();
0192         }
0193     }
0194     if (order.field()) {
0195         dbg.nospace() << "FIELD(" << *order.field() << ',';
0196         dbg.nospace() << qPrintable(orderString) << ')';
0197         return dbg.space();
0198     }
0199     dbg.nospace() << "NONE";
0200     return dbg.space();
0201 }
0202 
0203 KDbEscapedString KDbOrderByColumn::toSqlString(bool includeTableName,
0204                                                KDbConnection *conn,
0205                                                KDbQuerySchema *query,
0206                                                KDb::IdentifierEscapingType escapingType) const
0207 {
0208     const QByteArray orderString(d->order == KDbOrderByColumn::SortOrder::Ascending ? "" : " DESC");
0209     KDbEscapedString fieldName, tableName, collationString;
0210     KDbQueryColumnInfo *col = column();
0211     if (col) {
0212         if (d->pos > -1)
0213             return KDbEscapedString::number(d->pos + 1) + orderString;
0214         else {
0215             if (includeTableName && col->field()->table() && col->alias().isEmpty()) {
0216                 tableName = KDbEscapedString(escapeIdentifier(col->field()->table()->name(), conn, escapingType));
0217                 tableName += '.';
0218             }
0219             fieldName = KDbEscapedString(escapeIdentifier(col->aliasOrName(), conn, escapingType));
0220         }
0221         if (conn && col->field()->isTextType() && escapingType == KDb::DriverEscaping) {
0222             collationString = conn->driver()->collationSql();
0223         }
0224     }
0225     else {
0226         QString aliasOrName;
0227         if (includeTableName && d->field && d->field->table()) {
0228             tableName = KDbEscapedString(escapeIdentifier(d->field->table()->name(), conn, escapingType));
0229             tableName += '.';
0230         } else if (d->field && conn && query) {
0231             if (d->field->isExpression()) {
0232                 const int indexOfField = query->indexOf(*d->field);
0233                 aliasOrName = query->columnAlias(indexOfField);
0234                 if (aliasOrName.isEmpty()) {
0235                     kdbWarning() << "This field does not belong to specified query:" << *d->field
0236                                  << endl << "cannot find alias";
0237                     aliasOrName = QLatin1String("?unknown_field?");
0238                 }
0239             } else {
0240                 KDbQueryColumnInfo *ci = query->columnInfo(conn, d->field->name());
0241                 if (ci) {
0242                     aliasOrName = ci->aliasOrName();
0243                 }
0244             }
0245         }
0246         if (aliasOrName.isEmpty()) {
0247             // The field is not present on the SELECT list but is still correct,
0248             // e.g. SELECT id FROM cars ORDER BY owner
0249             aliasOrName = d->field ? d->field->name() : QLatin1String("?missing_field?")/*error*/;
0250         }
0251         fieldName = KDbEscapedString(escapeIdentifier(aliasOrName, conn, escapingType));
0252         if (conn && d->field && d->field->isTextType() && escapingType == KDb::DriverEscaping) {
0253             collationString = conn->driver()->collationSql();
0254         }
0255     }
0256     return tableName + fieldName + collationString + orderString;
0257 }
0258 
0259 KDbEscapedString KDbOrderByColumn::toSqlString(bool includeTableName,
0260                                                KDbConnection *conn,
0261                                                KDb::IdentifierEscapingType escapingType) const
0262 {
0263     return toSqlString(includeTableName, conn, nullptr, escapingType);
0264 }
0265 
0266 //=======================================
0267 
0268 class Q_DECL_HIDDEN KDbOrderByColumnList::Private
0269 {
0270 public:
0271     Private() {
0272     }
0273     ~Private() {
0274         qDeleteAll(data);
0275     }
0276     QList<KDbOrderByColumn*> data;
0277 };
0278 
0279 KDbOrderByColumnList::KDbOrderByColumnList()
0280         : d(new Private)
0281 {
0282 }
0283 
0284 KDbOrderByColumnList::KDbOrderByColumnList(const KDbOrderByColumnList& other, KDbConnection *conn,
0285                                            KDbQuerySchema* fromQuery, KDbQuerySchema* toQuery)
0286         : KDbOrderByColumnList()
0287 {
0288     for (QList<KDbOrderByColumn *>::ConstIterator it(other.constBegin()); it != other.constEnd();
0289          ++it)
0290     {
0291         KDbOrderByColumn* order = (*it)->copy(conn, fromQuery, toQuery);
0292         if (order) {
0293             d->data.append(order);
0294         }
0295     }
0296 }
0297 
0298 KDbOrderByColumnList::~KDbOrderByColumnList()
0299 {
0300     delete d;
0301 }
0302 
0303 bool KDbOrderByColumnList::operator==(const KDbOrderByColumnList &other) const
0304 {
0305     return d->data == other.d->data;
0306 }
0307 
0308 const KDbOrderByColumn* KDbOrderByColumnList::value(int index) const
0309 {
0310     return d->data.value(index);
0311 }
0312 
0313 KDbOrderByColumn* KDbOrderByColumnList::value(int index)
0314 {
0315     return d->data.value(index);
0316 }
0317 
0318 bool KDbOrderByColumnList::appendFields(KDbConnection *conn, KDbQuerySchema* querySchema,
0319                                         const QString& field1, KDbOrderByColumn::SortOrder order1,
0320                                         const QString& field2, KDbOrderByColumn::SortOrder order2,
0321                                         const QString& field3, KDbOrderByColumn::SortOrder order3,
0322                                         const QString& field4, KDbOrderByColumn::SortOrder order4,
0323                                         const QString& field5, KDbOrderByColumn::SortOrder order5)
0324 {
0325     if (!querySchema) {
0326         return false;
0327     }
0328     int numAdded = 0;
0329 #define ADD_COL(fieldName, order) \
0330     if (ok && !fieldName.isEmpty()) { \
0331         if (!appendField(conn, querySchema, fieldName, order)) \
0332             ok = false; \
0333         else \
0334             numAdded++; \
0335     }
0336     bool ok = true;
0337     ADD_COL(field1, order1)
0338     ADD_COL(field2, order2)
0339     ADD_COL(field3, order3)
0340     ADD_COL(field4, order4)
0341     ADD_COL(field5, order5)
0342 #undef ADD_COL
0343     if (ok) {
0344         return true;
0345     }
0346     for (int i = 0; i < numAdded; i++) {
0347         d->data.removeLast();
0348     }
0349     return false;
0350 }
0351 
0352 void KDbOrderByColumnList::appendColumn(KDbQueryColumnInfo* columnInfo,
0353                                         KDbOrderByColumn::SortOrder order)
0354 {
0355     if (columnInfo) {
0356         d->data.append(new KDbOrderByColumn(columnInfo, order));
0357     }
0358 }
0359 
0360 bool KDbOrderByColumnList::appendColumn(KDbConnection *conn, KDbQuerySchema* querySchema,
0361                                         KDbOrderByColumn::SortOrder order, int pos)
0362 {
0363     if (!querySchema) {
0364         return false;
0365     }
0366     const KDbQueryColumnInfo::Vector fieldsExpanded(querySchema->fieldsExpanded(conn));
0367     if (pos < 0 || pos >= fieldsExpanded.size()) {
0368         return false;
0369     }
0370     KDbQueryColumnInfo* ci = fieldsExpanded[pos];
0371     d->data.append(new KDbOrderByColumn(ci, order, pos));
0372     return true;
0373 }
0374 
0375 void KDbOrderByColumnList::appendField(KDbField* field, KDbOrderByColumn::SortOrder order)
0376 {
0377     if (field) {
0378         d->data.append(new KDbOrderByColumn(field, order));
0379     }
0380 }
0381 
0382 bool KDbOrderByColumnList::appendField(KDbConnection *conn, KDbQuerySchema* querySchema,
0383                                        const QString& fieldName, KDbOrderByColumn::SortOrder order)
0384 {
0385     if (!querySchema) {
0386         return false;
0387     }
0388     KDbQueryColumnInfo *columnInfo = querySchema->columnInfo(conn, fieldName);
0389     if (columnInfo) {
0390         d->data.append(new KDbOrderByColumn(columnInfo, order));
0391         return true;
0392     }
0393     KDbField *field = querySchema->findTableField(fieldName);
0394     if (field) {
0395         d->data.append(new KDbOrderByColumn(field, order));
0396         return true;
0397     }
0398     kdbWarning() << "no such field" << fieldName;
0399     return false;
0400 }
0401 
0402 bool KDbOrderByColumnList::isEmpty() const
0403 {
0404     return d->data.isEmpty();
0405 }
0406 
0407 int KDbOrderByColumnList::count() const
0408 {
0409     return d->data.count();
0410 }
0411 
0412 QList<KDbOrderByColumn*>::Iterator KDbOrderByColumnList::begin()
0413 {
0414     return d->data.begin();
0415 }
0416 
0417 QList<KDbOrderByColumn*>::Iterator KDbOrderByColumnList::end()
0418 {
0419     return d->data.end();
0420 }
0421 
0422 QList<KDbOrderByColumn*>::ConstIterator KDbOrderByColumnList::constBegin() const
0423 {
0424     return d->data.constBegin();
0425 }
0426 
0427 QList<KDbOrderByColumn*>::ConstIterator KDbOrderByColumnList::constEnd() const
0428 {
0429     return d->data.constEnd();
0430 }
0431 
0432 QDebug operator<<(QDebug dbg, const KDbOrderByColumnList& list)
0433 {
0434     if (list.isEmpty()) {
0435         dbg.nospace() << "NONE";
0436         return dbg.space();
0437     }
0438     bool first = true;
0439     for (QList<KDbOrderByColumn*>::ConstIterator it(list.constBegin()); it != list.constEnd(); ++it) {
0440         if (first)
0441             first = false;
0442         else
0443             dbg.nospace() << '\n';
0444         dbg.nospace() << *(*it);
0445     }
0446     return dbg.space();
0447 }
0448 
0449 KDbEscapedString KDbOrderByColumnList::toSqlString(bool includeTableNames, KDbConnection *conn,
0450                                                    KDbQuerySchema *query,
0451                                                    KDb::IdentifierEscapingType escapingType) const
0452 {
0453     KDbEscapedString string;
0454     for (QList<KDbOrderByColumn*>::ConstIterator it(constBegin()); it != constEnd(); ++it) {
0455         if (!string.isEmpty())
0456             string += ", ";
0457         string += (*it)->toSqlString(includeTableNames, conn, query, escapingType);
0458     }
0459     return string;
0460 }
0461 
0462 KDbEscapedString KDbOrderByColumnList::toSqlString(bool includeTableNames, KDbConnection *conn,
0463                                                    KDb::IdentifierEscapingType escapingType) const
0464 {
0465     return toSqlString(includeTableNames, conn, nullptr, escapingType);
0466 }
0467 
0468 void KDbOrderByColumnList::clear()
0469 {
0470     qDeleteAll(d->data);
0471     d->data.clear();
0472 }