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 }